<duncan.coutts@worc.ox.ac.uk
> wrote:
In all this discussion on configurations I think I'm not getting over my main point about why we can't go for the 'easy' option of allowing package 'available' tests everywhere.
<snip>
configuration: available(foo >= 2)
cpp-options: -Denable_cool_feature
I think that everybody already agrees that available(x) should not be allowed in cexp's. You've always been clear about the disadvantages of allowing that.
My point is that using(x) should not be allowed in cexp's either. I suggestion the following rules, which would eliminate the need for using(x), by allowing a configuration like this:
configuration: using(foo >= x)
....
to always be written as:
flag: blah
default: avalable(foo >= x)
configuration: blah
1. If Cabal is not given an explicit package version number to use, then it will always choose the latest already-installed version of that package that satisfies the effective Build-depends constraints. This is already how it works right now.
2. If Cabal is told to use a specific version of a package (e.g. on the command line), then
available(x) will return false for all versions of that package except for the version that the user requested. If that specifically-requested version fails to satisfy any effective configurations' Build-Depends for that package, then the build stops with an error.
3. If Cabal-get can satisfy a dependency without downloading a
different (usually newer) version of a package, then it will do so.
Otherwise, it will download and installed the latest available
version of the package that satisfies the effective Build-depends in the package
description.
4. The effective Build-depends in the intersection of the main stanza's Build-depends with the Build-depends of all enabled configurations. Effectively, a configuration cannot loosen or remove the dependencies given in the main stanza--a configuration can only restrict the dependencies.
Example 1:
Installed versions of foo: 1.0, 2.0, 3.0
Build-depends: foo >= 2.0
foo 3.0 is selected because it is the latest available version >= 2.0
Example 2:
Installed versions of foo:
1.0, 2.0, 3.0
Build-depends: foo < 2.0
foo 1.0 is used to build the package, because it is the latest version < 2.0
Example 3:
Installed versions of foo: 1.0, 2.0, 3.0
Build-depends: foo >=
2.0
Flag: bleeding-edge-features
Default: available(foo >= 3.0)
configuration: bleeding-edge-features
Build-depends: foo >= 3.0
cpp-options: -DBLEED
(a) Additional user input: use
foo-2.0
The user did not give the bleeding-edge-features flag, so its default is used. Since the user selected foo-2.0 explicitly, then available(foo >= 3.0) is false (when a specific version is selected, all other versions are made unavailable). Thus, the bleeding-edge-features flag is not enabled automatically, so the corresponding configuration is not used, (no -DBLEED).
(b) Additional user input: --bleeding-edge-features, use foo-2.0
The user forced the bleeding-edge-features flag, so the corresponding configuration is used. But, that configuration requires foo >= 3.0. The build stops with an error because foo >=
3.0 is not available due to the "use foo-2.0".
Example 4:
Installed versions of foo: 1.0, 2.0, 3.0
Build-depends: foo >= 2.0
Additional user input: none
Available for download:
foo-3.0, foo-4.0
foo-3.0 will be selected (the latest installed version, >= 2.0)
Nothing will be downloaded.
Example 5:
Installed versions of foo: 1.0, 2.0
Build-depends: foo >= 3.0
Available for download: foo-3.0, foo-4.0
Addtional user input: none
No installed versions of foo satisfy foo >= 3.0
If we are using Cabal (not cabal-get), then the build fails with an error.
If we are using cabal-get (not just Cabal) then there are two versions of foo available for download that satisfy it. Cabal-get will download and register
foo-4.0 (the latest version that satisfies the constraint) Then, Cabal will build the package using foo-4.0.
Example 6:
Installed versions of foo: 1.0, 2.0, 3.0
Build-depends: foo >= 2.0
Available for download:
foo-3.0, foo-4.0
Addtional user input: use foo-4.0
Although foo-2.0 and foo-3.0 are installed neither one will satisfy foo >= 3.0 because "use foo-4.0" effectively hides all versions except 4.0. Since
foo-4.0 is available for download, Cabal-get will download and register it. Then, Cabal will build the package using foo-4.0.
Example 7:
Build-depends: foo >= 2.0
Flag: use-old-version
Default: !available(foo >= 2.0)
Configuration: use-old-version
Build-depends: foo < 2.0
cpp-options: -DCOMPAT
Here, the user is trying to loosen the dependencies from the main stanza using a flag. But, as I stated in rule 4 above, the build-depends are always taken as intersections. That is, we have (foo >= 2.0) && (foo <
2.0) = { }, so the build fails. Notice that Cabal can (and should) determine statically that the Build-depends of the configuration is mutually exclusive with the build-depends of the main stanza and thus issue an error/warning.
Example 8:
Build-depends: foo >= 1.0
Flag: use-new-version
Default: available(foo >= 2.0)
#Notice that this stanza is not needed: if availble(foo >= 2.0),
#then we are guaranteed to use such a version
#Configuration: use-new-version
#Build-depends: foo >= 2.0
Configuratoin: !use-new-version
cpp-options: -DCOMPAT
This is a reformulation of Example 7 that actually works as intended: If foo >=
2.0 is not availabe, then we define COMPAT, otherwise, we don't.
Example 9:
Build-depends: foo >= 2.0
Flag: use-old-version
Default: !available(foo >= 2.0)
Configuration: use-old-version
Build-depends: foo >= 1.0
cpp-options: -DCOMPAT
Here is another reformulation of Example 7. Notice that the Build-depends of the main stanza is more restrictive than the build-depends of the configuration. This is a good indication that the user does not understand how Build-depends are combined, so a warning/error should be issued.
Regards,
Brian