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

Show constraint sources in dependency solver errors #10524

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

9999years
Copy link
Collaborator

@9999years 9999years commented Nov 4, 2024

Before:

[__0] rejecting: memory-0.18.0 (constraint from user target requires ==0.17.0)

After:

[__0] rejecting: memory-0.18.0
  (constraint from cabal.project requires ==0.17.0)

First shot at #10519. Seems unavoidably large.

Builds off of #9578.

Future work: Propagate line numbers into these constraints!

Implementation strategy:

  • Propagate provenance (ProjectConfigPath/ConstraintSource) into project config types (Distribution.Client.ProjectConfig, D.C.ProjectConfig.Legacy)

  • Extract and attach that information in D.C.ProjectConfig.findProjectPackages

  • Attach that information to ProjectPackageLocation (returned from findProjectPackages). Note: Might be worth unifying that with PackageLocation, would make the new type PackageLocationProvenance.

  • Use that information to write better ConstraintSources into the solver; this is mostly implemented.

Overall it seems like all the information exists, but it takes a lot of work to propagate it into the right parts of the system.

@9999years 9999years force-pushed the dependency-provenance branch 2 times, most recently from 4811a3c to 0c8471d Compare November 4, 2024 22:29
9999years added a commit to MercuryTechnologies/cabal that referenced this pull request Nov 4, 2024
We have a lot of `showType` functions that are effectively a `Pretty`
instance but less composable. Let's make them proper `Pretty` instances.

Split off of haskell#10524
@9999years 9999years force-pushed the dependency-provenance branch 9 times, most recently from 9ebbba8 to 522bec9 Compare November 9, 2024 00:25
@ulysses4ever
Copy link
Collaborator

@jasagredo just a heads-up: on the last Cabal meeting @9999years asked to not comment on their PRs while a PR is in the draft mode. I remember it because I’m guilty in it more than many!

@9999years 9999years force-pushed the dependency-provenance branch from 522bec9 to e52d016 Compare November 18, 2024 19:59
@9999years 9999years changed the title [WIP] Show constraint sources in dependency solver errors Show constraint sources in dependency solver errors Nov 18, 2024
@9999years 9999years marked this pull request as ready for review November 18, 2024 19:59
@9999years 9999years force-pushed the dependency-provenance branch 2 times, most recently from c10bd7d to 9854c9d Compare November 18, 2024 20:37
@9999years
Copy link
Collaborator Author

Needs a release note.

@9999years 9999years force-pushed the dependency-provenance branch 2 times, most recently from 9492b41 to b27b413 Compare November 19, 2024 00:45
@9999years
Copy link
Collaborator Author

Added a release note and tests, addressed comments. Should be ready to go!

Copy link
Collaborator

@geekosaur geekosaur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a pity 3.14.1.0 is breathing down our necks; it would be nice to see this go in, seems like it'd be a big UX improvement.

cabal-install/cabal-install.cabal Show resolved Hide resolved
@ulysses4ever ulysses4ever requested a review from grayjay November 20, 2024 02:42
Copy link
Collaborator

@grayjay grayjay left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started reviewing this PR, but I had a question. It looks like a lot of the changes relate to adding ConstraintSources to targets in v2 commands. Why do the build targets need ConstraintSources? I had thought that targets for most commands only specified the subset of the project to build and didn't affect solving. The only exception I can think of is v2-install, which can solve for packages outside of the project (e.g., "cabal install text-2.0"), but then I would expect all targets to be able to use the same ConstraintSource (ConstraintSourceUserTarget), which I think is the current behavior on master.

=<< readTargetSelectors
(localPackages baseCtx)
(Just BenchKind)
(map (\target -> WithConstraintSource{constraintInner = target, constraintSource = ConstraintSourceCommandlineFlag}) targetStrings)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does this line use ConstraintSourceCommandlineFlag as the ConstraintSource? If I understand correctly, targetStrings is a list of targets provided to the bench command, so ConstraintSourceUserTarget seems like a better match. This comment also applies to many of the other commands.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll change it if you want. I think it's really unclear which is supposed to be which, and I have no clue what Cabal means when it tells me a constraint comes from a 'user target'. What is ConstraintSourceCommandlineFlag for, if not command line arguments?

( WithConstraintSource
{ constraintInner =
NamedPackage (C.mkPackageName p) []
, constraintSource = ConstraintSourceUnknown
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be ConstraintSourceUserTarget?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the targets parameter should be replaced with one that contains constraint source information. Right now, the constraint source is unknown because that information is missing. We don't know what this function is getting called with or where the targets come from.

Comment on lines +3 to +7
-- This is `my-lib` 0.9.
source-repository-package
type: git
location: https://github.com/9999years/cabal-testsuite-my-lib.git
tag: 9a0af0aa81325c71e744def11db06265840ffb5f
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this test use an existing package in the Haskell organization as in the postCheckoutCommand test?

source-repository-package
type: git
-- A Sample repo to test post-checkout-command
location: https://github.com/haskell/bytestring
post-checkout-command: true
tag: 0.10.9.0

Copy link
Collaborator Author

@9999years 9999years Dec 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted a smaller repository so the tests would run faster; my repository is 136k vs bytestring is about 15m.

I also need a package that depends on a conflicting version of a library in the repo.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grep for location: in this repository and you'll see we're already cloning tons of non-Haskell org repos in the tests.

Could not resolve dependencies:
[__0] next goal: my-lib (user goal)
[__0] rejecting: my-lib; 2.0, 1.0
(constraint from cabal.project requires ==0.9)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similarly to the message in use-local-version-of-package.out, I think that this message would be clearer if it explained that the constraint came from the version of a package listed in cabal.project. It could also help to mention that it is a source repository package.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

cabal-install/src/Distribution/Client/Setup.hs Outdated Show resolved Hide resolved
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to also change the content of this test?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, @jasagredo asked me to rename it #10524 (comment)

@9999years 9999years force-pushed the dependency-provenance branch 2 times, most recently from e180d68 to 2844e3e Compare December 6, 2024 23:03
9999years added a commit to MercuryTechnologies/cabal that referenced this pull request Dec 10, 2024
We have a lot of `showType` functions that are effectively a `Pretty`
instance but less composable. Let's make them proper `Pretty` instances.

Split off of haskell#10524
9999years added a commit to MercuryTechnologies/cabal that referenced this pull request Dec 10, 2024
We have a lot of `showType` functions that are effectively a `Pretty`
instance but less composable. Let's make them proper `Pretty` instances.

Split off of haskell#10524
9999years added a commit to MercuryTechnologies/cabal that referenced this pull request Dec 13, 2024
We have a lot of `showType` functions that are effectively a `Pretty`
instance but less composable. Let's make them proper `Pretty` instances.

Split off of haskell#10524
9999years added a commit to MercuryTechnologies/cabal that referenced this pull request Dec 18, 2024
We have a lot of `showType` functions that are effectively a `Pretty`
instance but less composable. Let's make them proper `Pretty` instances.

Split off of haskell#10524
@9999years 9999years force-pushed the dependency-provenance branch from 2844e3e to 524ba6d Compare December 20, 2024 19:17
@9999years
Copy link
Collaborator Author

@grayjay I fixed a semantic conflict with #10546, mind re-reviewing?

@grayjay
Copy link
Collaborator

grayjay commented Dec 28, 2024

@9999years I still have a question about the overall change from my last review: #10524 (review)

Before:

    [__0] rejecting: memory-0.18.0 (constraint from user target requires ==0.17.0)

After:

    [__0] rejecting: memory-0.18.0
      (constraint from cabal.project requires ==0.17.0)
From @grayjay:
> The dependency solver currently doesn't describe preferences in error
> messages, so I think that it would take a lot more work to make this
> useful.
...provided that nothing else changes.
@9999years 9999years force-pushed the dependency-provenance branch from 611d65c to b84c092 Compare January 3, 2025 18:22
@9999years
Copy link
Collaborator Author

Why do the build targets need ConstraintSources?

@grayjay I'm looking at this now and the issue is that build targets (as Strings or TargetSelectors) are turned into instances of PackageSpecifier UnresolvedSourcePackage basically immediately, and those are used all over and can include things like source-repository-packages, so we definitely want constraint sources for those... we can mark them as unknown constraint sources but that doesn't lead to great UX, so it seems like it's easiest to just add ConstraintSourceCommandlineFlag or ConstraintSourceImplicitTarget to the target arguments as early as possible.

@grayjay
Copy link
Collaborator

grayjay commented Jan 10, 2025

@9999years I realized that I forgot to clarify the type of target that I was talking about in the comment above and at the Cabal meeting. It seems like Cabal uses "target" in multiple ways due to the change from v1 to v2 commands:

  1. solver targets: The dependency solver takes a set of package names as an argument and solves for those packages and their dependencies.
  2. v1 command build targets: v1 build targets had a relatively simple syntax. Those targets were passed to the dependency solver with very little modification.
  3. v2 command build targets: v2 build targets have a syntax that is much more complex and expressive than v1 targets. For example, they can specify all packages, a component type, or a file. If I understand correctly, for most commands, the build targets only specify the subset of the project to build and are not passed to the dependency solver. However, cabal still needs to pass targets to the solver. I think that those targets are derived from the structure of the project, such as local packages or source repository packages listed in cabal.project. They usually shouldn't change between build commands.

I would expect the solver targets to be associated with constraints, because cabal needs to constrain the versions to match the versions of local packages, when present. I wouldn't expect the v2 targets to be used for any constraints, since they don't affect solving (except for v2 install).

I can see why PackageSpecifiers might need to be associated with ConstraintSources in some cases, but I was wondering why this PR adds ConstraintSources to v2 build targets. Here are two specific examples:

EDIT: I think that the change from v1 to v2 commands also explains why the messages about constraint sources in the solver currently aren't very clear. The things that they describe, such as "user targets", became more internal with v2 commands.

@9999years
Copy link
Collaborator Author

9999years commented Feb 10, 2025

@grayjay I'm not sure how to explain this. I've done my best to explain the changes to TargetSelector in #10524 (comment). I think it will make more sense if you attempt to make the change you desire yourself (removing the constraint annotations from TargetSelectors) and the compiler will show you the places where those annotations are needed. I found myself left with a choice:

  • At the last moment possible, you can add an annotation to the selectors, e.g. ConstraintSourceUnknown (bad UX, low confidence that it's actually the correct thing to apply to unannotated strings)
  • Alternatively, you can add the annotations as early as possible, e.g. immediately after the command receives the arguments you can annotate them with ConstraintSourceCommandlineFlag. Then, you propagate those constraints to the points where they're actually used.

I've investigated making the changes here more minimal but I found myself increasingly convinced this only reduces the quality of the patch and leaves more work for someone to do in the future.

@ulysses4ever
Copy link
Collaborator

I'm thinking maybe we should bite the bullet and merge it, and be ready to revert if something goes horribly wrong for the users after the next release. I just really don't like the impasse I'm observing :-) But then again, I have minimal expertise.

@9999years
Copy link
Collaborator Author

Yeah, I wish it was easier to make this change in a less invasive fashion :/

@grayjay
Copy link
Collaborator

grayjay commented Feb 13, 2025

I'm not worried about the correctness of the code, but rather the technical debt. I'm concerned that this PR is very large (+1779, -1009, ignoring tests) and adds a lot of code that appears unnecessary and untested (adding constraint source information to the build targets of v2 commands, other than install).

I spent more time reading the code, and my best guess as to why this change touched so many lines is the change to PackageSpecifier. That type is used by the code that finds all packages in a project as well as the code that reads v2 build targets. Moving the ConstraintSource out of TargetSelector and/or related types and only associating a ConstraintSource with packages where needed could help reduce the amount of code.

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

Successfully merging this pull request may close these issues.

6 participants