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