haskell / cabal

Official upstream development repository for Cabal and cabal-install
https://haskell.org/cabal
Other
1.6k stars 689 forks source link

Extend the grammar/semantics of `default:` to support boolean expressions #5328

Open hvr opened 6 years ago

hvr commented 6 years ago

This is expanded version of this comment:

Currently, the grammar for the default field is limited to

default: <bool-value>

This is perfectly sufficient for properly used automatic flags (i.e. manual: False and used in a way which allows the solver to unique determine their value -- there's a separate feature request to have cabal detect "non-functional" uses of automatic flags); however, for manual flags the limitation of allowing only a constant boolean literal in default: is rather limiting.

Instead, starting with e.g. cabal-version:3.0, we should allow an enhanced version of default

default: <boolean-expression>

and where <boolean-expression> would be the expression we already support in if clauses which currently supports the

Such expression can be statically determined, as the impl/1, arch/1, and os/1 predicates are globally constant for a solver session.

The flag/1 predicate however requires us to impose a limitation though, in order to keep the implementation simple (allow for referential checks during single-pass parsing) and avoid the issue of undecidable flags (e.g. via cyclic references). To that end, the following simple well-formedness rule needs to be added to the specification of flag/1

phadej commented 6 years ago

Can/cannot manual flag default value depend on automatic flag value?

flag old-time
  manual : False
  default: False

flag other-flag
  manual :True
  default: flag(old-time)

Concrete motivation for proposal would be good to have too. What existing problems it would solve?

hvr commented 6 years ago

@phadej that's a very good point; I've augmented the spec; a more drastic way would be to simply disallow any use of flag() within default expressions.

My main use-case for this would be architectural conditionals; e.g. defaults for a manual flag inferred based on architecture or ghc versions (simplest case: a flag portable which enables a pure Haskell version of an implementation avoiding any platform/compiler specific features), but with the ability for a user to override this default. Another use-case is a flag to enable pkgconfig-based discoverability; you'd want this default on for some platforms like os(linux), but not necessarily on os(windows); otoh, some users may still prefer to turn on pkgconfig support on windows regardless. I stumble over this kind of situation every now and then, and keep wishing this had been already implemented...

juhp commented 6 years ago

Yes my use-case is basically ghc version.

grayjay commented 6 years ago

2397 and #2429 are related, but they use an if-else around the default field.

avh4 commented 2 years ago

My use-case for this is that my project has multiple frontend drivers, including a TUI driver, a gtk+ driver, and a Cocoa driver (and possibly others in the future). The gtk+ driver can in theory build on MacOS, but doing so requires a bunch of native library setup that normally won't be wanted; whereas gtk+ will be the preferred frontend for Linux builds.

I'd like to be able to have a "gtk" flag that defaults to False when os(darwin), defaults to True otherwise, and can be manually overridden in either case.

michaelpj commented 2 months ago

Here's a concrete usecase based on GHC versions.

HLS has a bunch of cabal flags determining whether to try and build in support for some tool or other, say stan. Now, often we know that stan will only build on certain GHC versions. We basically have two options:

  1. haskell-language-server +stan means "build HLS with stan support, possibly getting build failures if it doesn't work on your GHC version". In this case we probably need to have the default value of the stan flag be false, so that HLS works when built from Hackage, which means that users need to manually set a bunch of flags to true.
  2. haskell-language-server +stan means "build HLS with stan support, if that will work". In this case we can set the default value of the flag to "true", and put conditional logic in the cabal file to ignore if it we're on a non-working GHC version. Then users don't have to set flags manually, but they might be surprised that the flag is on and they don't get the feature!

The ideal situation would be 1, but we can change the default value of the flag based on the GHC version. Then users will get sensible behaviour in all cases, but the flag continues to mean "definitely try and include this functionality".

phadej commented 2 months ago

@michaelpj fwiw, the https://github.com/haskell/cabal/issues/3526 would also solve that in a convenient way. You could have a three way flag, True, False, and Auto.

FWIW, you can encode that today too with two flags, but that's not very user friendly.