From ffc9001b513606869c3cf94fb85a792503974d1f Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Tue, 7 Oct 2025 21:49:44 +0200 Subject: solver3: Eagerly satisfy previously satisfied Recommends 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 --- apt-pkg/solver3.cc | 7 +++++-- apt-pkg/solver3.h | 5 ++++- 2 files changed, 9 insertions(+), 3 deletions(-) (limited to 'apt-pkg') diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc index a386b967c..65e3940b4 100644 --- a/apt-pkg/solver3.cc +++ b/apt-pkg/solver3.cc @@ -223,8 +223,8 @@ bool APT::Solver::Work::operator<(APT::Solver::Work const &b) const { if ((not clause->optional && size < 2) != (not b.clause->optional && b.size < 2)) return not b.clause->optional && b.size < 2; - if (clause->optional != b.clause->optional) - return clause->optional; + if (clause->eager != b.clause->eager) + return not clause->eager; if (clause->group != b.clause->group) return clause->group > b.clause->group; if ((size < 2) != (b.size < 2)) @@ -955,6 +955,7 @@ APT::Solver::Clause APT::Solver::TranslateOrGroup(pkgCache::DepIterator start, p if (unlikely(debug >= 3)) std::cerr << "Promoting previously satisfied clause to hard dependency: " << clause.toString(cache, true) << std::endl; clause.optional = false; + clause.eager = true; } else if ( IsUpgrade && not(AllowRemove && AllowInstall) // promote Recommends to Depends in upgrade, not in dist-upgrade. @@ -965,6 +966,7 @@ APT::Solver::Clause APT::Solver::TranslateOrGroup(pkgCache::DepIterator start, p if (unlikely(debug >= 3)) std::cerr << "Promoting new clause to hard dependency: " << clause.toString(cache) << std::endl; clause.optional = false; + clause.eager = true; } else if (not existing.end() && importantToKeep(start) && satisfied) { @@ -975,6 +977,7 @@ APT::Solver::Clause APT::Solver::TranslateOrGroup(pkgCache::DepIterator start, p clause.solutions.erase(std::remove_if(clause.solutions.begin(), clause.solutions.end(), [this](auto var) { return var.CastPkg(cache)->CurrentVer == nullptr; }), clause.solutions.end()); + clause.eager = true; } } diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h index 019a01b9f..8d834b313 100644 --- a/apt-pkg/solver3.h +++ b/apt-pkg/solver3.h @@ -406,10 +406,13 @@ struct APT::Solver::Clause // \brief A negative clause negates the solutions, that is X->A|B you get X->!(A|B), aka X->!A&!B bool negative; + // \brief An optional clause may be eager + bool eager; + // Clauses merged with this clause std::forward_list merged; - inline Clause(Var reason, Group group, bool optional = false, bool negative = false) : reason(reason), group(group), optional(optional), negative(negative) {} + inline Clause(Var reason, Group group, bool optional = false, bool negative = false) : reason(reason), group(group), optional(optional), negative(negative), eager(not optional) {} std::string toString(pkgCache &cache, bool pretty = false, bool showMerged = true) const; }; -- cgit v1.2.3-70-g09d2