Configurations proposal, take n
Ian Lynagh
igloo at earth.li
Mon Nov 27 20:07:24 EST 2006
Ross, Duncan, Ian and Simon M met in the morning before the Haskell "fun
in afternoon" event in Oxford to thrash out a revised design for Cabal
configurations. We think it was quite productive.
Here we will try to summarise the state of the proposal at the end of
the discussion. We've filled in some details that weren't (well)
discussed, so Ross and Simon might not be in total agreement with all of
the below.
Let's start with some BNF. It's a bit vague (e.g. the string and
cabal_stanza productions are not defined, and we haven't said anything
about disallowing certain kinds of stanzas inside conditional blocks),
but hopefully it gives the right idea.
cabal_file: flag_introductions "\n" body
flag_introductions: flag_introduction "\n" flag_introductions
| <nothing>
flag_introduction: "flag:" flagname [ "\n" "description:" string ]
body: cabal_stanza "\n" body
| "if" conditional "{" body "}" [ "else" "{" body "}" ] body
| <nothing>
conditional: "os(" string ")"
| "arch(" string ")"
| "flag(" flagname ")"
| "True"
| "False"
| "!" conditional
| conditional "||" conditional
| conditional "&&" conditional
| "(" conditional ")"
Now an example:
flag: fps_in_base
description: Use a base library that contains fps
flag: debug
description: Show debugging information
if flag(fps_in_base) {
build-depends: base >= 2
} else {
build-depends: base < 2, fps >= 0.8
}
if flag(debug) && os(windows) {
cpp-options: -DDEBUG_WINDOWS_STUFF
}
First we declare two flags, fps_in_base and debug. We've also put in
descriptions that might be shown by some Cabal GUI frontend.
Now we look at the body. Any os(foo) and arch(foo) conditionals are
immediately reduced to either True or False.
True, False, if/else, !, ||, && and (...) do what you'd expect.
flag(foo) where foo is not introduced is an error.
Flags use a three valued logic: They can have the value True, False or
Unknown. If the value True or False is given by the user for a
particular flag then that flag behaves as that value. The interesting
case is when flags have the (default) value Unknown.
We start off by assuming all Unknown flags have the value True (the
reason for starting with True is that we expect people to normally want
to write flags in the sense new_thing_is_available, and we generally
want to use new things when possible; there is scope to include a
"default: False" in the flag introduction stanza to give the user
control over this) and see if we can satisfy the build-deps. If we can,
then we are done! Otherwise, we try with the last introduced flag being
False, and so on, i.e. we do something like
head $ do fps_in_base <- [True, False]
debug <- [True, False]
guard (satisfies_deps fps_in_base debug)
return (body fps_in_base debug)
Note that the order is determined by the order in which flags are
introduced. Intuitively, it is equivalent to trying the rest of the file
with first "True" then "False" for each flag introduced.
There is scope for optimisation of this process, but it nevertheless
remains exponential in the worst case, with 2^n possibilities (given n
flags) needing to be tried. Things are even worse if using cabal-get,
which then needs to go off and try to install other packages (making
sure to not allow the package we are currently working on to be depended
upon by them!) in order to satisfy build-deps. However, we believe that
in practice this should not be a problem.
We don't think that the blocks-of-stanzas format fits well with
configurations, so we have used a style with braces. This also allows us
to nest conditionals, which should eliminate some duplication.
We also think that the cabal syntax may be made clearer to the user if
this style was employed for the basic cabal syntax itself, e.g.
library {
cabal stanzas
}
executable {
cabal stanzas
}
executable {
cabal stanzas
}
rather than the current
cabal stanzas
cabal stanzas
cabal stanzas
but that is a proposal for another day!
Ian and Duncan
More information about the cabal-devel
mailing list