Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How should a package represent the fact that it does not support a particular GHC version? #9648

Open
michaelpj opened this issue Jan 23, 2024 · 7 comments

Comments

@michaelpj
Copy link
Collaborator

Suppose I have a component C of a package P that does not build with GHC X, and I want to signal this to my users. There are a few ways of doing this:

  1. Set base bounds that exclude the version of base that ships with that GHC
    • Variant: bound some other non-reinstallable package
    • Wart: this is sometimes semantically wrong, perhaps your package would build fine with that version of base, it's really GHC that's the problem.
    • UX: user gets a somewhat confusing solver error talking about base version compatibility
    • Prevalence: very prevalent
  2. Use a cabal file conditional on impl(ghc) and buildable: False
    • UX: user gets a somewhat confusing solver error saying the component is not buildable (see Associate a message with buildable: False #8905), in some circumstances cabal ignores the component entirely (e.g. when doing cabal build all).
    • Wart: the nice UX is not transitive: if you have a package C2 that depends on C, then it also clearly "won't work on that GHC", but you just get solver errors and not the nice behaviour of ignoring the component
    • Prevalence: I've only ever seen this at $WORK
  3. Write in tested-with.
    • Wart: this is pure metadata that does nothing.
    • Wart: this is sometimes semantically wrong, it's too soft. Often it's not just that I "haven't tested with GHC X, so it might not work", I actively know that it won't work with GHC X.
    • UX: user gets no feedback at all, package just fails to compile or work or whatever
    • Prevalence: occasionally used

None of these feel great to me. At the moment I think the buildable: False approach has the best UX, and it seems like the right way in principle, since it's a similar issue to "how should a package represent the fact that it does not support windows?". But it's still a bit unsatisfactory, and it's not widely used in the ecosystem.

This problem bites us quite a bit at work, since we have a lot of packages and they tend to gradually get support for new GHC versions. But our big package builder doesn't have a good way of knowing when a package isn't expected to work on a particular GHC so we have to have a big manual list of exceptions. It would be nice if this was more discernable from the package metadata.

It would be nice also if cabal recommended an approach. In particular, if we solved #8905 then I think promoting the buildable: False approach would be a strict improvement over setting base bounds, since it could give a better error to users. We're all used to interpreting solver errors mentioning base as GHC version conflicts, but it's a terrible UX.

The other idea I had is that maybe we could have some syntax to make this easy, e.g.: supported-ghcs on a component or at the top-level that implies the appropriate conditionals with nice messages.

@philderbeast
Copy link
Collaborator

philderbeast commented Jan 23, 2024

For dealing with project P having package K compiling (or not) with GHC X, we're able to have per-GHC, Px projects1 (that exclude K if it doesn't build with X). Having per-GHC packages, Kx is unconventional (but actually doable2, if not in the same directory) but that got me thinking is there another option?

  1. Effectively embed multiple package configurations, each a K for X, in the one package by using impl(ghc) at the top-level.

Footnotes

  1. Updo does this and uses a ghc-x.y.z.* naming convention for its ghc-prefixed-targets

  2. I haven't done this! I think it would be possible with hpack-dhall having done something similar varying packaging with sub-library granularity. Compare plugin-for-blobs/package.dhall that puts everything in one package to the other **/package.dhall that create smaller packages. The reused configuration is in *.package-relative.dhall files (dhall functions), such as plugin-for-blobs/uom/quantity/package-relative.dhall, that get imported and called.

@michaelpj
Copy link
Collaborator Author

Projects only work if everything is in one repository. I want this to work even with published packages in a package repository like Hackage.

@philderbeast
Copy link
Collaborator

Projects only work if everything is in one repository.

Projects using source-repository-package can work off-repository, can't they? I wonder if there's a cabal-testsuite/PackageTests test for that?

@alt-romes
Copy link
Collaborator

I agree that this would be useful, and the buildable approach sounds good to me.

In #3072 is suggested an assert: field with which the author also suggests we could assert a ghc version is within some bounds:

assert: impl(ghc >= 7.0 && < 8.0)

Coupled with good error messages printing the assertion, perhaps it would bring the resolution you seek without having to drop into the buildable "low-level" detail.

@michaelpj
Copy link
Collaborator Author

michaelpj commented Jan 24, 2024

I do think a solution that allowed for a user-supplied message would be nice. "Package not buildable in this environment, assertion failed: impl (ghc >= 7.0 && < 8.0)" is better, but "... 'foo does not support GHC 8.0 yet, see $link'" would be even better.

I also think it might be nice to allow this at the top-level somehow. It's usually the case that this stuff is all-or-nothing for a package.

@andreabedini
Copy link
Collaborator

I believe adding a message to buildable: false (or equivalent) is the right way to do it.

If anybody wants to work on this I can advise/mentor.

@gbaz
Copy link
Collaborator

gbaz commented Jan 26, 2024

I agree. I think that we should just start by attaching a message to buildable: false constraints that morally works like "imprecise exceptions" and the message is derived from the surrounding conditional clause (if any) that guards the flag.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants