
On Fri, 2006-08-11 at 12:15 +0100, Simon Marlow wrote:
Einar Karttunen wrote:
2) HUnit
build-depends: Flag(debug) ? (Flag(Hunit11) ? (HUnit-1.1), !Flag(Huni11) ? (HUnit >= 1, Hunit < 1.1 || > 1.1))
flag: debug = !True flag: HUnit11 = HUnit-1.1 && !(Hunit > 1.1)
configuration: Flag(HUnit11) ghc-options: -DHUNIT11
This seems quite complex. But it works - the meaning is: * if debug flag is set then: * if HUnit11 is set depend on on HUnit-1.1 and -DHUNIT11 * if HUnit11 is not set depend on version of HUnit that is >1 and (<1.1 or >1.1) that is not 1.1 * thus the -D is correct * the guess is just a guess and does not affect the semantics
What can I say? yeuch! I think we've left simplicity behind here.
Well the point is to separate the decision to pick up an optional dependency from actually doing it. I'm not going to defend the '?' syntax too much, but I do like the separation of flags with default values that depend on the environment from configuration tests which depend on those flags.
One goal (that I keep having to remind myself of) is that a .cabal file should be modifyable by an IDE such as Visual Haskell. I can't see Visual Haskell being able to successfully modify that complicated build-depends expression.
Is that really any different from the conditionals in the configuration tests?
This is one reason I decided to go the simpler route of individual configurations with conditionals, and why a conditional could be evaluated independently of all the others knowing only facts about the environment.
FWIW, using my original syntax and semantics, here's how to do what you did above:
configuration: debug && !Hunit11 build-depends: HUnit >= 1
configuration: debug && HUnit11 build-depends: HUnit-1.1
configuration: debug && (HUnit11 || package(HUnit>=1, 1.1)) ghc-options: -DHUNIT11
The whole thing is easier to understand IMO, and it's simple to implement too: no ordering, conditionals can be evalutated independently.
Well they're only independent if you take the view that package() conditionals test what's available in the environment rather than what we're going to actually use. If it's what we're going to use then one configuration can affects the condition of another. Having it just pick up things from the environment is also evil. Any package manager (debian, gentoo) need to be able to control/override that. That's why we were proposing a split between flags with their default values and conditions based on those flags. That way it's clear what can be overridden.
I'm back to the two-argument package() conditional, because it's actually useful. package(Hunit>=1, 1.1) is not the same as package(HUnit-1.1) or even package(HUnit>=1 && 1.1). It says "the dependency HUnit>=1 resolves to version 1.1", which is exactly what you wanted to know. This is how you write conditionals that test the versions of packages you actually depend on, which is the criticism raised by Duncan at the beginning of this thread. I believe my original syntax addressed this concern.
Ok if we have package(Hunit>=1, 1.1) then do we not then have an ordering issue? configuration: debug build-depends: HUnit-1.1 configuration: package(HUnit>=1, 1.1) ghc-options: -DHUNIT11 If I do the first one then the second then we know that we're going to depend on HUnit-1.1, if we do it the other way around we don't know that yet. That was one reason for bringing the conditionals up front with the '?' syntax that you object to. We can move it back to the above style if it's easier for users/IDEs but then there must be an ordering.
Now there are bad things that you can do with this syntax (auto-optional dependencies), and there are downright ridiculous things you can do with this syntax (test versions of packages you don't depend on), but I believe we could either make Cabal warn about those or disallow them altogether. I don't mind which.
I really worry that this will make many packages unpackagable because their semantics will not be translatable into sane distro packages. As I said, if I made a gentoo package that did auto-optional dependencies then the QA team would shoot me. I think it's quite easy to make package tests be only internal, that is what deps have we resolved for the package, and not mix that up with what happens to be available in the environment. Then as a separate thing have some facility to make the default decisions about which configurations to use to depend on the environment, but in a clearly separated way so that users or packagers can override the defaults. So let me suggest something with flags but without '?' syntax: flag: debug default: False configuration: flag(debug) build-depends: HUnit-1.1 configuration: using(HUnit==1.1) ghc-options: -DHUNIT11 flag: gui default: os(windows) || (available(gtk>=0.9.10) && available(cairo>=0.9.10)) configuration: flag(gui) && os(windows) build-depends: Win32>=1.1 ghc-options: -DUSE_WIN32_GUI configuration: flag(gui) && !os(windows) build-depends: gtk>=0.9.10, cairo>=0.9.10, glib>=0.9.10 ghc-options: -DUSE_GTK_GUI So in a flag we can make the defaults depend on the environment. In a configuration test we can only depend on internal things like flags and which version of a package that we depend on we decided to use. (I'm deliberately avoiding using 'package' here because it's meaning is overloaded with our various proposals). I'd also say that os/arch tests could be used in flag defaults and also in configuration tests since they're fixed aspects of the environment rather than variable like package availability. Then there is the problem that the order of evaluating configurations is significant. My previous proposal was to move them up front and use '?' but I see that's not great either. We could just say they're done in order. I do not think it's ok that they be made independent by just saying that they only depend on the environment rather than what we're actually using, that stores up too many problems imho. Duncan