Copyright | (c) 2014 Peter Trsko |
---|---|
License | BSD3 |
Maintainer | peter.trsko@gmail.com |
Stability | experimental |
Portability | DeriveDataTypeable, NoImplicitPrelude |
Safe Haskell | Safe-Inferred |
Language | Haskell2010 |
Create pkg-config configuration file from Haskell code using combinators specialized for this purpose. To learn more about pkg-config please read one or both following articles:
- Guide to pkg-config by Dan Nicholson
- Autotools Mythbuster by Diego Elio Pettenò: Chapter 4. Dependency discovery -- pkg-config
- data PkgConfig
- pkgVariables :: Functor f => ([PkgVariable] -> f [PkgVariable]) -> PkgConfig -> f PkgConfig
- pkgName :: Functor f => (Text -> f Text) -> PkgConfig -> f PkgConfig
- pkgDescription :: Functor f => (Text -> f Text) -> PkgConfig -> f PkgConfig
- pkgUrl :: Functor f => (Text -> f Text) -> PkgConfig -> f PkgConfig
- pkgVersion :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- pkgRequires :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- pkgRequiresPrivate :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- pkgConflicts :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- pkgCflags :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- pkgLibs :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- pkgLibsPrivate :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig
- type PkgDescription = Text
- type PkgName = Text
- type PkgUrl = Text
- type PkgVariable = (Text, PkgTemplate)
- type PkgVersion = PkgTemplate
- toStrictText :: PkgConfig -> Text
- writePkgConfig :: FilePath -> PkgConfig -> IO ()
- type PkgTemplate = Template
- var :: Text -> PkgTemplate
- lit :: Text -> PkgTemplate
- strLit :: String -> PkgTemplate
- singletonLit :: Char -> PkgTemplate
- quote :: PkgTemplate -> PkgTemplate
- (</>) :: PkgTemplate -> PkgTemplate -> PkgTemplate
- (<.>) :: PkgTemplate -> PkgTemplate -> PkgTemplate
- version :: [Word] -> PkgTemplate
- (~=) :: Text -> [Word] -> PkgTemplate
- (~/=) :: Text -> [Word] -> PkgTemplate
- (~<) :: Text -> [Word] -> PkgTemplate
- (~>) :: Text -> [Word] -> PkgTemplate
- (~<=) :: Text -> [Word] -> PkgTemplate
- (~>=) :: Text -> [Word] -> PkgTemplate
- option :: Text -> PkgTemplate -> PkgTemplate
- strOption :: String -> PkgTemplate -> PkgTemplate
- includes :: [PkgTemplate] -> PkgTemplate
- libraries :: [PkgTemplate] -> PkgTemplate
- libraryPath :: [PkgTemplate] -> PkgTemplate
- list :: [PkgTemplate] -> PkgTemplate
- options :: [PkgTemplate] -> PkgTemplate
- separatedBy :: Text -> [PkgTemplate] -> PkgTemplate
- variables :: PkgTemplate -> [Text]
Usage
Following code is able to generate foo.pc
, a pkg-config configuration
file for library named foo
:
{-# LANGUAGE OverloadedStrings #-} module Main (main) where import Data.String (IsString
) import Data.Default.Class (Default
(def
)) -- From data-default-class library: -- http://hackage.haskell.org/package/data-default-class import Control.Lens -- From lens library: -- http://hackage.haskell.org/package/lens import Data.PkgConfig libraryBaseName ::IsString
a => a libraryBaseName = "foo" main :: IO () main =writePkgConfig
(libraryBaseName++
".pc") libPkgConfig where libPkgConfig =def
&pkgVariables
.~ [ ("prefix", "/usr/local" ) , ("includedir",var
"prefix"</>
"include") , ("libdir",var
"prefix"</>
"lib" ) , ("arch", "i386" ) ] &pkgName
.~ libraryBaseName &pkgDescription
.~ "Example pkg-config." &pkgVersion
.~version
[1, 2, 3] &pkgCflags
.~includes
[var
"includedir"] &pkgRequires
.~list
[ "bar"~>
[0], "bar"~<=
[3, 1] , "baz"~=
[1, 2, 3] ] &pkgLibs
.~options
[libraryPath
[var
"libdir",var
"libdir"</>
var
"arch"] ,libraries
[libraryBaseName] ]
Content of generated foo.pc
:
prefix=/usr/local includedir=${prefix}/include libdir=${prefix}/lib arch=i386 Name: foo Description: Example pkg-config. Version: 1.2.3 Requires: bar > 0, bar <= 3.1, baz = 1.2.3 Cflags: -I"${includedir}" Libs: -L"${libdir}" -L"${libdir}/${arch}" -lfoo
Note that functions &
and .~
, used in the example, are from
lens library. Please consult its
documentation for details.
PkgConfig
Data type that describes whole pkg-config configuration file for one specific library. It also tries to preserve as much of pkg-config philosophy as possible.
Lenses are used for accessing individual fields of PkgConfig
data type.
Example:
def
&pkgVariables
.~ [("prefix", "/usr/local")] &pkgName
.~ "some library" -- ... &pkgLibs
.~includes
[var
"prefix"</>
"include"</>
"foo" ]
Representation of pkg-config configuration file.
Lenses
pkgVariables :: Functor f => ([PkgVariable] -> f [PkgVariable]) -> PkgConfig -> f PkgConfig Source
Variable definitions.
pkgName :: Functor f => (Text -> f Text) -> PkgConfig -> f PkgConfig Source
Human-readable name of a library or package. This field is not used by
pkg-config tool for queries, because it uses .pc
file base name.
pkgDescription :: Functor f => (Text -> f Text) -> PkgConfig -> f PkgConfig Source
Brief description of the package.
pkgUrl :: Functor f => (Text -> f Text) -> PkgConfig -> f PkgConfig Source
URL where people can get more information about and download the package.
pkgVersion :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
Version of the package.
pkgRequires :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
List of packages required by this package and their version bounds.
pkgRequiresPrivate :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
Compiler flags specific to this package and any required libraries that
don't support pkg-config. If the required libraries support pkg-config,
they should be added to Requires
(pkgRequires
) or Requires.private
(pkgRequiresPrivate
).
pkgConflicts :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
An optional field describing packages that this one conflicts with. The version specific rules from the Requires field also apply here. This field also takes multiple instances of the same package. E.g.:
Conflicts: bar < 1.2.3, bar >= 1.3.0.
pkgCflags :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
Compiler flags specific to this package and any required libraries that
don't support pkg-config. If the required libraries support pkg-config,
they should be added to Requires
(pkgRequires
) or Requires.private
(pkgRequiresPrivate
).
pkgLibs :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
Linking flags specific to this package and any required libraries that
don't support pkg-config. The same rules as for Cflags
(pkgCflags
)
field apply here.
pkgLibsPrivate :: Functor f => (PkgTemplate -> f PkgTemplate) -> PkgConfig -> f PkgConfig Source
Linking flags for private libraries required by this package but not
exposed to applications. The same rules as for Cflags
(pkgCflags
) field
apply here.
Type Aliases
These are used to make type signatures easier to read.
type PkgDescription = Text Source
type PkgVariable = (Text, PkgTemplate) Source
Variable definition consisting of its name and value in form of
PkgTemplate
.
type PkgVersion = PkgTemplate Source
Package version may use variable expansion and so it is represented by
PkgConfig
.
Serialization
I/O
writePkgConfig :: FilePath -> PkgConfig -> IO () Source
PkgTemplate
The pkg-config tool allows variable declaration so that they can later be
used in other parts of its configuration file. To give Haskell programmer
the same power, this library provides PkgTemplate
. One can think of it as
a string with named holes, i.e. places where variables will be expanded.
Following is example of how two variables, namely prefix
and includedir
,
can be defined inside pkg-config configuration file:
prefix=/usr/local includedir=${prefix}/include
PkgConfig
has a field pkgVariables
that is used to define variables and
above example can be translated in to:
def
&pkgVariables
.~ [ ("prefix", "/usr/local") , ("includedir",var
"prefix"</>
"include") ]
Lot of similar properties of String
hold for Template
as well.
Including the fact that Template
is monoid and therefore can be
concatenated using monoid operations:
>>>
strLit "foo" <> strLit "bar"
foobar
Since Template
has IsString
instance, then, if OverloadedStrings
language extension is enabled, it is possible to simplify above example in
to:
>>>
"foo" <> "bar" :: PkgTemplate
foobar
For consistency instance for Default
type class is also provided and it
holds following property:
def
===mempty
Additionally following properties hold:
lit
"" ===mempty
var
"" =/=mempty
type PkgTemplate = Template Source
Template consists of variables and literal strings. All special characters ('$', '#', '\' and end-of-line sequences) contained in literals are escaped when serialized.
Smart Constructors
var :: Text -> PkgTemplate Source
Construct variable fragment of a template.
>>>
var "prefix" <> lit "/bin"
$prefix/bin
lit :: Text -> PkgTemplate Source
Construct literal fragment of a template. This is useful if language
extension OverloadedStrings
is not enabled.
>>>
var "prefix" <> lit "/bin"
$prefix/bin
strLit :: String -> PkgTemplate Source
Create PkgTemplate
literal from String
by packing it in to strict
Text
first.
singletonLit :: Char -> PkgTemplate Source
Crate one character long PkgTemplate
literal.
Combinators
quote :: PkgTemplate -> PkgTemplate Source
Put quotation marks ('"') around a template.
>>>
quote $ var "prefix" </> "include"
"${prefix}/include"
>>>
var "prefix" </> quote "dir with spaces"
${prefix}/"dir with spaces"
FilePath-like Combinators
(</>) :: PkgTemplate -> PkgTemplate -> PkgTemplate Source
Put literal "/" between two templates.
>>>
var "prefix" </> lit "foo" <.> lit "pc"
${prefix}/foo.pc
(<.>) :: PkgTemplate -> PkgTemplate -> PkgTemplate Source
Put literal "." between two templates.
>>>
var "prefix" </> lit "foo" <.> lit "pc"
${prefix}/foo.pc>>>
var "major" <.> var "minor" <.> var "patch"
${major}.${minor}.${patch}
Version Combinators
version :: [Word] -> PkgTemplate Source
Treat list of integers as version number and construct template literal out of it.
>>>
version [1, 2, 3]
1.2.3>>>
version [] == mempty
True
(~=) :: Text -> [Word] -> PkgTemplate Source
Dependency on a package of exact version.
>>>
"sqlite" ~= [3, 8, 7, 1]
sqlite = 3.8.7.1
(~/=) :: Text -> [Word] -> PkgTemplate Source
Dependency on a package not of a specific version.
>>>
"alpha" ~/= [7, 2]
alpha != 7.2
(~<) :: Text -> [Word] -> PkgTemplate Source
Dependency on a package with version greater or less then specified value.
>>>
"alpha" ~< [7, 3]
alpha < 7.3
(~>) :: Text -> [Word] -> PkgTemplate Source
Dependency on a package with version greater then specified value.
>>>
"sqlite" ~> [3, 8]
sqlite3 > 3.8
(~<=) :: Text -> [Word] -> PkgTemplate Source
Dependency on a package with version greater or less or equal then specified value.
(~>=) :: Text -> [Word] -> PkgTemplate Source
Dependency on a package with version greater or equal then specified value.
Options Combinators
option :: Text -> PkgTemplate -> PkgTemplate Source
strOption :: String -> PkgTemplate -> PkgTemplate Source
includes :: [PkgTemplate] -> PkgTemplate Source
Take list of templates and make compiler include options. Template for
include directory is wrapped in quotes (see quote
and option
functions).
>>>
let d = var "prefix" </> "include" in includes [d, d </> var "arch"]
-I"${prefix}/include" -I"${prefix}/include/${arch}">>>
includes [var "prefix" </> "some dir"]
-I"${prefix}/some dir"
libraries :: [PkgTemplate] -> PkgTemplate Source
Take list of templates and make compiler library options.
>>>
libraries ["m", "rt", "foo"]
-lm -lrt -lfoo
libraryPath :: [PkgTemplate] -> PkgTemplate Source
Specialized Folds
list :: [PkgTemplate] -> PkgTemplate Source
options :: [PkgTemplate] -> PkgTemplate Source
separatedBy :: Text -> [PkgTemplate] -> PkgTemplate Source
Put specified text between templates.
Following properties hold:
forall s.separatedBy
s [] ===mempty
forall s t.separatedBy
s [t] === t
Example:
>>>
separatedBy ", " ["foo", "bar", "baz"]
foo, bar, baz
Queries
variables :: PkgTemplate -> [Text] Source
List all variables mentioned in PkgTemplate
.
>>>
variables $ var "foo" </> "bar" </> var "baz"
["foo","baz"]