summaryrefslogtreecommitdiff
Commit message (Collapse)AuthorAgeFilesLines
* Release 2.9.292.9.29Julian Andres Klode2025-02-145-6/+42
|
* Merge branch 'solver3' into 'main'Julian Andres Klode2025-02-1451-428/+500
|\ | | | | | | | | Get 3.0 solver mostly in shape See merge request apt-team/apt!451
| * solver3: Store clauses as the reasons for decisionsJulian Andres Klode2025-02-142-23/+35
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-1422-65/+74
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-146-64/+28
| | | | | | | | | | | | | | | | | | | | | | | | | | 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
| * test: Remove long obsolete FIXME for solver3Julian Andres Klode2025-02-141-2/+1
| |
| * test: Annotate the broken test casesJulian Andres Klode2025-02-141-4/+4
| |
| * test-ubuntu-bug-614993: Avoid implicit upgrade-by-source requirementJulian Andres Klode2025-02-142-11/+8
| | | | | | | | | | | | | | This allows it to work with solver3 before that feature is implemented. This essentially reverts the change to that test made in 119a8d0ed19a4e946591b9aef7d662e14ca7ece5
| * ResolveByKeep: Do not pass UPGRADE_ALL to EDSP solverJulian Andres Klode2025-02-142-2/+1
| | | | | | | | | | | | | | | | | | | | When we call an EDSP solver from ResolveByKeep(), we are not in an upgrade context: In an upgrade context, we call the solver directly from upgrade.cc instead of doing any Resolve() or ResolveByKeep() calls. This fixes the test-bug-602412-dequote-redirect test case, where ResolveByKeep() was called as part of --fix-missing
| * solver3: Pass EDSP flags directly rather than via configJulian Andres Klode2025-02-144-13/+11
| | | | | | | | | | | | | | 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.
| * test-dont-forget-conflicts-via-unknown-architectures: Use install, not upgradeJulian Andres Klode2025-02-142-2/+2
| | | | | | | | | | The 3.0 solver does not support upgrade arguments to override the policy.
| * Fix test-phased-updates-upgradeJulian Andres Klode2025-02-142-3/+2
| | | | | | | | | | | | | | Our upgrade policy does not support specifying packagenames to upgrade commands that are not subject to the policy. It's possible this can be addressed at a later time, but let's not block the rest of the test from running.
| * solver3: Fix ordering of or groupsJulian Andres Klode2025-02-143-16/+43
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-142-5/+4
| | | | | | | | | | | | | | | | 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-142-22/+16
| | | | | | | | | | | | 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.
| * test: Fix 7 test cases for 3.0 solver, annotate othersJulian Andres Klode2025-02-1410-36/+112
| | | | | | | | | | Fix the test cases we can fix directly; add annotations where we know what work is needed, next we'll look at the other bugs.
| * test: Support comments in --skip/--only filesJulian Andres Klode2025-02-141-2/+2
| | | | | | | | Strip everything after "#" and spaces before it.
| * solver3: Install Essential on dist-upgradeJulian Andres Klode2025-02-143-3/+28
| | | | | | | | | | | | | | | | 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-146-11/+7
| | | | | | | | | | | | 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-144-9/+15
| | | | | | | | | | | | 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-147-128/+37
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-143-1/+13
| | | | | | | | | | A SAT solver can run more or less forever, but that's not a good user experience.
| * solver3: Discover recursive dependenciesJulian Andres Klode2025-02-143-33/+52
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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-1412-43/+60
| | | | | | | | | | | | | | | | | | | | | | | | | | 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.
| * Add DepIterator::IsSatisfied(PkgIterator)Julian Andres Klode2025-02-142-0/+8
|/ | | | | This allows checking if a dependency is satisfied by all versions of a package.
* Swedish program translation updatePeter Krefting2025-02-131-281/+307
| | | | | | Note: Committer changed language team to list on @l.d.o as per bug Closes: #1070029
* German program translation updateHelge Kreutzmann2025-02-131-95/+69
| | | | Closes: #1095681
* Merge branch 'run-tests-fix-skip' into 'main'Julian Andres Klode2025-02-121-2/+2
|\ | | | | | | | | test: Fix skipping test cases See merge request apt-team/apt!450
| * test: Fix skipping test casesArnaud Rebillout2025-02-121-2/+2
|/ | | | | | | | | | | The `--skip` option was introduced in commit 1fbdc04be7547b8c4bb84f400329415ea0bc60b5, and it was broken straight from the start for the case where `TESTTORUN` is not set (see last hunk in this commit, the mistake is obvious). It went unnoticed as the GitLab CI runs tests with the `-j` option, so `TESTTORUN` is set. While ci.debian.net runs without -j, but doesn't skip any test, so it doesn't trigger the bug either.
* Fix syntax errors in French update hidden by dirty buildDavid Kalnischkies2025-02-091-2/+1
| | | | | | | | | | | | A clean build fails due to issues in the translated apt.ent, but I "only" tested a dirty build which didn't cause the file to be regenerated and hence an old valid file to be used. Ignored for the changelog as this is just a fixup of a previous commit that never shipped to anyone except CI. Regression-Of: 2d3f4654cd6a70c3fa1a40ec423a19b101ea5307 Git-Dch: Ignore
* French manpage translation updateJean-Pierre Giraud2025-02-091-851/+1144
| | | | | | | Note: Committer fixed `fr.po:1833: end-of-line within string` error reported by `msgfmt --check`. Closes: #1088326, #1050876
* Dutch manpages translation updateFrans Spiesschaert2025-02-091-536/+54
| | | | | | Note: Committer fixed two accidentally translated closing tags Closes: #1095411
* Dutch program translation updateFrans Spiesschaert2025-02-091-138/+13
| | | | Closes: #1095409
* German program translation updateHelge Kreutzmann2025-02-091-5/+6
| | | | Closes: #1052411
* Release 2.9.282.9.28Julian Andres Klode2025-02-075-6/+41
|
* Merge branch 'solver3'Julian Andres Klode2025-02-074-300/+383
|\
| * solver3: Avoid std::vector for statically sized arraysJulian Andres Klode2025-02-072-26/+70
| | | | | | | | | | | | | | | | | | | | The bounds checking on the vector accesses is killing performance, so switch from vector to a basic array, given that we don't actually need _any_ functionality from vector... Of course while we are at it, let us define a safe wrapper around it so we cannot accidentally index arrays for package IDs with version IDs and whatnot.
| * solver3: Add const where helpfulJulian Andres Klode2025-02-072-10/+24
| | | | | | | | | | operator[] is a bit annoying here, but oh well, what can we do?
| * solver3: Point to stored clauses, do not copy themJulian Andres Klode2025-02-072-38/+41
| | | | | | | | | | | | This makes Work trivially destructible, and in turn solved, allowing their queues to be destructed without running destructors, and avoiding the copy should have a nice performance improvement.
| * solver3: Use a package clause for optional rootsJulian Andres Klode2025-02-072-6/+23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Instead of iterating over the version here and picking it, just enqueue the package as well, which should allow us to select the version at a later time. This also causes a funny inverse problem now, though, as was evidenced in one of the test cases: To summarize, if our optional roots are all single items, they will be considered soft-unit, causing them to be processed in order. However it can be that an optional root has a specific version selected because another version was rejected. Consider X Conflicts A (= 1) A, B have 2 versions: '2' available, '1' installed B (= n) Depends A (= n) Run `apt install X`. The expected result is for A and B to be upgraded to version 2. With only a package root, if B appears in the cache before A however, we will get: Install X Reject A (= 1) Install B Install B (= 1) # keep it installed Reject A (= 2) => A is being removed as both versions are rejected Hence we do also need to re-introduce the additional version clause, now we get: Install X Reject A (= 1) Install A (= 2) # it got "promoted" to a 'stronger' soft-unit Install B Fail B (= 1) # keep it installed Install B (= 2) Introduce a root state to hold all the clauses that don't have another owner. moo
| * solver3: Refactor Dump() to toString()Julian Andres Klode2025-02-072-54/+27
| | | | | | | | | | This vastly simplifies the code at the expense of performance, lol.
| * solver3: Support comparing packages and versionsJulian Andres Klode2025-02-071-8/+15
| | | | | | | | | | | | | | | | | | | | | | When comparing packages, we compare the best version for the package. To determine the best version, the rules for comparisons are applied normally. This allows us to mix packages and versions in a clause as the solutions for a dependency, which means we'll be able to defer the selection of a particular version of a package to a later time.
| * solver3: Replace PropagateInstall() with Discover()Julian Andres Klode2025-02-073-93/+84
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Just propagate the stored clauses after we have discovered them; this is quite straightforward. We now more reliably discover common dependencies at the package level, adjust the test case accordingly. The next step is to make discovery recursive, or iterative, to build an entire recursive tree from all roots, and then we can reject reverse dependencies based on it. A bunch of refactorings are needed in the process. We remove the useless Hint enumeration and insert a flags struct into the State, such that we can record whether a package/version has been discovered, to avoid spending double time on discovery.
| * solver3: Extract TranslateOrGroup() out of EnqueueOrGroup()Julian Andres Klode2025-02-073-41/+43
| | | | | | | | | | | | | | This is not *purely* a refactoring, we accidentally used the version of the dependency when enqueuing conflicts rather than the reason, so the conflict string in the test case is different; the logging had the same issue.
| * solver3: Extract Clause out of WorkJulian Andres Klode2025-02-072-66/+86
| | | | | | | | | | Extract clause into a separate struct and embed a copy of it in our Work class.
| * solver3: Simplify WhyStr()Julian Andres Klode2025-02-071-16/+5
| | | | | | | | Reuse the generic variable rendering
| * solver3: Cache all configuration callsJulian Andres Klode2025-02-072-9/+10
| |
| * solver3: Cache calls to policyJulian Andres Klode2025-02-072-6/+22
| | | | | | | | It's a bit silly otherwise.