Configurations proposal

Hi All, Before the 6.6 release we had a longish discussion on how to do configurations and their semantics and implementation complexity etc. I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning. Key points: It's crucial that the user / packaging system have control over optional aspects of configuration. For example it is verboten for a package to just inspect the current environment and decide to use a package just because it's available without the user getting any say in that decision. (Eg a user might want to build a package to depoly on another system where that optional package is not available). What it certainly allowed is for a package to state conditional dependencies, that is a package may say: "if you, the user, force me to build using base-1.0 then I absolutely require fps-0.8". So the user / packager must have complete control over the environment that the package sees. On the other hand, for convenience it's ok for the default decision about these optional things to depend on what's available, the os or whatever. It's just important that any defaults be overrideable. So that motivates a design that separates decisions based on the environment from conditional requirements that are intrinsic and independent of the environment. So I propose two kinds of stanza: flags and configurations. flag ::= "flag:" name "default:" fexp fexp ::= "available"( dependency ) | "os"( STRING ) | "arch"( STRING ) | fexp || fexp | fexp && fexp | !fexp | True | False conf ::= "configuration:" cexp fields cexp ::= "flag"( STRING ) | "using"( dependency ) | "os"( STRING ) | "arch"( STRING ) | cexp || cexp | cexp && cexp | !cexp | True | False So flag default values can depend on what packages are available in the environment. There's no restriction on the packages that are named. The default value may also depend on the OS and architecture the package is being configured for. (We should consider splitting this into host and target arch). The idea is that these named flags turn into user tweakable settings but with sensible default values. For example they might turn into check buttons in an IDE or --enable-foo flags to the cabal configure command line. We could certainly allow and optional description of the flag. Here are some examples: flag: debug default: False synopsis: Whether to turn on debug checks or not. description: Enabling debugging turns on blah blah blah .... flag: gui default: os(windows) || (available(gtk>=0.9.10) && available(cairo>=0.9.10)) As for the meaning, it's not clear to me if it should be that each test is independent of if all constraints must be satisfied on a single package simultaneously. That is if we have a expression like: available ( P > 1) && available ( P < 1) does there have to be a single package P with versions satisfying both constraints (ie impossible) or if the tests are independent and so may be satisfied by there being both P-0.9 and P-1.1 available. I'm not sure it really matters, so we should go for the simple independent test meaning. Opinions? Now for the configurations. The point of view here is that we already know exactly the versions of packages that the builder has chosen for us to use and all we can do is adjust our build settings on that basis or complain that we need more packages to be able to build under these conditions. If we need more packages then the build agent may let us have them or not. (in which case configuration fails). So let's look at a few examples: configuration: flag(debug) cpp-options: -DDEBUG other-modules: Thing.DebugUtils build-depends: SuperDebugLogger >= 1.1 configuration: flag(gui) && os(windows) build-depends: Win32 >= 2.0 configuration: flag(gui) && !os(windows) build-depends: gtk >= 0.9.10, cairo >= 0.9.10 configuration: using(base < 2) build-depends: fps >= 0.8 Note how in the last example, there was no flag involved. This conditional dependency has nothing to do with aspects of the 'outer' environment, it depends only on what packages we've been told to build with. So some fields in the stanza just affect internal aspects of the build like exposing more modules or using some cpp defines or whatever. The more tricky ones are the ones that ask for more deps. There are a few corner cases here. Remember that at the point that we are evaluating the configuration's guard expression have decided on the exact versions of packages that are going to be used. The easy case is adding a new dep, then it's up to the builder (ie Cabal) to figure out if that dep can be satisfied. If it can then the version that Cabal decides on is added to the set of packages we're using and we carry on. If it's not available then the configuration fails. The harder cases are: * adding a build dep on a package that we already depend on, but with a version constraint that is not satisfied by the version that was already selected. * adding a dep which causes the guard of another configuration to change. In the first case I would suggest that this configuration fails. However it may not be total failure, the build agent may re-try the configuration with a different set of package versions (if it has any flexibility in that area - depending on what's available and on any user policy). In the second case I would suggest that we allow configuration guards to change from false to true but not the other way around (ie it would be a configuration failure and an error on the part of the package author). This allows us to do a simple fixpoint and make the result independent of the order of the evaluation of the configuration guards. So the restriction on a guard changing from true to false would ban examples like: configuration: !using(foo) build-depends: foo Since supposing that foo is not in the top level build-depends then ! using(foo) will be true in which case we'll add foo. But now that changes the guard. The alternative to banning this kind of case is to simply ignore any changes from true to false, but then I think we'd get a bit of oddness and the evaluation order would matter in some strange cases. Can anyone think of any sensible examples that this rule would get in the way for? I mentioned earlier that the build agent might need/decide to backtrack if it found that the versions of packages it chose did not satisfy the extra build-deps introduced by some configuration stanza. How about we start with True as the Can anyone see if it's possible to find out a priori if there is a set of packages that can satisfy the conditional constraints. Is this a standard constraint solving problem? Does it have a solution? Here's a nasty example: configuration: using(P = 1.1) build-depends: P = 1.0 configuration: using(P = 1.0) build-depends: P = 1.1 Duncan

On Wed, 2006-10-25 at 01:36 +0100, Duncan Coutts wrote:
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
As a quick addendum, some comments arising from discussion with Ian... We may decide to ban more stuff. For example Ian suggested that it might not be sensible to let packages conditionally change the exposed modules. His example was using a package on Windows and then trying to use in on Linux and finding the exposed modules were all diferent. Changing hidden modules presumably would be ok. On the other hand, in Gtk2Hs I know one case where we do this. We have a Graphics.UI.Gtk.Cairo api module that is only included if Gtk was built against Cairo. In any case it could be faked by using cpp to just not export anything rather than not having the module exposed at all. So it's not clear that it's worth banning. Or maybe making it slightly harder is worth it so that people don't get in the habit. Ian also pointed out that some tests were missing, like a compiler test. I presume he was thinking of something like: configuration: implementation(hugs) some-thing: This is good stuff to discuss, but I think really those are the easy bits and we should figure out the tricky bits first. Duncan

On 25.10 01:36, Duncan Coutts wrote:
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
Nice to get this going, this is the most important missing feature in Cabal from my perspective.
So I propose two kinds of stanza: flags and configurations.
flag ::= "flag:" name "default:" fexp
fexp ::= "available"( dependency ) | "os"( STRING ) | "arch"( STRING ) | fexp || fexp | fexp && fexp | !fexp | True | False
conf ::= "configuration:" cexp fields
cexp ::= "flag"( STRING ) | "using"( dependency ) | "os"( STRING ) | "arch"( STRING ) | cexp || cexp | cexp && cexp | !cexp | True | False
Add '(' cexp ')' and '(' fexp ')' to the syntax.
As for the meaning, it's not clear to me if it should be that each test is independent of if all constraints must be satisfied on a single package simultaneously. That is if we have a expression like: available ( P > 1) && available ( P < 1) does there have to be a single package P with versions satisfying both constraints (ie impossible) or if the tests are independent and so may be satisfied by there being both P-0.9 and P-1.1 available.
I'm not sure it really matters, so we should go for the simple independent test meaning. Opinions?
We can already do "available( P > 1 & P < 1)" (the version syntax supports such things). I think simple independent test makes things easier to understand. But there were some counter examples in the old discussion I think.
Here's a nasty example:
configuration: using(P = 1.1) build-depends: P = 1.0
configuration: using(P = 1.0) build-depends: P = 1.1
This can be solved quite easily: 1) there is no prior build-depend on P => we are not using P so ignore these. 2) we depend on P version V. 3A) V=1.1: the first configuration matches, and we signal an error as we require two different versions of P. 3B) V=2: neither configuration matches and we continue happily ignoring them. version:

On Wed, 2006-10-25 at 12:32 +0300, Einar Karttunen wrote:
On 25.10 01:36, Duncan Coutts wrote:
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
Nice to get this going, this is the most important missing feature in Cabal from my perspective.
Yes, many things are blocking on this.
So I propose two kinds of stanza: flags and configurations.
flag ::= "flag:" name "default:" fexp
fexp ::= "available"( dependency ) | "os"( STRING ) | "arch"( STRING ) | fexp || fexp | fexp && fexp | !fexp | True | False
conf ::= "configuration:" cexp fields
cexp ::= "flag"( STRING ) | "using"( dependency ) | "os"( STRING ) | "arch"( STRING ) | cexp || cexp | cexp && cexp | !cexp | True | False
Add '(' cexp ')' and '(' fexp ')' to the syntax.
Yep.
As for the meaning, it's not clear to me if it should be that each test is independent of if all constraints must be satisfied on a single package simultaneously. That is if we have a expression like: available ( P > 1) && available ( P < 1) does there have to be a single package P with versions satisfying both constraints (ie impossible) or if the tests are independent and so may be satisfied by there being both P-0.9 and P-1.1 available.
I'm not sure it really matters, so we should go for the simple independent test meaning. Opinions?
We can already do "available( P > 1 & P < 1)" (the version syntax supports such things).
Yes there's already a full range expression with union and intersection.
I think simple independent test makes things easier to understand. But there were some counter examples in the old discussion I think.
Yes I agree. Were there any significant ones? I think I objected at first, but that was on the basis that the 'package' test should be for the package that we're actually going to use. That aspect is already contained in this proposal, so I can't think of any examples for this. One other bit that is not totally clear is, if ghc allows multiple versions of a package in a single lib/prog, then does available( P > 1 & P < 1) refer to a single package or a single version of a package? In general I've no idea how we're supposed to deal with being able to use multiple versions of a package. I think in most cases it'd break anyway. You can only really us multiple versions if you don't expose anything in an interface. For example if Cabal tried to satisfy this: build-depends: foo = 1.0 configuration: os(windows) build-depends: foo = 1.1 by using *both* foo-1.0 and 1.1 then I expect that's going to break and it's not what the author intended. This is something we'll have to address eventually.
Here's a nasty example:
configuration: using(P = 1.1) build-depends: P = 1.0
configuration: using(P = 1.0) build-depends: P = 1.1
This can be solved quite easily:
2) we depend on P version V. 3A) V=1.1: the first configuration matches, and we signal an error as we require two different versions of P.
I'm not sure it's that simple. In normal circumstances, if we had both 1.0 and 1.1 available and def default Cabal had picked the latest version 1.1, then the first configuration might suggest it be sensible to backtrack and try with 1.0. Of course as we know, in this case that will not get us anywhere. Here's a more real example: build-depends: foo >= 1.0 configuration: os(windows) build-depends: foo < 2.0 So suppose that we know that there's some additional version constraint on one platform. Suppose also that we have foo-1.0 and 2.0 installed/available. By default at the moment Cabal picks the latest version of each package. So we'll end up finding that version doesn't satisfy the constraints on windows. So the obvious solution is to backtrack and try foo-1.0 instead. So, can we determine if there is a solution? Duncan

On 10/24/06, Duncan Coutts
Hi All,
Before the 6.6 release we had a longish discussion on how to do configurations and their semantics and implementation complexity etc.
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
The only way I have really wanted to use configurations so far is to build debug, profiling, and optimized versions of a library. In order to build multiple configurations, is it going to still be the case that I have to "configure / build / install" separately for each configuration? I would like to be able to say something like "Setup.lhs configure --optimized --debug --profiled" to build all three versions at once. Also, let's say I have this: Name: foo Version: 1.0 configuration: flag(DEBUG) cpp-options: -DDEBUG Name: bar Build-depends: foo >= 1.0 configuration: flag(DEBUG) Build-depends: ???? How can I say that I want the DEBUG version of package "bar" to depend on the DEBUG version of package "foo."? Is this allowed? Name: foo Version: 1.0 configuration: flag(debug) Name: foo-debug Otherwise, how can I have a debug and an optimized configuration of a given library installed at the same time? Regards, Brian

On Wed, 2006-10-25 at 09:39 -0500, Brian Smith wrote:
The only way I have really wanted to use configurations so far is to build debug, profiling, and optimized versions of a library. In order to build multiple configurations, is it going to still be the case that I have to "configure / build / install" separately for each configuration? I would like to be able to say something like " Setup.lhs configure --optimized --debug --profiled" to build all three versions at once.
Cabal already lets you build both normal and profiling versions of libs using just one "configure / build / install". Just use configure -p (or --enable-library-profiling). I think there should also be a configure -O flag to build using normal optimisation flags for the current compiler (if the compiler/interpreter supports such a notion). That would be an either/or option though, not a both.
Also, let's say I have this:
Name: foo Version: 1.0
configuration: flag(DEBUG) cpp-options: -DDEBUG
Name: bar Build-depends: foo >= 1.0
configuration: flag(DEBUG) Build-depends: ????
How can I say that I want the DEBUG version of package "bar" to depend on the DEBUG version of package "foo."? Is this allowed?
No. This is not allowed. Indeed you can't express it with the syntax. That's because we don't record which configurations were active for a package that was built. This is intentional.
Name: foo Version: 1.0
configuration: flag(debug) Name: foo-debug
Otherwise, how can I have a debug and an optimized configuration of a given library installed at the same time?
You can't. No current Haskell implementation supports this (without doing things manually like using separate package databases / search paths). What you want is what the ghc build system refers to as a 'way'. It's a whole other discussion I think. Duncan

On 10/25/06, Duncan Coutts
On Wed, 2006-10-25 at 09:39 -0500, Brian Smith wrote:
Name: foo Version: 1.0
configuration: flag(debug) Name: foo-debug
Otherwise, how can I have a debug and an optimized configuration of a given library installed at the same time?
You can't. No current Haskell implementation supports this (without doing things manually like using separate package databases / search paths).
What you want is what the ghc build system refers to as a 'way'. It's a whole other discussion I think.
Yes, I am interested in building with different "ways" as you say. But, I think one of your examples used -DDEBUG. I think it is reasonable to want to install regular and debug versions of a package together. You said that no current Haskell implementation supports what I proposed. But, isn't this purely a Cabal issue. If I give a -debug flag to Cabal, and Cabal allows me to rename the package to foo-debug (by allowing me to put a name entry in the configuration stanza, like in my example), then foo and foo-debug can sit right next to each other in GHC or Hugs (at least). The user would do: runhaskell Setup.lhs configure -debug runhaskell Setup.lhs build runhaskell Setup.lhs install Now, foo-debug is installed. Then: runhaskell Setup.lhs configure runhaskell Setup.lhs build runhaskell Setup.lhs install Now, foo is installed too. Then, we can make packages that depend on either foo or foo-debug using the mechanism you proposed. Configuration: flag(debug) Name: bar-debug Build-depends: foo-debug || foo Configuration: !flag(debug) Name: bar Build-depends: foo The only extension to your idea is being able to change the name of the installed package in the configuration. I think that this is a very desirable feature because it allows people to build packages with names that uniquely identify the configuration, if the choose to do so. Regards, Brian

On 10/25/06, Brian Smith
Configuration: flag(debug) Name: bar-debug Build-depends: foo-debug || foo
Configuration: !flag(debug) Name: bar Build-depends: foo
After sending this I realized I made a mistake. Try this: Flag: debug Default: False Flag: foo-debug-available Default: available(foo-debug) Configuration: flag(debug) && foo-debug-available Name: bar-debug Build-depends: foo-debug Cpp-Options: -DDEBUG Configuration: flag(debug) && !foo-debug-available Name: bar-debug Build-depends: foo Cpp-Options: -DDEBUG Configuration: !flag(debug) Name: bar Build-depends: foo Unfortunately, this way of requesting "using the debug version if available" does not scale. - Brian

