suggestion for a small extension to configurations

Duncan Coutts duncan.coutts at worc.ox.ac.uk
Tue Oct 16 18:15:14 EDT 2007


With the current issue of the split base library we have a lot of code
like:

flag split-base

library
  if flag(split-base)
    build-depends: base >= 3.0, pretty, directory, etc
  else
    build-depends: base < 3.0

This does a backtracking search, checking both alternatives to see if
one is satisfiable.

It's a bit unsatisfactory however since we have to introduce a flag for
a choice which really should be automatic and not visible to the user.

It was always a deliberate part of the design of configurations that we
should not let package authors make automatic decisions without the
environment (packager or user) being able to influence the choice. 

So the question is, is there a special case that we can say really is an
automatic decision that the user does not need to control with a flag?

Here's my suggestion:

library
  build-depends: base
  if package(base >= 3)
    build-depends: pretty, directory, etc

and it is syntactic sugar for:

flag _unnamed1

library
  build-depends: base
  if flag(_unnamed1)
    build-depends: base >= 3.0
    build-depends: pretty, directory, etc
  else
    build-depends: !(base >= 3.0)

So we introduce a new predicate 'package()' that tests the version
number of a package that we've already declared that we depend on. It is
crucial that it be one we already depend upon. It does not test packages
in the environment generally, it tests the version of the package that
the environment has or might impose.

So we still introduce a backtracking point, it's just that now it's an
backtracking without a named flag. This is ok because the decision about
which branch to take is completely determined once the environment picks
a version for the dependent package in question.

Details...

each package predicate test introduces it's own distinct anonymous flag.
The flags are introduced in order, top to bottom, left to right and
appear after all other named flags.

eg:

flag blah

build-depends: foo, bar
if package(foo > 1) && package (bar > 1) && flag(blah)
  ...
else 
  ...

=>

flag blah
flag _1
flag _2

build-depends: foo, bar
if flag(_1) && flag(_2) && flag(blah)
  build-depends: foo > 1
  build-depends: bar > 1
  ...
else
  build-depends: !(foo > 1)
  build-depends: !(bar > 1)
  ...

Saying that we already depend on a package is interpreted relative to
the position of the package() test in the overall condition tree. It
means that to get to the branch where the package() test is, we must
have that package in the build-depends explicitly in some parent node.
Possibly as an extension we could allow it to appear in all the
alternatives of some sibling node eg:




So, overall, does that make sense? Is that sufficiently precise? Should
we do it?

Here's the canonical example. It's a package (tar) that works with
ghc-6.4, 6.6 and 6.8 and uses bytestring:

Flag bytestring-in-base
Flag split-base

library
  if flag(bytestring-in-base)
    -- bytestring was in base-2.0 and 2.1.1
    build-depends: base >= 2.0 && < 2.2
  else
    -- in base 1.0 and 3.0 bytestring is a separate package
    build-depends: base < 2.0 || >= 3, bytestring >= 0.9

  if flag(split-base)
    build-depends:   base >= 3.0, directory, old-time
  else
    build-depends:   base < 3.0

which we could simplify to:

library
  build-depends: base

  if package(base >= 3)
    directory, old-time

  if package(base < 2 || >= 3)
    build-depends: bytestring >= 0.9    

Much nicer I think.

Duncan



More information about the cabal-devel mailing list