summaryrefslogtreecommitdiff
path: root/apt-pkg/solver3.cc
Commit message (Collapse)AuthorAgeFilesLines
* solver3: Do not lose notion that we backtrackedJulian Andres Klode2025-10-251-1/+5
| | | | | | | | | Record a silly string if we backtracked such that we are aware of it. We need to handle this in a broader fashion, but this was at least breaking test-apt-never-markauto-sections with remove-manual LP: #2129819
* solver3: Eagerly satisfy previously satisfied RecommendsJulian Andres Klode2025-10-251-2/+5
| | | | | | | | | | | | | | | | | | When a user ran `apt install gpg`, the solver decided to remove `gnupg` and `seahorse` because `seahorse` was only pulled in as a Recommends of ubuntu-desktop and Recommends were resolved after all other dependencies. Solve this to most extent by introducing eager optionality: These dependencies, while they do not take part in classic unit propagation, are otherwise treated like hard dependencies and resolved as soon as possible rather than after any hard dependencies. This ensures that the Recommends of ubuntu-desktop on seahorse is retained correctly, and as a result, gnupg is updated to the latest version. Oops: 6c8e32eb-665d-11f0-a985-fa163ec8ca8c
* solver3: Allow any downloadable version to mark a package as not obsoleteJulian Andres Klode2025-08-261-3/+8
| | | | | | | | | | | | Lots of people have pinned an older version that is no longer installable, or more common yesterday, we saw a lot of people who seemingly manually installed a newer version of a deb than in the repositories, causing the package to be considered obsolete, which does not make much sense. Oops: 4c39d922-410f-11f0-bbf9-fa163ec44ecd Oops: a64054f6-4140-11f0-bbfd-fa163ec44ecd Oops: a32196c1-661d-11f0-a97c-fa163ec8ca8c
* solver3: Sort dependency targets against current alternativeJulian Andres Klode2025-08-261-4/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | Instead of inadvertently sorting dependency targets against the first alternative in an or group, sort them against the current alternative as we should. Otherwise we ended up sorting gnome-shell | notification-daemon With Package: notification-daemon Package: awesome Provides: notification-daemon into gnome-shell | awesome | notification-daemon <-we sorted this part wrongly-> This fixes a minor difference in test-release-candidate-switching in one case, but not the other where the difference was actually caused by the loss of install argument ordering information in the new solver, so just flip the arguments so both solvers produce the same outcome.
* solver3: Filter installed RecommendsJulian Andres Klode2025-06-101-0/+10
| | | | | | When inspecting Recommends of installed packages, filter them to the installed packages only, as we want to avoid switching between alternatives of recommended packages.
* solver3: Explicitly declare and define ~SolverJulian Andres Klode2025-06-101-0/+2
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | On recent armfhf and s390x builds in the PPA, the compiler seems to have generated the destructor at the wrong point where the definitions where not yet complete, and it does seem ill-advised to rely on a default-constructed destructor in the solver for future ABI sake. In file included from /usr/include/c++/14/vector:66, from /<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/include/apt-pkg/cachefilter.h:14: /usr/include/c++/14/bits/stl_vector.h: In instantiation of ‘constexpr std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp = APT::Solver::Work; _Alloc = std::allocator<APT::Solver::Work>]’: /usr/include/c++/14/bits/stl_vector.h:531:7: required from here 531 | vector() = default; | ^~~~~~ /usr/include/c++/14/bits/stl_vector.h:369:49: error: invalid use of incomplete type ‘struct APT::Solver::Work’ 369 | _M_impl._M_end_of_storage - _M_impl._M_start); | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ In file included from /<<PKGBUILDDIR>>/apt-pkg/edsp.cc:22: /<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/include/apt-pkg/solver3.h:87:11: note: forward declaration of ‘struct APT::Solver::Work’ 87 | struct Work; | ^~~~ /usr/include/c++/14/bits/stl_vector.h: In instantiation of ‘constexpr std::_Vector_base<_Tp, _Alloc>::~_Vector_base() [with _Tp = APT::Solver::Solved; _Alloc = std::allocator<APT::Solver::Solved>]’: /usr/include/c++/14/bits/stl_vector.h:531:7: required from here 531 | vector() = default; | ^~~~~~ /usr/include/c++/14/bits/stl_vector.h:369:49: error: invalid use of incomplete type ‘struct APT::Solver::Solved’ 369 | _M_impl._M_end_of_storage - _M_impl._M_start); | ~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~ /<<PKGBUILDDIR>>/obj-arm-linux-gnueabihf/include/apt-pkg/solver3.h:88:11: note: forward declaration of ‘struct APT::Solver::Solved’ 88 | struct Solved; | ^~~~~~
* solver3: Ignore Architecture: all for obsoleted-byJulian Andres Klode2025-06-101-1/+3
| | | | | | | | | | | | In case a new version of the source package is published, we check that if there's a newer binary for the same architecture and then consider the binary obsoleted. This logic did not properly account for Architecture: all packages which are considered as native architecture package with an "All" multi-arch flag set, and hence a native architecture package may inadvertently be considered obsoleted by a package that only built on Architecture: all.
* solver3: Only merge dependencies on the same packageJulian Andres Klode2025-05-281-6/+14
| | | | | Avoid or groups and dependencies on different (virtual) packages, to avoid some common pitfalls like the added xorg test case.
* solver3: Merge Depends into RecommendsJulian Andres Klode2025-05-271-7/+23
| | | | | | | | | | | | | | | | | | | | | | | Merge any hard clauses into optional clauses, such that optional clauses don't end up with more choices. For example if you have Depends: a | b Recommends: a | c This becomes: Depends: a | b Recommends: a We have simulated this with the chaos-actor in the test case and a Depends: git (not satisfied by chaos provider) Recommends: git (satisfied by chaos provider) and the latter constraint is limited to the former.
* solver3: Merge intersecting dependenciesJulian Andres Klode2025-05-271-5/+45
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | If a package declares multiple dependencies that can be solved by the same packages we should use the common set of packages to solve them. A common example is requiring the same Debian source version, or the same upstream version as in our test case: git-ng Depends: git (>> 1:2.26.2), git (<< 1:2.26.2-.) The solver expands this to the concrete objects: git-ng Depends: "real git" (= 1:2.26.2-1) | chaos-actor, "real git" (= 1:2.26.2-1) | "real git" (= 1:2.25.1-1) When given an upgrade request, the solver would now choose chaos-actor to satisfy git (>> 1:2.26.2) "real git" (= 1:2.25.1-1) to satisfy git (<< 1:2.26.2-.) To satisfy the two constraints, which is not the intended outcome. Address this problem by introducing a concept of merged clauses: If two dependencies of a package have overlapping solutions, replace the dependency by the intersection, and record the merged clause instead, this leads to a single clause: Depends: git (>> 1:2.26.2) and git (<< 1:2.26.2-.) which expands to just the real git binary. The implementation is a bit finicky in that it removes the variables from the original clause which may not be helpful for debugging, but it records the clauses merged with, as seen in the test case, so the reasoning is clear. LP: #2111792
* solver3: Assume manual packagesJulian Andres Klode2025-05-261-1/+16
| | | | | | | | | | | | | | | | | | | | If we have allowed the removal of manual packages, assume them all before starting the solver. This should ensure that as long as there is a solution that does not remove a manually installed package, it is found. This requires a sweeping set of changes in the test suite, but ensures that we get "safe" behavior from the solver. We have in particular seen that without asserting the installed packages, several people ended up with ubuntu-minimal and perl removed in a situation where that was not warranted, that is, they install winehq, and then pull in some new perl packages in a newer version than the installed one, and the solver chose to create a mismatched version set, which then caused the main perl package to not be installable. Oops: 1b55173a-3526-11f0-b7ac-fa163e171f02 Oops: dbd5149e-36b9-11f0-bb74-fa163ec44ecd
* solver3: Initialize startTime if not set in Pop()Julian Andres Klode2025-05-261-0/+2
| | | | | | | | If the user pushes Assumptions that fail, we could inadvertently timeout during the Pop() as the variable was not initialized. Always initialize it to 0, and if we haven't set an actual time by the time we Pop() set it there.
* solver3: Allow CompareProviders3 with empty packageJulian Andres Klode2025-05-261-1/+1
| | | | | | This can be used to compare arbitrary packages in non-dependency contexts. It remains to be seen whether this is a meaningful approach.
* solver3: If no choice was made nothing to invertJulian Andres Klode2025-05-261-1/+1
| | | | | | | | | We sometimes enqueue no choice as the choice, specifically when an optional clause became unsat as we need to reenqueue it on undo. But of course, this breaks undoing stuff because now it tries to insert !root and then solver breaks.
* solver3: Handle failed assumption in Pop()Julian Andres Klode2025-05-261-0/+7
| | | | | | | | | | | If an Assume() failed, we will have pushed the choices stack, but not actually pushed anything to the solved stack. This would cause a segmentation fault trying to read the referenced entry that doesn't actually exists, namely solved[solved.size()]. Handle this by exiting early as there's nothing to do except pop the entry back out of the array.
* solver3: Remove Work::choiceJulian Andres Klode2025-05-261-5/+4
| | | | | | | | | | | | Instead of having the historic choice here, follow MiniSAT and use the assigned variable from the tail. This should also make the Assume() function work now, albeit we still need to actually migrate to it. This is a first step towards refactoring the Solve() loop to use a propagate/find next literal/assume it kind of loop, albeit there is a bit more to prepare there as we need to also reinsert work items when backtracking.
* Introduce apt why, apt why-notJulian Andres Klode2025-05-191-0/+20
| | | | | | | | | | | These are implemented somewhat differently from aptitudes why and why-not commands: They produce the actual solver trace for why a particular decision has been taken. For the why-not case, we need to explicitly discover our specified package, as if nothing else depends on it in our graph, it would otherwise always be undiscovered and conflicts not detected (see e.g. level-3 in the test).
* solver3: Do not install new packages for SuggestsJulian Andres Klode2025-03-201-0/+5
| | | | | | | | | | As a followup to the previous commit, restore the exact behavior of the old solver to break the Suggests on upgrade here by erasing any choices not previously installed. This ensures that the Suggests never installs any new package, but it will still retain previously installed packages when solver3 is doing autoremove.
* solver3: Handle previously satisfied Suggests lastJulian Andres Klode2025-03-201-0/+1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | In #2103556 we can see that avahi-daemon Suggests avahi-autoipd, which is previously satisfied by dhcpcd-base due to a Provides; that Provides has now been dropped; and solver3 keeps the update back. As in LP: #2102720 this is a Suggests and it seems wrong for the Suggests to actually influence dependency choices and be able to hold back updates. Instead, satisfy previously satisfied Suggests at the end. For the particular case in this bug report, this results in avahi-autoipd, the real package, to be installed to keep the Suggests working which needs further consideration whether that is the right choice (we can argue either way), but in any case this is a better solution. This in turn breaks the conkeror test which is a somewhat awkward unrealistic test these days as it has no automatically installed packages; because previously something must have had a Suggests on say libdatrie1 or something that was processed first. This would be fine before 5daf6dbfd272be2f8e3c59d4bab4be8c119b7aa1 but as of that commit we no longer rewrite conkeror Depends xulrunner-1.9 | xulrunner-1.9.1 into conkeror Depends xulrunner-1.9.1 | xulrunner-1.9 Because xulrunner-1.9 is manually installed. Mark xulrunner-1.9 as automatically installed to fix the test case. The resulting behavior seems correct: If you manually want xulrunner-1.9 specifically we should not replace it with xulrunner-1.9.1; but if it's automatically installed we should. LP: #2103556
* solver3: Record manual flag before discovering packagesJulian Andres Klode2025-03-201-2/+5
| | | | | | | | | | | | | | | | When discovering packages we look at the manual flag to avoid reordering obsolete manually installed packages last. We inadvertently initialized that flag interlaced with discovering packages so if we discovered packages depending on obsolete, but manually installed, packages first, we were not aware they were manually installed and demoted them. I am struggling to come up with a test case for this as I can't get the package objects in the cache to be in the right order for the bug to trigger. LP: #2102720
* solver3: Only hide ver->pkg clauses, not pkg->ver version selection onesJulian Andres Klode2025-03-131-4/+14
| | | | | | | | Particularly as Helmut pointed out we need to show which version we selected for install if there is only one but there is a versioned conflicts. Reported-By: Helmut Grohne <helmut@subdivi.de> on IRC
* solver3: Fix test-resolve-by-keep-new-recommendJulian Andres Klode2025-03-121-20/+22
| | | | | | | | | | | | | | | | | Reorder the if branches a bit to simplify the understanding of the promotions, and allow us to keep track of which dependency existed in the installed version. Change the rule for promoting new recommends to only promote new recommends to depends in *upgrade* and not *dist-upgrade* per the test-resolve-by-keep-new-recommends test case. This makes some sense: In a dist-upgrade, the solver could otherwise decide to remove an installed package if both the following hold: (1) something needs the upgraded version | something else (2) the upgraded version is not installable due to unsat recommends
* solver3: Fix Recommends/Suggests vs Enhances confusionJulian Andres Klode2025-03-121-1/+9
| | | | | | | | | | | | | | | | | | We accidentally considered an Enhances a reason to keep a package installed, which of course it is not, fix the determination of "existing soft dependency" to only include soft dependencies that should keep a package installed in the autoremover to solve the issue. This also fixes edsp/mantic-upgrade-rel-to-2024-05-29.edsp to not install llvm-13-dev for clang-13 which is an installed package with no upgrade. Also ensure we stop after the first match; the DependsList() is ordered by decreasing priority and we don't want to override Recommends by Suggests in case a package declares both... LP: #2101800
* solver3: Correctly determine 'same' or groupsJulian Andres Klode2025-03-101-9/+29
| | | | | | We incorrectly used the DependencyData of the first or group member, however that only checked that the first or group member was the same and that both either had a next member or not.
* solver3: Print correct reason when registering dependencyJulian Andres Klode2025-03-101-2/+1
| | | | This is relevant when debugging common dependencies.
* solver3: Fix detection of satisfied recommends for pkg variablesJulian Andres Klode2025-03-101-1/+1
| | | | This only worked for version literals lol
* solver3: Do not prefer new installs over manually installed obsoletesJulian Andres Klode2025-03-091-2/+5
| | | | | | | | | | Only move obsolete packages last that are automatically installed, as we don't want to remove manually installed obsolete packages. Add some missing const annotations to the operator[] arguments as well that were needed. LP: #2100247
* solver3: Verbose error messagesJulian Andres Klode2025-03-081-21/+186
| | | | | | | | | | | | | | | | | | | | | | Introduce a new function, LongWhyStr() that returns a longer reason for why something is being installed (or not). This does the same path walk as the other function does, but it renders the clauses at each level, and one per line, so the whole output is a lot more informative. It is a separate function to keep the existing debug messages use the simple single line implication graph We remove the other special case in AddWork() for empty solutions to mke use of the general case in Solve() instead, and then adapt the case in Solve() to the same case as in Enqueue(). This also happens to fix the bug that when we encountered an empty clause we just printed the clause had no solution, but not how we got to install the package with the clause. Adapt the test suite for the changes which is an annoying amount of paperwork.
* solver3: bestReason: Only find actual assignmentsJulian Andres Klode2025-03-071-1/+5
| | | | | | | | | | I marked exim4-daemon-light for install, which marked exim4-daemon-heavy for not install, but now in postfix Conflicts: mail-transport-agent we saw "not exim4-daemon-heavy" as the reason for not installing postfix. Oh noes.
* solver3: Support pretty printing clausesJulian Andres Klode2025-03-061-5/+16
| | | | | This will print the underlying dependency which is nicer to read.
* solver3: refactor: Return inserted Clause in AddWork()Julian Andres Klode2025-03-061-7/+8
| | | | | This avoids relying on the inserted clause being at the back of the clauses vector.
* refactor obsolete checks to use VerFileIterator::Downloadable()Julian Andres Klode2025-03-031-6/+5
|
* solver3: Fix error stack handlingJulian Andres Klode2025-02-181-7/+7
| | | | | | | | | | | | Pushing the stack in push and popping it in pop did not really work correctly and is more complex than needed. Instead, push the error stack at the start of the Solve() method and revert at the end, such that we leave exactly at the same error stack level we entered. To handle error clearing on backtracking, just discard any pending errors.
* solver3: Fix test-apt-move-and-forget-manual-sectionsJulian Andres Klode2025-02-181-2/+24
| | | | | Implement the moving of the auto bit. The whole auto-bit management is not entirely optimal yet, but this works.
* solver3: Store clauses as the reasons for decisionsJulian Andres Klode2025-02-141-19/+30
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | So far we only stored the last reason why something was decided, for example, if "A depends B | C" and we assigned B=false, C=false, we'd store "(not) C" as the reason for "(not) A". This gives us only a partial implication graph; after all "C" was not the *sole* reason for not installing A. This has two implications: 1. We cannot do conflict-driven clause learning 2. We cannot print excellent information about why packages cannot be installed (or removed) This commit is incomplete in addressing both; in particular, we always store a clause as a reason for something that is not a root object; whereas MiniSAT would only store a clause on propagation. That is, if A depends B | C, and we install A, then we have to make a choice between B|C. Let's say we pick B, we store 'A depends B|C' as the reason whereas MiniSAT would not store a reason (because it picked the "next best" unassigned literal). Hopefully this is not going to be an issue. The reason is used to calculate the assignments that caused the decision in MiniSAT, but the idea is that we can just treat reason clauses with unassigned values as "no reason". The conflict explanation (WhyStr) has been changed to print the strongest reason; which produces the same result as the previous solution for the test suite. What does this mean? If we look at A depends B|C, let's analyse: Why not A? We return the first assigned value for B|C, likely B. We might have returned C here before as it was the last assignment, but we might also return C here, if B is not assigned. Why B? We return A. If we look at A conflicts B: Why not A? Well B Why not B? Well A Thanks to the structure of the implication graph this is quite simple, but also generalizing this to the CNF format should not be hard. A future version will extend clauses with backlinks to pkgCache::Dependency*, allowing us to print useful information to uses such as "A Depends B | C | D (>= 2)" in the real form, rather than the expanded form which may be "A -> B | C | D=3 | D=2".
* solver3: Only enqueue shared dependencies at the package levelJulian Andres Klode2025-02-141-0/+10
| | | | | | | | | | | | | | | | | | | | | Dependencies shared by all versions are enqueued at the package level, so avoid enqueuing duplicates at the version level. This presumably has no meaningful impact on performance, potentially a negative performance impact on some workloads as we now need to find the duplicates again; it can become useful when there is a lot of backtracking. More importantly though this improves error messages, because now we can say that "all versions of foo depend on X", rather than saying "foo=1 depends on X" and you are left wondering why we did not select "foo=2". In this commit though, improved error messages are not implemented, they depend on redesigning the reason tracking to use clauses. Also the rationale tracking includes a lot more dependencies of the form "pkg:arch=version -> pkg:arch" which are annoying. Improved error messages should fold them into one node.
* solver3: Remove work rescoring in favor of unit propagationJulian Andres Klode2025-02-141-56/+26
| | | | | | | | | | | | | Instead of expensive rescoring of all outstanding items, use unit propagation to find new units after conflicts. We still count the items when adding them; but unless they are 0 or 1, which they should not be, they don't have any effect: The size field is now effectively static. If the size of an optional clause changed to 1, it is inserted a second time, and then moves up to the top of the optional items per the Work::operator< rules.
* solver3: Refactor a debug messageJulian Andres Klode2025-02-141-6/+1
| | | | | Use Var::toString() to print the variable, instead of duplicating the code :D
* solver3: Pass EDSP flags directly rather than via configJulian Andres Klode2025-02-141-2/+3
| | | | | | | This was a rather silly way to communicate state, and it was in the wrong place. Notably also, multiple calls to the solver had the options sticky, that is, if you run upgrade and then it calls ResolveByKeep(), for example.
* solver3: Fix ordering of or groupsJulian Andres Klode2025-02-141-6/+10
| | | | | | | | | | | | | | | | | | | | | | We inadvertently performed a global ordering of all possible solutions for the or group using CompareProviders3. This however is not correct, as we lose the ordering of the dependency group *too* much. Mostly this has no effect, but you can see for example in test-explore-or-groups-in-markinstall various instances of it. Adjust said test case to work with the 3.0 solver to the extent possible under the current design. The 3.0 solver does Recommends after processing any manually installed packages; as such the various Recommends test cases do not work: A `Recommends: okay|upgrade` will not upgrade `upgrade` if it visited `upgrade` first. This may change at a future time, but the correct semantics for Recommends are not entirely clear. Notably, the existing solver is not always consistent. You can see here where they matter, but recently I added test-solver-recommends-depends in which the Recommends do not influence the choice of other Depends.
* solver3: Implement phasing as part of strict pinning rulesJulian Andres Klode2025-02-141-1/+3
| | | | | | | | Phased updates are ignored when strict pinning is on; such that only the installed version will be available. By design of SAT solvers, this means that the version selection clause is unit, and hence the version can be directly propagated, i.e. that choice is safe.
* solver3: Promote various Recommends to DependsJulian Andres Klode2025-02-141-0/+12
| | | | | | | | | | Already satisfied Recommends should be promoted to Depends; as well as Recommends that are new, if the version they are from is an upgrade (such that upgrading does not introduce unsat Recommends). This only works for the case where the new Recommends actually exist; test-resolve-by-keep-new-recommends is not yet implemented, but this means the phasing tests will behave correctly.
* solver3: Correctly call MarkKeep() for kept back packagesJulian Andres Klode2025-02-141-11/+15
| | | | | | This fixes the difference in test-unpack-different-version-unpacked, but more importantly this is needed for phasing to be displayed correctly once that is implemented.
* solver3: Install Essential on dist-upgradeJulian Andres Klode2025-02-141-0/+14
| | | | | | | | This is a bit gnarly, but dist-upgrade is mapped to is an upgrade + removals allowed + installs allowed :D
* solver3: Defer 3.0 'deep' autoremoval to 3.1, fix autoremoveJulian Andres Klode2025-02-141-1/+1
| | | | | | Restore the depcache's MarkRequired logic for 3.0 solver; and change the MarkInstall() call to pass a more correct value for FromUser, to not override an existing automatic status.
* solver3: Reject reverse conflicts on installJulian Andres Klode2025-02-141-0/+9
| | | | | | We forgot this in the previous iteration. This makes the reasoning in the test cases much nicer and apt-test's mantic-to-noble-jak.edsp now finishes rather than running into the timeout (potentially forever).
* solver3: Reject reverse dependencies nativelyJulian Andres Klode2025-02-141-106/+26
| | | | | | | | | | | | | | | | | | Instead of utilizing the reverse depends functionality of the cache and marking all possible reverse dependencies for removal, mark them ourselves by keeping track of reverse-implication-clauses. Notably, this improves the reverse dependency rejection substantially: The previous RejectReverseDependencies() function did not handle Provides. For this to work correctly right now, we need to discover optional clauses too when queuing them. This is somewhat suboptimal as we technically we don't care if they become unsat, we just waste time tracking them. The tests get a bit awkward, but oh well, we use what we can use.
* solver3: Implement a timeout, default 10sJulian Andres Klode2025-02-141-1/+7
| | | | | A SAT solver can run more or less forever, but that's not a good user experience.
* solver3: Discover recursive dependenciesJulian Andres Klode2025-02-141-32/+47
| | | | | | | | | | | | | | | | | | | | | | | | | | | When we have discovered all clauses for a version, discover each possible solution for the clauses. This means that when Discover(foo) is called _anything_ that could lead to foo becoming uninstallable is translated; so we can extend this next by keeping a list of reverse dependencies for each package and rejecting those. We limit the discovery to those variables that we did not already enqueue as a negative fact at the root level, as those can never become true. We are utilizing a queue here which is not the most performant solution possible, but where it excels is in producing usable stack traces when debugging. Traversing the entire dependency tree using recursion can easily produce thousand levels of recursion. The queue means that we discover packages in a breadth-first manner compatible with the order in which we propagate dependencies, which is helpful for consistency. The queue did not appear as a bottleneck in benchmarking. If it did, we could switch to a grow-only ring buffer (std::queue's underlying deque also shrinks automatically which is suboptimal).
* solver3: Defer version selection where possibleJulian Andres Klode2025-02-141-12/+20
| | | | | | | | | | | | | If a dependency can be satisfied by all versions of a package, add the package to the clause instead of the version object. This works only if there are no providers for the package: Providers are quite hard to enumerate over and make sure that all versions of a package satisfy the provider dependency. Implement arbitrary selection between packages and versions for the CompareProviders class: We pick the best version for each package and then pit them against each other.