On 10/24/06, Duncan Coutts
Hi All,
Before the 6.6 release we had a longish discussion on how to do configurations and their semantics and implementation complexity etc.
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
Duncan, I have thought about this more and I have the following questions: * How does the proposed configuration mechanism work with executables? Can executables be built conditionally? Does the chosen configuration apply to library and all executables in the package description? * What happens when multiple configurations apply?: Configuration: True Build-depends: foo-1.0 Configuration: True Build-depends: foo-1.0 I think that if more than one condition applies, then configuration should fail with an error. * Are True and False really useful in cexp's? If a configuration is always selected (True) then the whole mechanism is not even needed, and if it is always False then it is never used. I think any combination of and's, or's, and not's with constants can be rewritten without the constants. * You mentioned the case where a using(x) expression conflicts with a build-depends: Configuration: using(foo = 1.0) Build-depends: foo = 2.0 It seems to me that: using(foo = x) == !(available foo > x), using(foo > x) == (available foo > x), using(foo < x) == !(available foo >= x), because of Cabal's "use the latest available version" policy. If all usages of using(x) can be replaced with available(f(x)), then I think it would be better to just get rid of using(x) and allow available(x) in both fexp's and cexp's, for consistency. However, I know that you do not want to do that, because you don't want available(x) allowed in cexp's. As you noted previously in the thread, using(x) seems to cause a lot of complications--is it really necessary? I think your example of using(x) is easily rewritten to not require it: flag: require-fps default: !available(base >= 2) configuration: require-fps build-depends: fps >= 0.8 * Why not use [ os="linux" ] instead of [ os(linux) ]? Since we already have (=) for comparing package versions, it seems to be sensible to overload (=) to compare these flags as well. * Your example used [ os(windows) ]. However, the value for System.Info.osis usually "mingw32" or "cygwin" (or similar) on Windows. I think that the os() and arch() values should be consistent with System.Info whenever possible. * Finally, I suggest that we create some package descriptions for complicated packages, to use as "use case" scenerios. In particular, I think that at least GHC, lhs2tex, wxHaskell, and network-alt would be cases to study. By having such use cases, you will have a clearer idea of exactly what is needed in practical usage. You can also use these cases to delineate the scope of Cabal's configuration mechanism. I have a Cabal files for GHC (stage2 only) that you can use if you think this is a good idea (actually, I can probably write the Cabal file for both stages of the GHC build, sans usages of features that Cabal doesn't have). Please let me know if this feedback is helpful. I am thinking to embed Cabal in two different tools, which is why I am so interested in these little details. Regards, Brian

On Wed, 2006-10-25 at 22:19 -0500, Brian Smith wrote:
On 10/24/06, Duncan Coutts
wrote: Hi All, Before the 6.6 release we had a longish discussion on how to do configurations and their semantics and implementation complexity etc.
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
Duncan, I have thought about this more and I have the following questions:
* How does the proposed configuration mechanism work with executables? Can executables be built conditionally? Does the chosen configuration apply to library and all executables in the package description?
that's not quite clear yet. I invite suggestions. It might be useful to have executable stanzas inside conditional stanzas, and it may be useful to have conditional stanzas inside executable stanzas.
* What happens when multiple configurations apply?:
Configuration: True Build-depends: foo-1.0
Configuration: True Build-depends: foo-1.0
I think that if more than one condition applies, then configuration should fail with an error.
I think it's important that mutliple configurations can apply at once. So then the question is how do we interpret additions to fields, eg do we interpret them as sets or multi-sets. I think we'll have to say that the order in which the flags are added is undefined (eg for things like cc-options).
* Are True and False really useful in cexp's? If a configuration is always selected (True) then the whole mechanism is not even needed, and if it is always False then it is never used. I think any combination of and's, or's, and not's with constants can be rewritten without the constants.
You're right that they're not directly useful but they can easily be expresses using and or & not. We do need convenient True and False in fexps so for consistency I would allow them in cexps, as they do no harm.
* You mentioned the case where a using(x) expression conflicts with a build-depends: Configuration: using(foo = 1.0) Build-depends: foo = 2.0 It seems to me that: using(foo = x) == !(available foo > x), using(foo > x) == (available foo > x), using(foo < x) == !(available foo >= x), because of Cabal's "use the latest available version" policy.
They're not the same. If the latest versions of each package do not satisfy the constraints then Cabal may have to pick different versions and then the above do not hold. Additionally, there's no reason why Cabal should not be extended to allow users to build against older versions of libs they have installed, eg for testing purposes or because they know only the older version is available on some site where they intend to deploy.
If all usages of using(x) can be replaced with available(f(x)), then I think it would be better to just get rid of using(x) and allow available(x) in both fexp's and cexp's, for consistency. However, I know that you do not want to do that, because you don't want available(x) allowed in cexp's. As you noted previously in the thread, using(x) seems to cause a lot of complications--is it really necessary? I think your example of using(x) is easily rewritten to not require it:
But even if using is equal to some available expression in normal circumstances, there's still a reason for using rather than available, which is that using is less expressive. It doesn't allow packages to automatically pick up dependencies merely because they're available in the environment. This is one of the main points of the design. Anything like that has to go via a flag, which is then controllable by the user or package manager.
* Why not use [ os="linux" ] instead of [ os(linux) ]? Since we already have (=) for comparing package versions, it seems to be sensible to overload (=) to compare these flags as well.
Yeah, I don't mind. It was just for consistency with the other tests. Convince everyone else.
* Your example used [ os(windows) ]. However, the value for System.Info.os is usually "mingw32" or "cygwin" (or similar) on Windows. I think that the os() and arch() values should be consistent with System.Info whenever possible.
You're probably right, though that in it self needs resolving since people do need reliable tests for being on windows. (os(mingw32) || os(mingw64) || os (cygwin)) starts to get a little unwieldy.
* Finally, I suggest that we create some package descriptions for complicated packages, to use as "use case" scenerios. In particular, I think that at least GHC, lhs2tex, wxHaskell, and network-alt would be cases to study. By having such use cases, you will have a clearer idea of exactly what is needed in practical usage. You can also use these cases to delineate the scope of Cabal's configuration mechanism. I have a Cabal files for GHC (stage2 only) that you can use if you think this is a good idea (actually, I can probably write the Cabal file for both stages of the GHC build, sans usages of features that Cabal doesn't have).
Very good idea.
Please let me know if this feedback is helpful. I am thinking to embed Cabal in two different tools, which is why I am so interested in these little details.
Lots of interesting points. Good stuff. Duncan

On 10/26/06, Duncan Coutts
On Wed, 2006-10-25 at 22:19 -0500, Brian Smith wrote:
* What happens when multiple configurations apply?:
Configuration: True Build-depends: foo-1.0
Configuration: True Build-depends: foo-1.0
I think that if more than one condition applies, then configuration should fail with an error.
I think it's important that mutliple configurations can apply at once. So then the question is how do we interpret additions to fields, eg do we interpret them as sets or multi-sets. I think we'll have to say that the order in which the flags are added is undefined (eg for things like cc-options).
It is important that builds be reproducible predictably, so I do not think it is a good idea to introduce undefined behavior. I think this will be made clearer when some real examples are created.
* You mentioned the case where a using(x) expression conflicts with a
build-depends: Configuration: using(foo = 1.0) Build-depends: foo = 2.0 It seems to me that: using(foo = x) == !(available foo > x), using(foo > x) == (available foo > x), using(foo < x) == !(available foo >= x), because of Cabal's "use the latest available version" policy.
They're not the same. If the latest versions of each package do not satisfy the constraints then Cabal may have to pick different versions and then the above do not hold.
But, they are pretty close to the same. The feature causes multiple difficulties. Do you have a real-world motivating example for it, that cannot easily be replaced by adding a flag and using available(x)? I propose that it be left out. Then the whole design becomes much simpler, doesn't it? In particular, no backtracking or non-trivial constraint solving is needed. Additionally, there's no reason why Cabal should not be extended to
allow users to build against older versions of libs they have installed, eg for testing purposes or because they know only the older version is available on some site where they intend to deploy.
They can do this already with your configuration mechanism: Build-depends: foo >= 2.0, bar >= 3.0 flag: minimum-versions Synopsis: Choose the minimum version number of all packages Description: This is useful to test that the minimum version constraints in the package description are correct. default: False configuration: flag(minimum-versions) Build-Depends: foo = 2.0, bar = 3.0 In the presence of flags and configurations, I do not think Cabal will be able to provide a feature to do this automatically. For example: If you want to extend Cabal to allow the user to specify a specific version of a package on the command-line, again, I think this should be done with flags. But, this requires that you allow non-boolean flags. Incidentally, if os(), and arch() were non-boolean flags, you would not need special syntax for them.
If all usages of using(x) can be replaced with available(f(x)), then
I think it would be better to just get rid of using(x) and allow available(x) in both fexp's and cexp's, for consistency. However, I know that you do not want to do that, because you don't want available(x) allowed in cexp's. As you noted previously in the thread, using(x) seems to cause a lot of complications--is it really necessary? I think your example of using(x) is easily rewritten to not require it:
But even if using is equal to some available expression in normal circumstances, there's still a reason for using rather than available, which is that using is less expressive. It doesn't allow packages to automatically pick up dependencies merely because they're available in the environment. This is one of the main points of the design. Anything like that has to go via a flag, which is then controllable by the user or package manager.
I agree with you, and that is partly why I am suggesting that using(x) is not good--it exposes (most of) the functionality of available(x) in exactly the context you do not want that functionality exposed. But, mostly, I just think it would be better to make the design simpler if possible.
* Your example used [ os(windows) ]. However, the value for
System.Info.os is usually "mingw32" or "cygwin" (or similar) on Windows. I think that the os() and arch() values should be consistent with System.Info whenever possible.
You're probably right, though that in it self needs resolving since people do need reliable tests for being on windows. (os(mingw32) || os(mingw64) || os (cygwin)) starts to get a little unwieldy.
I agree--I wish System.Info.os worked differently, actually. But, there are apparently some people (not me) that use Haskell on Cygwin, and they need to be able to detect it, because e.g. Posix and Readline are available on Cygwin but not Mingw. Maybe os(x) should be either "mingw" or "cygwin"? - Brian

Hi
* Your example used [ os(windows) ]. However, the value for
Woohoo! Well done, karma++ - for being factually correct.
I agree--I wish System.Info.os worked differently, actually. But, there are apparently some people (not me) that use Haskell on Cygwin, and they need to be able to detect it, because e.g. Posix and Readline are available on Cygwin but not Mingw. Maybe os(x) should be either "mingw" or "cygwin"?
WRONG! Plain on old flat WRONG! If you run WinHugs on Windows having built WinHugs with Visual Studio, never even showing anything in the build process either mingw or cygwin, then mingw is the System.Info.os, which is, without a doubt, WRONG! Perhaps this is the time for the Cabal people to push for a System.Info.windows flag, which is True on Windows and False if not? If you depend on an entirely wrong configuration variable, then that makes your configuration entirely wrong. For information, Yhc returns "Windows" if your OS is Windows - rather than returning the day of the week plus the birthday of your best friend (which makes similar sense to returning the installation status of an non-os component!). /Here endeth the rant Thanks Neil

On Thu, 2006-10-26 at 17:03 +0100, Neil Mitchell wrote:
Hi
* Your example used [ os(windows) ]. However, the value for
Woohoo! Well done, karma++ - for being factually correct.
I agree--I wish System.Info.os worked differently, actually. But, there are apparently some people (not me) that use Haskell on Cygwin, and they need to be able to detect it, because e.g. Posix and Readline are available on Cygwin but not Mingw. Maybe os(x) should be either "mingw" or "cygwin"?
WRONG! Plain on old flat WRONG! If you run WinHugs on Windows having built WinHugs with Visual Studio, never even showing anything in the build process either mingw or cygwin, then mingw is the System.Info.os, which is, without a doubt, WRONG!
Perhaps this is the time for the Cabal people to push for a System.Info.windows flag, which is True on Windows and False if not? If you depend on an entirely wrong configuration variable, then that makes your configuration entirely wrong. For information, Yhc returns "Windows" if your OS is Windows - rather than returning the day of the week plus the birthday of your best friend (which makes similar sense to returning the installation status of an non-os component!).
I agree. I think they should be using some other method to find cygwin components. Eg they can check if the posix package is available or something. Duncan

On Wed, Oct 25, 2006 at 01:36:05AM +0100, Duncan Coutts wrote:
I would like to re-propose the last scheme that I cam up with. I'll try to make it a concrete proposal as well as giving some motivating examples to give the intuition of the meaning.
I've finally caught up with this thread. I'm going to put all my replies here as most of them don't have a single obvious message to reply to, and this seems easier. Hopefully I've given my opinion on all the major outstanding issues, but let me know if I missed one. ----- Varying exposed modules First, as Duncan said, I don't think the exposed modules should be able to vary as then (as Brian Smith said) dependencies just don't make sense. Yes, base is currently broken in this regard, but that's something I hope we can fix by refactoring it. It might be good to have a concept of half-exposed modules, which are only exposed to "friends" (other bits of gtk2hs, your testsuite, that sort of thing), but that is orthogonal so we can ignore that for now. ----- Multiple versions of a package I think: build-depends: foo (== 1) build-depends: foo (== 2) should refer to the same foo, and thus cabal will fail given the above. If you want to have both versions of foo available then I think you should say something like: build-depends: foo AS oldfoo require: oldfoo (== 1) build-depends: foo AS newfoo require: newfoo (== 2) If there is agreement then this also becomes orthogonal, and can be dealt with as part of the package grafting discussion rather than this one. ----- using/backtracking The using/backtracking debate seems to be that Duncan wants to be able to say: build-depends: base configuration: using(base < 2) build-depends: fps but having this power allows you to say things like: configuration: using(foo == 2) build-depends: foo == 1 configuration: using(foo == 1) build-depends: foo == 2 where you would have to try foo 2, fail and backtrack to foo 1. There is all sorts of nastiness lurking around here, so I would also like to remove using(). Instead, Duncan's example would be written: flag: fps_in_base default: available(base >= 2) configuration: fps_in_base build-depends: base(>= 2) configuration: !fps_in_base build-depends: base(< 2), fps The user would be able to shoot themselves in the foot by overidding the fps_in_base flag such that they don't have the deps installed, but that's their own fault. Note that the right thing will happen if they just leave it as the default. ----- Order of cc-options At some point Duncan said: I think we'll have to say that the order in which the flags are added is undefined (eg for things like cc-options). I don't think this is a good idea, as some options need to be given in a particular order. Rather, I think we should guarantee to give them in the order they appear in the .cabal file. ----- Other bits A couple of other things occured to me. First, it should not be permitted to declare flags inside a configuration (I don't think anyone was saying otherwise, but I also don't think I saw it explicitly stated). Second, I would like a cabal flag to give an error if I have not given a value explicitly for any flag. I'd also like to be able to say --myflag=default (or whatever syntax you like) as a way to satisfy this check. Thanks Ian

On Wed, 2006-11-01 at 01:13 +0000, Ian Lynagh wrote:
I've finally caught up with this thread. I'm going to put all my replies here as most of them don't have a single obvious message to reply to, and this seems easier. Hopefully I've given my opinion on all the major outstanding issues, but let me know if I missed one.
Ta! :-)
----- Varying exposed modules
First, as Duncan said, I don't think the exposed modules should be able to vary as then (as Brian Smith said) dependencies just don't make sense. Yes, base is currently broken in this regard, but that's something I hope we can fix by refactoring it.
It might be good to have a concept of half-exposed modules, which are only exposed to "friends" (other bits of gtk2hs, your testsuite, that sort of thing), but that is orthogonal so we can ignore that for now.
Yep, we can ignore it for the moment.
----- Multiple versions of a package
I think:
build-depends: foo (== 1)
build-depends: foo (== 2)
should refer to the same foo, and thus cabal will fail given the above. If you want to have both versions of foo available then I think you should say something like:
build-depends: foo AS oldfoo require: oldfoo (== 1)
build-depends: foo AS newfoo require: newfoo (== 2)
If there is agreement then this also becomes orthogonal, and can be dealt with as part of the package grafting discussion rather than this one.
That seems very reasonable.
----- using/backtracking
The using/backtracking debate seems to be that Duncan wants to be able to say:
build-depends: base
configuration: using(base < 2) build-depends: fps
but having this power allows you to say things like:
configuration: using(foo == 2) build-depends: foo == 1
configuration: using(foo == 1) build-depends: foo == 2
where you would have to try foo 2, fail and backtrack to foo 1. There is all sorts of nastiness lurking around here, so I would also like to remove using(). Instead, Duncan's example would be written:
flag: fps_in_base default: available(base >= 2)
configuration: fps_in_base build-depends: base(>= 2)
configuration: !fps_in_base build-depends: base(< 2), fps
The user would be able to shoot themselves in the foot by overidding the fps_in_base flag such that they don't have the deps installed, but that's their own fault. Note that the right thing will happen if they just leave it as the default.
Yeah it'd work but I don't like it. It exposes too much to the user for one thing. It's not always going to do the the right thing either. In this case you can only have one version of base installed at once, but for other libs that's not the case. So imagine I do have base-1.0 and 2.0 installed then there's no guarantee that the version that end up meaning the test comes out true is the same one we're using. So it needn't even do the right thing if they leave it as the default. This is an example of why 'available' is not always what you want. What we want here is 'using'. So I claim that what we're really trying to express is an implication dep: build-depends: base, (base < 1 `imp` fps >= 0.8) but then that allows things like: build-depends: base, (base < 1 `imp` base > 1) (Not that this dep must be banned, it's just that it's not satisfiable) My question is: can we find a solution to these kinds of deps in a reasonable amount of time. Is there any simple restriction we could put on these deps to make solving them easier, eg some monotonicity condition.
----- Order of cc-options
At some point Duncan said:
I think we'll have to say that the order in which the flags are added is undefined (eg for things like cc-options).
I don't think this is a good idea, as some options need to be given in a particular order. Rather, I think we should guarantee to give them in the order they appear in the .cabal file.
Actually later I recanted and agreed that it is indeed quite possible to do this.
----- Other bits
A couple of other things occured to me. First, it should not be permitted to declare flags inside a configuration (I don't think anyone was saying otherwise, but I also don't think I saw it explicitly stated).
Yep.
Second, I would like a cabal flag to give an error if I have not given a value explicitly for any flag. I'd also like to be able to say --myflag=default (or whatever syntax you like) as a way to satisfy this check.
Yes, this is useful for distro packagers. Duncan

On Wed, Nov 01, 2006 at 01:34:35AM +0000, Duncan Coutts wrote:
On Wed, 2006-11-01 at 01:13 +0000, Ian Lynagh wrote:
flag: fps_in_base default: available(base >= 2)
configuration: fps_in_base build-depends: base(>= 2)
configuration: !fps_in_base build-depends: base(< 2), fps
The user would be able to shoot themselves in the foot by overidding the fps_in_base flag such that they don't have the deps installed, but that's their own fault. Note that the right thing will happen if they just leave it as the default.
Yeah it'd work but I don't like it.
It exposes too much to the user for one thing. It's not always going to do the the right thing either. In this case you can only have one version of base installed at once, but for other libs that's not the case. So imagine I do have base-1.0 and 2.0 installed then there's no guarantee that the version that end up meaning the test comes out true is the same one we're using.
I'm not following; with what I wrote above, the base we end up using would make the flag have the value it does. You could certainly write a buggy .cabal file, but there are many ways you can do that without even using advanced features, e.g. build-depends: foo (== 1) build-depends: foo (== 2) You could, if you so wished, force fps_in_base to be false even if you had both bases, in which case it would use base 1. But presumably you wanted that to happen... (that makes me think of something else - we probably want the user to be able to tell cabal "I want to use this version foo"; this should probably also change the results of available predicates).
So it needn't even do the right thing if they leave it as the default. This is an example of why 'available' is not always what you want. What we want here is 'using'.
I'm still not convinced we are not better off explicitly using the packages that we want with build-deps, rather than having cabal try to work out what versions it thinks we want with all the backtracking and stuff to contend with. It should certainly be easier to implement without using predicates, too.
So I claim that what we're really trying to express is an implication dep:
build-depends: base, (base < 1 `imp` fps >= 0.8)
but then that allows things like:
build-depends: base, (base < 1 `imp` base > 1)
(Not that this dep must be banned, it's just that it's not satisfiable)
My question is: can we find a solution to these kinds of deps in a reasonable amount of time.
Is there any simple restriction we could put on these deps to make solving them easier, eg some monotonicity condition.
I'm more interested in that this allows us to write build-depends: foo, bar build-depends: (foo == 1 `imp` bar == 2) build-depends: (foo == 2 `imp` bar == 1) which is satisfiable in 2 ways if we have all 4 packages and versions available: * foo 1, bar 2 * foo 2, bar 1 neither of which is obviously "better" than the other. Altogether, I think this is another feature which introduces lots of nasty issues that we are better off avoiding. Thanks Ian
participants (5)
-
Brian Smith
-
Duncan Coutts
-
Einar Karttunen
-
Ian Lynagh
-
Neil Mitchell