Cabal Configurations Status Report
Simon Marlow
simonmarhaskell at gmail.com
Fri Jul 27 04:11:50 EDT 2007
Thomas Schilling wrote:
> Hello cabal-devel,
>
> the following is a summary of the changes to Cabal to enable Cabal
> Configurations.
>
> As we are about to integrate Cabal configurations into the main
> branch in order to get more testing feedback, I think it is a good
> time to give an overview about my changes.
>
> ** Parsing
>
> In order to allow the new syntactic categories of sections and if-else
> blocks, I added those categories to the low-level parsing primitives.
> Possible section names are "library", "executable", or "flag" of
> which the latter two require the name of the executable/flag name as a
> parameter. Conditional blocks may have an optional else-branch.
>
> The current implementation is rather restrictive in its permitted
> style; namely, the opening "{" has to be on the same line as the
> section name or "if" keyword and the closing "}" has to stand alone on
> its line unless it is followed by an "else {" branch.
>
> At a higher level we then parse this lower-level structure into a
> 'GenericPackageDescription' structure. This is defined as
>
> data GenericPackageDescription =
> GenericPackageDescription {
> packageDescription :: PackageDescription,
> packageFlags :: [Flag],
> condLibrary :: Maybe (CondTree ConfVar [Dependency]
> Library),
> condExecutables :: [(String, CondTree ConfVar [Dependency]
> Executable)]
> }
>
> The 'packageDescription' field contains a conventional package
> description with only part of the fields filled in, namely all global
> settings like version number or package name. However, it does *not*
> contain a valid "build-depends" field. This and all other
> library-related parts now go into the library section. The old Cabal
> format can easily be translated into this format so backwards
> compatibility is no problem.
>
> The 'CondTree' structure is an internal representation of a block of
> field descriptions and possible conditional blocks.
>
> data CondTree v c a = CondNode
> { condTreeData :: a
> , condTreeConstraints :: c
> , condTreeComponents :: [( Condition v
> , CondTree v c a
> , Maybe (CondTree v c a))]
> }
>
> A 'CondTree' node contains both data and constraints/dependencies.
> Conditions are parameterized over the variable type, which in our case
> will presumably be only one type.
>
> data ConfVar = OS String | Arch String | Flag String
>
>
> ** Resolving Conditions/Dependencies
>
> 'CondTree's can be resolved to a flat 'a' (the data part) given a
> function to determine which constraints are satisfied and which
> aren't.
>
> resolveWithFlags :: Monoid a =>
> [(String,[Bool])]
> -- ^ Domain for each flag name, will be tested in order.
> -> String -- ^ OS name, as returned by System.Info.os
> -> String -- ^ arch name, as returned by System.Info.arch
> -> [CondTree ConfVar [d] a] -- ^ All CondTrees must satisfy the deps
> -> ([d] -> DepTestRslt [d]) -- ^ Dependency test function.
> -> (Either [d] -- missing dependencies
> ( [a] -- result data
> , [d] -- overall chosen dependencies
> , [(String, Bool)])) -- chosen flag assignments
>
> This function is the heart of the flag resolution process and is
> implemented rather naively by trying all possible flag assignments and
> choosing the first that satisfies the dependencies. As long as we
> don't use too many flags (or set some explicitly) this shouldn't be a
> problem.
>
> Note that the data has to be a Monoid. The problem is, that due to
> lack of first-class record fields[1] we have to store a partially
> filled-in 'a' (in our use case a 'Library' or 'Executable') and then
> rely on the Monoid instance to do the right thing. This strategy also
> required changing the way defaults are set; before, this was done by
> setting it in the empty value and just (blindly) overwriting it
> whenever it was set explicitly in the .cabal file. Now, however, we
> have to set it after the package has been resolved, and find out which
> fields haven't been set explicitly. This is a rather hackish
> ("hacky"?) approach but seems to work fine so far.
>
> [1] Implementing approximations would likely require extensions to
> Haskell98 thus wouldn't be a good idea for Cabal which tries to be
> as portable as possible.
>
> The function that does the actual flag-assignment resolution on
> packages is
>
> finalizePackageDescription ::
> [(String,Bool)] -- ^ Explicitly specified flag assignments
> -> Maybe [PackageIdentifier] -- ^ Available dependencies
> -> String -- ^ OS-name
> -> String -- ^ Arch-name
> -> GenericPackageDescription
> -> Either [Dependency] -- ^ Missing dependencies
> ( PackageDescription -- ^ Resolved package description
> , [(String,Bool)]) -- ^ Flag assignments chosen
>
> Using this function the 'configure' command determines a resolved
> package description (or fails) and stores it in the 'LocalBuildInfo'
> structure. From there it is available to all the other setup
> commands. Unfortunately, this is not sufficient for all build
> commands.
>
> ** Breaking the Interface to Setup.(l)hs
>
> Due to the new staged package resolution process, the 'confHook'
> arguments had to be changed, and most hooks could be changed to drop
> the 'PackageDescription' argument.
Regarding hooks, I made a proposal for a radical overhaul of hooks a couple
of months ago:
http://www.haskell.org/pipermail/cabal-devel/2007-April/000512.html
I think there was general agreement, see the followup thread
http://www.haskell.org/pipermail/cabal-devel/2007-May/000528.html
It might be worth doing this at the same time - lump all the breaking
changes together to avoid repeated pain.
Cheers,
Simon
More information about the cabal-devel
mailing list