Safe Haskell | Safe-Inferred |
---|---|
Language | Haskell2010 |
Cartel - a library to specify Cabal files in Haskell
The Cabal file format works very well for small projects. However, in big projects with a library, many executables, and test suites, some irritations emerge. You need to specify dependencies in multiple places, leading to redundancy. You also have to manually add in new modules, make sure you list all modules (a real pain with executables, as problems may arise only after you build a distribution tarball), and update your module lists when you refactor.
Specifying your Cabal files in Haskell rather than in a plain-text file format helps deal with a lot of these problems. You have the full power of Haskell to make definitions in one place and then reuse them. You can also dynamically read a tree of modules and use the result, thus avoiding the need to manually update your module lists.
A disadvantage to Cartel is that is more verbose than a vanilla Cabal file. In addition, you also have to remember to generate the new Cabal file whenever you change the script that generates your Cabal file.
To the extent possible, Cartel uses the Haskell type system to
prevent you from making mistakes in your Cabal file. For example,
the Betsy
type prevents you from using flag
s that you have not
declared, and you can't put an exposedModules
field in anything
but a library. However, Cartel does not prevent against all
errors. For example, Cartel does nothing to prevent you from
applying a function that calls for a NonEmptyString
to a string
that is, in fact, empty. Another example is that Cabal requires
executables to have a main-is
field, but Cartel does not force
you to include one. Ultimately your Cabal file might still have
errors that you have to fix by changing the program that generates
the file.
Everything you usually need is in this module. Other Cartel
modules contain implementation details. See first the
NonEmptyString
type synonym, which has important details on how
to regard String
s and NonEmptyString
s as you read the
documentation. Also, examine Cartel.GenCartelCabal, which
generates Cartel's own Cabal file using Cartel.
Hopefully this module's documentation is organized so that top-to-bottom reading will make sense.
- data Word :: *
- type NonEmptyString = String
- class Blank a where
- blank :: a
- type Version = [Word]
- data Section
- data Vcs
- darcs :: Vcs
- git :: Vcs
- svn :: Vcs
- mercurial :: Vcs
- bazaar :: Vcs
- archVcs :: Vcs
- monotone :: Vcs
- cvs :: NonEmptyString -> Vcs
- data RepoKind
- repoHead :: RepoKind
- repoThis :: RepoKind
- data Repository = Repository {}
- githubHead :: NonEmptyString -> NonEmptyString -> Section
- repository :: Repository -> Section
- class LogicTree a where
- invert :: Condition -> Condition
- data Constraint
- lt :: Version -> Constraint
- gt :: Version -> Constraint
- eq :: Version -> Constraint
- ltEq :: Version -> Constraint
- gtEq :: Version -> Constraint
- anyVersion :: Constraint
- data Compiler
- ghc :: Compiler
- nhc :: Compiler
- yhc :: Compiler
- hugs :: Compiler
- helium :: Compiler
- jhc :: Compiler
- lhc :: Compiler
- data Condition
- system :: NonEmptyString -> Condition
- arch :: NonEmptyString -> Condition
- impl :: Compiler -> Constraint -> Condition
- flag :: FlagName -> Condition
- true :: Condition
- false :: Condition
- condBlock :: HasBuildInfo a => Condition -> (a, [a]) -> [a] -> a
- data Package
- package :: NonEmptyString -> Constraint -> Package
- closedOpen :: NonEmptyString -> Version -> Version -> Package
- apiVersion :: NonEmptyString -> Version -> Package
- nextBreaking :: NonEmptyString -> Version -> Package
- nextMajor :: NonEmptyString -> Version -> Package
- exactly :: NonEmptyString -> Version -> Package
- unconstrained :: NonEmptyString -> Package
- data BuildInfoField
- class HasBuildInfo a
- haskell98 :: HasBuildInfo a => a
- haskell2010 :: HasBuildInfo a => a
- buildDepends :: HasBuildInfo a => [Package] -> a
- otherModules :: HasBuildInfo a => [NonEmptyString] -> a
- hsSourceDirs :: HasBuildInfo a => [NonEmptyString] -> a
- extensions :: HasBuildInfo a => [NonEmptyString] -> a
- buildTools :: HasBuildInfo a => [Package] -> a
- buildable :: HasBuildInfo a => Bool -> a
- ghcOptions :: HasBuildInfo a => [NonEmptyString] -> a
- ghcProfOptions :: HasBuildInfo a => [NonEmptyString] -> a
- ghcSharedOptions :: HasBuildInfo a => [NonEmptyString] -> a
- hugsOptions :: HasBuildInfo a => [NonEmptyString] -> a
- nhc98Options :: HasBuildInfo a => [NonEmptyString] -> a
- includes :: HasBuildInfo a => [NonEmptyString] -> a
- installIncludes :: HasBuildInfo a => [NonEmptyString] -> a
- includeDirs :: HasBuildInfo a => [NonEmptyString] -> a
- cSources :: HasBuildInfo a => [NonEmptyString] -> a
- extraLibraries :: HasBuildInfo a => [NonEmptyString] -> a
- extraLibDirs :: HasBuildInfo a => [NonEmptyString] -> a
- ccOptions :: HasBuildInfo a => [NonEmptyString] -> a
- cppOptions :: HasBuildInfo a => [NonEmptyString] -> a
- ldOptions :: HasBuildInfo a => [NonEmptyString] -> a
- pkgConfigDepends :: HasBuildInfo a => [Package] -> a
- frameworks :: HasBuildInfo a => [NonEmptyString] -> a
- class BuildsExe a where
- mainIs :: NonEmptyString -> a
- class BuildsExitcode a where
- exitcodeStdio :: a
- exitcodeFields :: (BuildsExitcode a, BuildsExe a) => NonEmptyString -> [a]
- data Betsy m a
- data FlagName
- data FlagOpts = FlagOpts {}
- makeFlag :: Applicative m => NonEmptyString -> FlagOpts -> Betsy m FlagName
- currentFlags :: Applicative f => Betsy f [(FlagName, FlagOpts)]
- data LibraryField
- exposed :: Bool -> LibraryField
- exposedModules :: [NonEmptyString] -> LibraryField
- data ExecutableField
- executable :: NonEmptyString -> [ExecutableField] -> Section
- detailed :: TestSuiteField
- data TestSuiteField
- testModule :: NonEmptyString -> TestSuiteField
- testSuite :: NonEmptyString -> [TestSuiteField] -> Section
- data BenchmarkField
- benchmark :: NonEmptyString -> [BenchmarkField] -> Section
- fileExtensions :: [String]
- modulesWithExtensions :: MonadIO m => [NonEmptyString] -> FilePath -> Betsy m [NonEmptyString]
- modules :: MonadIO m => FilePath -> Betsy m [NonEmptyString]
- data BuildType
- simple :: BuildType
- configure :: BuildType
- make :: BuildType
- custom :: BuildType
- data License
- gpl :: License
- agpl :: License
- lgpl :: License
- bsd2 :: License
- bsd3 :: License
- bsd4 :: License
- mit :: License
- mpl :: License
- apache :: License
- publicDomain :: License
- allRightsReserved :: License
- otherLicense :: License
- data Properties = Properties {
- name :: String
- version :: Version
- cabalVersion :: Maybe (Word, Word)
- buildType :: Maybe BuildType
- license :: Maybe License
- licenseFile :: String
- licenseFiles :: [NonEmptyString]
- copyright :: String
- author :: String
- maintainer :: String
- stability :: String
- homepage :: String
- bugReports :: String
- packageUrl :: String
- synopsis :: String
- description :: [String]
- category :: String
- testedWith :: [(Compiler, Constraint)]
- dataFiles :: [NonEmptyString]
- dataDir :: String
- extraSourceFiles :: [NonEmptyString]
- extraDocFiles :: [NonEmptyString]
- extraTmpFiles :: [NonEmptyString]
- data Cabal
- cabal :: Properties -> [LibraryField] -> [Section] -> Cabal
- defaultMain :: Betsy IO (Properties, [LibraryField], [Section]) -> IO ()
- data Error
- class Renderable a where
- class RenderableIndented a where
- renderIndented :: Int -> a -> String
- renderBetsy :: Functor m => Betsy m Cabal -> m (Either Error String)
- betsyToCabalStringIO :: (MonadIO m, Functor m) => Betsy m Cabal -> m (Either Error String)
Basic types and classes
data Word :: *
type NonEmptyString = String Source
A non-empty string. This string should never be empty. It is
used where, for example, a field in a Cabal file is required to
have a value and that value cannot be empty. In contrast, Cartel
uses an ordinary String
for values that can be empty.
This is only a type synonym, so nothing in the type system enforces
that these strings must be non-empty. Typically though, Cabal will
give you grief about the file that Cartel generates if you used an
empty value for a NonEmptyString
.
Typeclass for things that can be blank. More specifically,
blank a
results in an item that, when rendered in a Cabal file,
is the null string. blank
can be useful to indicate that you
have no options, and you can also use it in combination with record
syntax when you want to specify just a few options.
A version number. The Cabal documentation says this "usually" consists of a sequence of natural numbers separated by dots. Though this suggests that a version number could contain something other than natural numbers, in fact the types in the Cabal library do not allow anything other than numbers and you will get a parse error if you try to use anything else.
Therefore Cartel's Version
type only allows a list of Word
, as
each number cannot be negative. In addition, this list should
never be empty. However, this is just a type synonym for a list of
Word
, so the type system does not enforce the requirement that
this list be non-empty.
Sections
A single section in a Cabal file; this may be a source
repository, executable, test suite, or benchmark. You build a
Section
with the repository
, executable
, testSuite
, and
benchmark
functions.
Repositories
Version control systems
:: NonEmptyString | The named module |
-> Vcs |
Repository kinds
What kind of VCS repository is this?
Building repositories
data Repository Source
A single repository
section.
Repository | |
|
:: NonEmptyString | The Github account name |
-> NonEmptyString | The Github project name within the account |
-> Section |
Creates a Section
that is a Repository
for a Github head.
For example, for Cartel, use githubHead "massysett" "cartel"
.
repository :: Repository -> Section Source
Creates a Section
for a repository.
Logicals
Constraints
data Constraint Source
Expresses any version constraint, including no version constraint.
lt :: Version -> Constraint Source
Less than
gt :: Version -> Constraint Source
Greater than
eq :: Version -> Constraint Source
Equal to
ltEq :: Version -> Constraint Source
Less than or equal to
gtEq :: Version -> Constraint Source
Greater than or equal to
anyVersion :: Constraint Source
Matches any version at all (in a Cabal file, this is represented as an empty string).
Conditionals
Conditions. Ultimately these are used in a CondBlock
.
system :: NonEmptyString -> Condition Source
Operating system; tested against System.Info.os
on the
target system.
arch :: NonEmptyString -> Condition Source
Argument is matched against System.Info.arch
on the target
system.
impl :: Compiler -> Constraint -> Condition Source
Tests for the configured Haskell implementation.
flag :: FlagName -> Condition Source
Evaluates to the current assignment of the flag of the given name. Flag names are case insensitive. Testing for flags that have not been introduced with a flag section is an error.
:: HasBuildInfo a | |
=> Condition | Condition to satisfy |
-> (a, [a]) | Use these results if condition is true |
-> [a] | Use these results if condition if false |
-> a |
Builds if
statements. For example:
condition (flag "buildExe") (buildable True, []) [buildable False]
A little more complicated:
condition (flag "buildExe" &&& system "windows") (buildable True, []) [buildable False]
Packages
A single package, consisting of a package name and an optional
set of constraints. Used when specifying buildDepends
,
buildTools
, and pkgConfigDepends
.
Some functions exist to ease the creation of a Package
. For a
package with no version constrains, simply do something like
. Common use cases are
covered in the functions in the "Package Helpers" section below.
For something more complicated, use the functions in the
"Logicals" sections above, along with the unconstrained
"QuickCheck"&&&
and |||
combinators, to create your own Constraint
and then use it with
the package
function.
:: NonEmptyString | The name of the package |
-> Constraint | Version constraints. |
-> Package |
Builds a Package
.
Package helpers
:: NonEmptyString | Package name |
-> Version | Version number for lower bound |
-> Version | Version number for upper bound |
-> Package | Resulting |
Creates a package interval that is closed on the left, open on the right. Useful for the common case under the PVP to specify that you depend on a version that is at least a particular version, but less than another version.
closedOpen "bytestring" [0,17] [0,19] ==> bytestring >= 0.17 && < 0.19
apiVersion :: NonEmptyString -> Version -> Package Source
Specifies a particular API version. Useful to lock your package dependencies down to a particular API version.
apiVersion "base" [1] ==> base >= 1 && < 2 apiVersion "base" [1,2] ==> base >= 1.2 && < 1.3 apiVersion "base" [1,2,3] ==> base >= 1.2.3 && < 1.2.4
nextBreaking :: NonEmptyString -> Version -> Package Source
Depends on the version given, up to the next breaking API change.
nextBreaking "base" [4] ==> base >= 4 && < 4.1 nextBreaking "base" [4,1] ==> base >= 4.1 && < 4.2 nextBreaking "base" [4,7,0,0] ==> base >= 4.7.0.0 && < 4.8
nextMajor :: NonEmptyString -> Version -> Package Source
Depends on the version given, up to the next time the first
digit increments. Useful for base
.
nextBreaking "base" [4] ==> base >= 4 && < 5
exactly :: NonEmptyString -> Version -> Package Source
Depends on exactly this version only.
exactly "base" [4,5,0,0] ==> base ==4.5.0.0
Build information
Libraries, executables, test suites, and benchmarks all share
common fields for build information. BuildInfoField
represents
these common fields, and HasBuildInfo
is a typeclass encompassing
libraries, executables, test suites, and benchmarks. You can
build these fields for any of these sections using the functions
and values listed here.
data BuildInfoField Source
A single field of build information. This can appear in a
Library
, Executable
, TestSuite
, or Benchmark
.
class HasBuildInfo a Source
Things that can be an item in a build information field in a Cabal file.
HasBuildInfo BenchmarkField | A list of packages needed to build this component |
HasBuildInfo TestSuiteField | |
HasBuildInfo ExecutableField | |
HasBuildInfo LibraryField |
haskell98 :: HasBuildInfo a => a Source
Sets Haskell 98 as the default-language
.
Currently not documented in Cabal, see
haskell2010 :: HasBuildInfo a => a Source
Sets Haskell 2010 as the default-language
.
Currently not documented in Cabal, see
buildDepends :: HasBuildInfo a => [Package] -> a Source
Modules used but not exposed. For libraries, these are
hidden modules; for executable, these are auxiliary modules to
be linked with the file in the main-is
field.
modules
can help greatly with maintenance of
this field.
otherModules :: HasBuildInfo a => [NonEmptyString] -> a Source
Root directories for the module hierarchy
hsSourceDirs :: HasBuildInfo a => [NonEmptyString] -> a Source
Haskell extensions used by every module.
extensions :: HasBuildInfo a => [NonEmptyString] -> a Source
buildTools :: HasBuildInfo a => [Package] -> a Source
Programs needed to build this package, such as c2hs.
buildable :: HasBuildInfo a => Bool -> a Source
Is this component buildable?
ghcOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
ghcProfOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
ghcSharedOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
hugsOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
nhc98Options :: HasBuildInfo a => [NonEmptyString] -> a Source
includes :: HasBuildInfo a => [NonEmptyString] -> a Source
Header files to be included in any compilations via C. Applies to both header files that are already installed on the system and to those coming with the package to be installed.
installIncludes :: HasBuildInfo a => [NonEmptyString] -> a Source
Header files to be installed into $libdir/includes
when the
package is installed. These files should be found in relative
to the top of the source tree or relative to one of the
directories listed in include-dirs
.
includeDirs :: HasBuildInfo a => [NonEmptyString] -> a Source
List of diretories to search for header files when dealing with C compilations.
cSources :: HasBuildInfo a => [NonEmptyString] -> a Source
C sources to be compiled and lined with the Haskell files.
extraLibraries :: HasBuildInfo a => [NonEmptyString] -> a Source
Extra libraries to link with.
extraLibDirs :: HasBuildInfo a => [NonEmptyString] -> a Source
Directories to search for libraries.
ccOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
C Compiler options.
cppOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
C Preprocessor options. Undocumented, see https://github.com/haskell/cabal/issues/646
ldOptions :: HasBuildInfo a => [NonEmptyString] -> a Source
Linker options.
pkgConfigDepends :: HasBuildInfo a => [Package] -> a Source
List of pkg-config packages needed to build this component.
frameworks :: HasBuildInfo a => [NonEmptyString] -> a Source
OS X frameworks.
BuildsExe
Benchmarks, test suites, and executables have common fields;
the BuildsExe
class captures these.
class BuildsExe a where Source
Sections that build executables. These are the Executable
,
Benchmark
, and TestSuite
sections.
mainIs :: NonEmptyString -> a Source
Overloaded function allowing you to use mainIs
for an
Executable
, Benchmark
, or TestSuite
section.
Exitcode executables
Test suites and benchmarks are capable of building things of
type exitcode-stdio-1.0
; the BuildsExitcode
class reflects
this.
class BuildsExitcode a where Source
Sections that build executables that can be exitcode-stdio-1.0
.
These are the Benchmark
and TestSuite
sections.
exitcodeStdio :: a Source
Returns a field that is exitcode-stdio-1.0
:: (BuildsExitcode a, BuildsExe a) | |
=> NonEmptyString | Value for |
-> [a] |
Builds two fields. The first indicates that this is an
exitcode-stdio-1.0
executable; the second is the appropriate
main-is
field.
Betsy
Computations that can create and use Cabal flags. Use of this
type, along with the defaultMain
function ensures that any
FlagName
you use has been properly set up by using makeFlag
.
That way, you don't use flags in a flag
without actually
declaring the flag. When defaultMain
creates your Cabal
file, it will print the necessary Flag
sections.
Betsy
is parameterized on a type, m
. When this type is a
monad, Betsy
is also a monad, allowing you to use use the usual
monad combinators and do
notation. Betsy
is also a monad transformer.
Flags
Options for flags, except for the flag's name.
FlagOpts | |
|
:: Applicative m | |
=> NonEmptyString | Name of flag |
-> FlagOpts | Options for the flag |
-> Betsy m FlagName | This operation will fail if there is already a flag with the name you gave. |
Creates new flags.
currentFlags :: Applicative f => Betsy f [(FlagName, FlagOpts)] Source
Returns a list of all flags made so far.
Libraries
A library consists of one or more LibraryField
s. Typically
you will return these fields inside of the Betsy
type through
the defaultMain
function. To build a LibraryField
, you will
mostly use the bindings in the "Build Information" section of
this module. You will also need exposedModules
and you might
use exposed
and condBlock
.
data LibraryField Source
A field in the Library
section of the Cabal file. A
Library
section can have multiple fields.
exposed :: Bool -> LibraryField Source
Whether a library is exposed. GHC can hide libraries.
exposedModules :: [NonEmptyString] -> LibraryField Source
A library's exposed modules. modules
can help you generate
this, without you having to manually list each module and keep the
list up to date.
Executables
An executable consists of one more more ExecutableField
s.
You build an executable by passing one or more ExecutableField
s
to the executable
function. To get an ExecutableField
, you
will mostly use the bindings in the "Build Information" section
of this module, as well as mainIs
. You might also need
condBlock
.
data ExecutableField Source
A single field in an Executable
section. An Executable
section may have multiple fields.
:: NonEmptyString | The name of the executable that Cabal will build. |
-> [ExecutableField] | An executable can contain zero or more |
-> Section |
Builds a Section
for executable files.
Test suites
A test suite consists of one more more TestSuiteField
s. You
build an test suite by passing one or more TestSuiteField
s to
the testSuite
function. To get a TestSuiteField
, you will
mostly use the bindings in the "Build Information" section of
this module. You might also need the testModule
,
exitcodeStdio
, mainIs
, detailed
, condBlock
, and
exitcodeFields
bindings.
data TestSuiteField Source
A single field value in a TestSuite
section. A single test
suite section may contain mulitple fields.
testModule :: NonEmptyString -> TestSuiteField Source
The module exporting the tests
symbol. This is required when
using Detailed
and disallowed when using ExitcodeStdio
.
:: NonEmptyString | The executable name for the resulting test suite |
-> [TestSuiteField] | Zero or more |
-> Section |
Builds a Section
for test suites.
Benchmarks
A benchmark consists of one more more BenchmarkField
s. You
build an benchmark by passing one or more BenchmarkField
s to
the benchmark
function. To get an BenchmarkField
, you will
mostly use the bindings in the "Build Information" section of
this module. You might also need the exitcodeStdio
,
exitcodeFields
, and condBlock
bindings.
data BenchmarkField Source
A single field in a Benchmark
section.
Eq BenchmarkField | |
Ord BenchmarkField | |
Show BenchmarkField | |
BuildsExitcode BenchmarkField | |
BuildsExe BenchmarkField | |
HasBuildInfo BenchmarkField | A list of packages needed to build this component |
RenderableIndented BenchmarkField |
:: NonEmptyString | The name of the executable file that will be the benchmark |
-> [BenchmarkField] | Zero or more benchmark fields. |
-> Section |
Builds a Section
for benchmarks.
Getting module lists
fileExtensions :: [String] Source
Common extensions of Haskell files and files that are preprocessed into Haskell files. Includes:
- hs (Haskell)
- lhs (literate Haskell)
- gc (greencard)
- chs (c2hs)
- hsc (hsc2hs)
- y and ly (happy)
- x (alex)
- cpphs
:: MonadIO m | |
=> [NonEmptyString] | Look for files that have these extensions. Do not include the leading dot with the extension. For example, to look for Haskell and literate Haskell files only, use ["hs", "lhs"] |
-> FilePath | Start searching within this directory. |
-> Betsy m [NonEmptyString] | A list of Haskell modules in the given directory tree. The file contents are not examined; only the file names matter. Returned as a list of dotted names. |
Gets all Haskell modules in a given directory tree. Allows you
to specify what extensions you are interested in. For this to work
best, you will want to keep all your library modules in their own
directory, such as lib/
. You can also separate executables and
test suites this way. hsSourceDirs
will then tell Cabal to use
these directories.
Properties
Build types
Licenses
data Properties Source
Global package properties.
Properties | |
|
Cabal file
Usually you will not need this type, as defaultMain
produces
a Cabal
value for you, but you will need it if you want to use
bestyToCabalString
.
Represents an entire Cabal file.
cabal :: Properties -> [LibraryField] -> [Section] -> Cabal Source
Creates a Cabal
value when applied to package properties, any
applicable library fields, and any sections.
Generating Cabal files
defaultMain - usually all you need
defaultMain :: Betsy IO (Properties, [LibraryField], [Section]) -> IO () Source
Generates a Cabal file. If you have no library, just leave the
list of LibraryField
empty. Include any and all executables,
test suites, benchmarks, and repositories in the list of Section
.
Ensures that the generated Cabal file also includes any flags you
made with makeFlag
. If there is an error (such as a duplicate
flag) an error message is printed to standard error and the program
invokes exitFailure
; otherwise, the generated Cabal file is
printed to standard output and the program invokes exitSuccess
.
Other bindings - for more unusual uses
Errors that may result from running a Betsy
computation.
DuplicateFlag FlagName | The user requested creation of a duplicate flag. |
Failed String |
|
EmptyFlagName | The user requested creation of a flag with an empty name. |
class Renderable a where Source
Render an item. The rendered text shall contain no newlines.
class RenderableIndented a where Source
Render an item. The rendered text must contain a newline at the
end of each line and must end with a newline. The leftmost line of
the rendered text shall be indented by the given number of
indentation levels (the number of spaces in each level is set by
indentAmt
).
If there are no lines to indent, return an empty string.
renderIndented :: Int -> a -> String Source