-
Notifications
You must be signed in to change notification settings - Fork 696
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
[RFC] package-constraints
#9477
Comments
Sounds good to me. A risk is that I think that's probably not too confusing, but it is a potential footgun. |
|
But I'm fine with the original name as well. |
The original is fine with me. The alternative looks ugly (I'm sorry!). |
Thank you for opening a RFC.
We need to be more precise. Each component can list their own dependencies, which are made of a package name, an optional library name and a version range. AFAIK the dependencies of all components are merged toghether when solving for a plan. This includes the dependencies of executable components (at least in the v2 code paths). So if two components specify different ranges for aeson, the solver will only pick a version of aeson that satisfies both version ranges. Tests and benchmarks components are a bit special, because they can be enabled or disabled, either automatically or manually, and when they are disabled their dependencies are not taken into account. Therefore the following does not seem to make much sense (in general).
I agree that listing build-depends for each component, only to have them always merged togheter, is repetitive and confusing. I would not be opposed to a better way to express this. AFAIU these constraints would not imply a dependency but only a version range to intersect with? I am afraid the difference between a build-deps and a package-constraint might be as confusing as separate build-deps per components. I wonder if it could make sense a package level build-deps, it could be simpler for those who don't want to be bothered making a per-component distinction. I think this needs to be understood and discussed more before we can make a decision. |
I just want to repeat a point discussed in the other issue: one of the reasons for doing it the way that is proposed rather than making this a constraint that goes into the solver is that we do feel like we want to state a local constraint. That is, we only want the constraints to apply to the dependencies generated by the local components, and not those elsewhere in the build plan. The case we want to avoid is this:
where (Having written this, I once again am uncertain whether this is true. I think the solver knows about qualified goals and implication goals, so maybe it is possible to add a goal that says "if you need to pick x when trying to solve for y, then here is a constraint on x".)
There is a key difference between this and
Both of these are quite undesirable if you have some components that don't use a dependency. So we really do want to say that this is only an instruction to the solver if the dependency is used, and not an instruction to use it. |
As @andreabedini points out, this isn't possible, only one version of I think it could be a bit confusing how this is quite a syntactic feature and the constraints specified here will operate in quite a different way to if you specify Also, it should be considered how these constraints should interact with qualified contexts:
|
I am not sure I follow what you mean with "the case we want to avoid is" but executable dependencies and library dependencies are merged together (top-level namespace/goal? I am not sure what the correct term is) so
The thing is that we already have those, and they are the version ranges in build-depends :-)
In that phrase I actually meant "add this dependency to all components in the package". I am aware that is not what it is proposed here. Let's start from the beginning. What problem does this want to solve? Less repetition? More clarity w.r.t the required version ranges across components? |
Ah sorry, perhaps I am assuming that #4087 is done. But hopefully it will be done at some point! So we don't want this to work badly with per-component solving.
Sure, but the problem is that
The aim is to improve the common case of "I have many components, and I just want them all to use the same bounds for
What we want is a way to state directly the intention and have cabal do the right thing. The questions we are trying to answer are:
I do agree with this, and it makes me somewhat reluctant to use the "constraint" naming. The other way of looking at it might be: how bad would it be if it worked just like If we did this I'd be tempted to go for a per-component My main qualm would be that I think until now cabal files have been exclusively "local", i.e. they talk only about the direct dependencies of the package. This feature would allow cabal files to instead say things about arbitrary other packages somewhere in the build graph. I don't know if that's bad, but I think it's new. |
This shows our different point of view. My understanding is that #4087 still needs a clear design and I am tempted to say that it might not even be a feature we want because it could introduce more problems than it solves. So, I am not assuming it is going to land anytime soon.
Why not do the simplest reasonable thing?
Note that I intentionally repeated
There are been cases where build plans get broken by a "non-local" effect. E.g. we see many when a package splits in two (network/network-uri, Cabal/Cabal-syntax). But I argue we should not rush to allow this kind of "non-local constraints" (i.e. constraints of non-dependencies) without a detailed analysis. Anyway, this is not what the proposal is about. The proposal makes clear that this new annotation only provides a version range to matching build-depends with no vesion range. My judgment is that it is not worth it. The discussion around what name can better communicate the subtle meaning of the stanza shows that this feature adds complexity and potential confusion. A reasonable use of common stanza like I suggested above obtains the same goal with clarity and minimum repetition. |
Because it looks like a PITA to maintain? Every time I modify any dependency I have to check the other components and do some set intersection in my head. And I'm not sure how to do it systematically for larger packages such as I feel a little like you're saying "no, this isn't actually annoying", which just tells me that you haven't actually tried to maintain a package where this is annoying :p We do already know about common stanzas!
Well, the idea is to solve the problem, and the version with true constraints would also solve the problem, with the benefit of clarity and the downside of excess power. What if we allowed full constraints but made |
@ffaf1, since you maintain |
The way it is implemented right now follows the "if this component is not selected, do not apply this bound". So as an example:
will result in |
@andreabedini The main benefit of this change is to not have to manually topologically sort your components (to apply the bound in the bottom-most component), or have to make combinatorial common stanzas, or put everything in common stanzas, but instead provide bounds that will be applied always. I agree the name @mpickering I did not check what happens with setup dependencies. I'm passing this when converting the cond tree to a solver list of dependencies. I think I could make it apply to those other contexts too if wanted. However |
Alright, that's a fair rebuttal
I think this is good alternative, and thinking about it, it is surprising we don't do it already. Having It is not immediate how to do this without closing the door to a possible future with per-component solving. Maybe we can still consider a package description with different bounds to common dependencies as valid but cabal can emit a notice at build time? e.g.
TBH, I think this is a clear UX improvement (beside the exact formulation of the message of course). I think this would need to be done by both Cabal can cabal-install actually. |
I would propose something like:
so then you can do
i.e. we do things at the component level and use common stanzas to get it across components.
That seems sensible to me. This has the advantage that we can relax those descriptions if/when we get per-component solving. |
On Dec 7th in the Cabal devs call, we reached an agreement (which we can still iterate on). Do correct me if you think I misunderstood any part of the conclusion, please. It probably deserves a separate RFC as this one would be rejected.
|
I wanted to mention an idea for making the use of default bounds clearer, though it is probably too ugly and verbose in the common use case. We could add a new syntax, such as
|
When we discussed this I made the assertion that sometimes it's useful to constrain an indirect dependency. I tried to write down my examples here, but I think they are in fact not convincing. |
Obsoleted by #9569 |
Problem this solves
When describing a package in a
.cabal
file, multiple components can declare the same dependency in theirbuild-depends
sections. There can be two flavors of this:aeson
in one test-suite and a different version in another, in order to test backwards compatibility always.It is to case (2) that we turn our attention in this RFC. On the one hand it feels redundant and error-prone to repeat the version bound in all components. On the other hand, in order to make sure you are building with the same version regardless of what subset of those components is enabled one has to perform a topological-sort of the components and assign the version bound in the bottom-most component.
A different solution is to use
common
stanzas as done by some packages but this leads to a weird syntax, where theimport
section of a component is importing both configurations andbuild-depends
sections.Proposed solution
Implement a new section in the
.cabal
package file namedpackage-constraints
which contains a list of dependencies in the same format as abuild-depends
.For each of the dependencies in the
package-constraints
, whenever a dependency on the same package appears in abuild-depends
section of a component, the version declared in the component will be intersected with the version frompackage-constraints
. In particular, if thebuild-depends
of the component declares no version bound, it will inherit the one frompackage-constraints
.For specific cases like (1) above, the user should not include such a special dependency in these
package-constraints
section.This will be just a syntactic sugar and will have no influence in:
constraints
incabal.project
)build-tool-depends
pkgconfig-depends
Backwards compatibility
This change would be fully backwards-compatible as there existed no field with this name before and omitting it would work the same way as before this change, i.e. it is an optional field.
The text was updated successfully, but these errors were encountered: