-
Notifications
You must be signed in to change notification settings - Fork 697
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
Make the cabal-install solver multilibs-aware #6039
Comments
I think this would be pretty straightforward, at least for source packages, because it could be implemented similarly to enforcing build tool dependencies. The visibility field could probably be implemented similarly to the current handling of unbuildable components. I have a few questions about how visibility should behave, though.
There may be an issue with enforcing visibility for installed packages, because there is a hack where the solver treats installed internal libraries as separate packages with munged names ( cabal/cabal-install/Distribution/Solver/Modular/IndexConversion.hs Lines 106 to 131 in 5f57d4d
|
Yes. In #5848 we're talking about disabling it at the syntax level, but internally it's still used
Yes and no, but I'll have to check the second one. |
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
This commit tracks dependencies on sub-libraries by extending the functionality for tracking executables that was added in e86f838. It also starts adding support for library visibility, though it currently only works for source packages. There is a TODO for handling installed packages. This commit handles visibility similarly to the way that the buildable field is handled currently. It only checks whether a component is made private by the current environment and flag constraints at the start of dependency solving. This means that the solver can treat a component as visible when the visibility is controlled by an automatic flag, and the build can fail later, depending on the value that is chosen for the flag. Fixes haskell#6038.
I wonder if a volunteer could easily help with the investigation. How would one inspect the "solver's view of the installed package index"? Run with |
The most direct way to see how the solver handles multiple installed instances is to look up the package by name in the Index returned by convPIs and print all returned instances. I think that -v3 would also work, but the test case would need to cause the solver to reject all versions of the installed package (e.g., with an unsatisfiable version constraint) in order to make the solver print them all. I noticed that cabal has a --shadow-installed-packages flag, so the behavior may actually be configurable. If the index returned by convPIs contains both instances of the package in the test, then I think it would also be useful to see whether it is possible to depend on either one of them. |
Regarding package shadowing:
|
I don't know of any unreported progress. There is certainly reported interest in related things on #hackage, in https://discourse.haskell.org/t/new-hackage-server-features/2621/42, in haskell/hackage-server#1119. |
@andreabedini Thanks for volunteering to work on this issue! I don't know of any additional progress, but I can give my current thoughts about test cases and the desired behavior. I mentioned two test cases in #6039 (comment). For the first case, I'm assuming that regardless of the value of the flag For the second test case in #6039 (comment), I wanted to give a more concrete example:
Alternatively, the test case could avoid modifying source code by giving package A-1.0 a build flag that makes one sublibrary unbuildable when it is true and the other unbuildable when it is false and then installing both configurations. |
Here some notes from today. Some of this might be well-know but it wasn't for me :) First question:
Multiple instances do not seem to be a problem. Interlude:
Indeed sublibraries are installed in the packagedb with one entry per component, with a mangled name. E.g.
where index-envs is a private sub-library of plutus-core package and the main library depends on the sublibrary (via its unit-id). The two package-db entries look like this:
(Note that the sub-library has From the solver point of view, all entries in ghc-pkg are separate instances. The index (result of
Second question:
Despite the clear instructions this test turned out a bit trickier for me. I made a package with two sub-libraries and installed them in the user packagedb (I had to use That said. Since each instance contains only one component, it looks meaningless to say "instance of A-1.0 that contains both components". Plan for tomorrow: Looking at the solver index it looks like it's not demangling the component names. IIRC The original problem I am running into is that the solver does not seem to be able to re-use public sublibraries in the packagedb, and ends up recompiling anything that depends on them. This is the behaviour I described in input-output-hk/haskell.nix#1662 (comment). Tomorrow I'll investigate how the solver sees the situation. IIRC the solver was complaining it could not use the installed package because some component was missing. Other thing to double check :) |
Thanks for investigating. I didn't realize that sublibrary names were still mangled in the package db.
I'm not sure I understand this part, so I wanted to clarify that the definition of "instance" that I was using is a single installation of a given package and version. Instances can differ by having different build flags, versions of dependencies, etc. If the package has more than one library, then the instance will be split across multiple entries in the package db. A major part of the remaining work for this issue is allowing the solver to group installed libraries into instances. The reason that package D depends on both sublibrary B and sublibrary C in the test case is that I wanted to ensure that cabal doesn't incorrectly group the two installed sublibraries from Package A-1.0 together as one instance when they are actually from two different instances.
If I understand correctly, this is the problem I described in #6039 (comment). The solver doesn't currently know how to satisfy dependencies from source packages to installed sublibraries. I think that the best way to solve this issue is to represent each installed package as a single |
Thank you for clarifying. I was indeed a bit confused about the meaning of "instance" in this discussion but I ended up adopting the one used in the solver (which identifies instances with packagedb entries). Now I understand you are using the term in a more general way and the difference is exactly what we need to teach to the solver. This note is also relevant. cabal/cabal-install-solver/src/Distribution/Solver/Modular/IndexConversion.hs Lines 111 to 143 in 3674900
|
Thinking about this, are we sure this is a problem? two installed instances of Package A-1.0 will have all their dependencies fixed. Even if they end up being split into multiple libraries in the packagedb, their dependencies will either match or they won't. |
The solver was originally designed without sublibraries, so it expected there to be one library per installed package. Then that hack was added for internal (private) libraries. It worked because it isn't possible for a source package to depend on an installed library that is private. Now I think we should change the solver's installed package index to act as a map of packages, each with one or more libraries.
I don't think that we should mix libraries from different installations of a package, since they could differ in more ways than dependencies, such as cabal build flags, compiler flags, or even source code. In my opinion, it would be similar to mixing libraries from different versions of the package. I also think that recording the instance of an installed library (with the InstanceUnitId idea from above) would simplify forcing the dependencies to match. |
A large question around this seems to be: "what does compatible" mean? From a dependency perspective, any "consumer" of a package find that package compatible (with any other instance of that package) as long as the exposed API (symbols, and relevant signatures) match; anything else is a blackbox to the consumer. This ignore the semantic part where the symbols and signatures could stay the same, but the behaviour/meaning is different. That's what we usually have the PVP for? |
@angerman Is this comment for a different issue? This issue doesn't relate to GHC ways, except that they may both relate to InstalledPackageInfo. |
@grayjay possible. @andreabedini had been flooding me with issues 🙈 |
This is the right issue, but maybe things got a bit confused. Let me try to remember and summarise the discussion. The solver being aware of multiple libraries means that the it will have to decide what to do in the situation where
Under what conditions the preinstalled package can be re-used? The current answer is never, thanks to the name mangling introduced with private sublibs, the solver does not even see the preinstalled The precise information about how The solver already knows that pre-installed packages have their dependencies fixed. So the preinstalled Flags is something we could pack into the unit-id (and maybe we do already, Cabal and cabal-install use two different schemas and I always forget what goes into Cabal's one). @grayjay suggests other things can go wrong:
Am I wrong understanding that in the above quote you imply that current behaviour is the only safe one? That is, we should never re-use a preinstalled sibiling library? I belive this would be too conservative. |
@angerman Now I understand. GHC ways is just an example of how installed packages can vary by installation. I think that we need to capture everything that you mentioned in the field that we use to identify an instance. @andreabedini I actually hadn't considered the case that you described, where a package is only partially installed (only some of its sublibraries are installed). Do you think that that is likely to be a common case? I think that cabal should be able to handle it, but it seems like an edge case to me, like handling a broken installation. I also realized that I don't know exactly what information goes into the unit ID. The InstanceUnitId design above relies on the unit ID of the main library capturing everything about how the whole package was built, including information about the other components. If the unit ID of the main library doesn't change when other components change, then maybe the unit ID isn't the best way to identify an instance. The InstanceUnitId design could allow cabal to use two components of a package that were installed at different times, if the components were built in a compatible way (having the same unit ID for the main library). The safest design would be for cabal to create a random unique ID for the instance whenever it installs a package, but I don't know if that is practical. |
From my POV, this whole issue was always about this but now I see the distinction you make.
Yes, I was wrong indeed. If we make public sublibraries visible to the solver and use a random (but common) unit id like you say, Edit: no, the solver does not look at any id when it comes to pre-installed packages. It only matches on package name and version. Perhaps the first step is to make the solver aware of the configuration of pre-installed packages. |
The Cabal part is done, but cabal-install's solver is not aware of the multiple public libraries feature yet, leading to late errors and issues like #6038
The text was updated successfully, but these errors were encountered: