summaryrefslogtreecommitdiff
path: root/apt-pkg
diff options
context:
space:
mode:
authorJulian Andres Klode <julian.klode@canonical.com>2025-02-03 16:56:08 +0100
committerJulian Andres Klode <julian.klode@canonical.com>2025-02-07 20:59:43 +0100
commit466ff3cf0afc5c034ec77868781d8e19e274e7ff (patch)
treeb133e8ec5a47979703adbf8e543445198361c240 /apt-pkg
parent3c4551bbdae483d37b985b2c690d77372fb50e89 (diff)
solver3: Partially generalize work items from Version to Var
Store all possible solutions and choices as Var. Currently any Var in here must be a Version because CompareProviders3 cannot compare versions against packages yet, but in the future (TM), this will allow storing packages directly in clauses, which allows defering version selection to a later point.
Diffstat (limited to 'apt-pkg')
-rw-r--r--apt-pkg/solver3.cc62
-rw-r--r--apt-pkg/solver3.h15
2 files changed, 46 insertions, 31 deletions
diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc
index 15d55be38..87e4babc8 100644
--- a/apt-pkg/solver3.cc
+++ b/apt-pkg/solver3.cc
@@ -49,6 +49,15 @@ struct APT::Solver::CompareProviders3 /*{{{*/
{
return (*this)(pkgCache::VerIterator(Cache, AV), pkgCache::VerIterator(Cache, BV));
}
+ bool operator()(Var a, Var b)
+ {
+ if (auto va = a.Ver(Cache))
+ {
+ if (auto vb = b.Ver(Cache))
+ return (*this)(va, vb);
+ }
+ abort();
+ }
bool operator()(pkgCache::VerIterator const &AV, pkgCache::VerIterator const &BV)
{
pkgCache::PkgIterator const A = AV.ParentPkg();
@@ -219,10 +228,7 @@ void APT::Solver::Work::Dump(pkgCache &cache)
std::cerr << Ver.ParentPkg().FullName() << "=" << Ver.VerStr();
std::cerr << " -> ";
for (auto sol : solutions)
- {
- auto Ver = pkgCache::VerIterator(cache, sol);
- std::cerr << " | " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr();
- }
+ std::cerr << " | " << sol.toString(cache);
}
// Prints an implication graph part of the form A -> B -> C, possibly with "not"
@@ -375,7 +381,7 @@ bool APT::Solver::PropagateInstall(Var var)
Work workItem{Var(Pkg), depth(), Group::SelectVersion};
for (auto ver = Pkg.VersionList(); not ver.end(); ver++)
{
- workItem.solutions.push_back(ver);
+ workItem.solutions.push_back(Var(ver));
if ((*this)[ver].decision != Decision::MUSTNOT)
anyInstallable = true;
if ((*this)[ver].decision == Decision::MUST)
@@ -396,7 +402,7 @@ bool APT::Solver::PropagateInstall(Var var)
if (workItem.solutions.size() > 1 || workItem.optional)
AddWork(std::move(workItem));
- else if (not Enqueue(Var(pkgCache::VerIterator(cache, workItem.solutions[0])), true, workItem.reason))
+ else if (not Enqueue(workItem.solutions[0], true, workItem.reason))
return false;
// FIXME: We skip enqueuing duplicate common dependencies if we already selected a version, but
@@ -534,7 +540,7 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
{
if (unlikely(debug >= 3))
std::cerr << "Adding work to item " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " -> " << tgti.ParentPkg().FullName() << "=" << tgti.VerStr() << "\n";
- workItem.solutions.push_back(*tgt);
+ workItem.solutions.push_back(Var(pkgCache::VerIterator(cache, *tgt)));
}
}
delete[] all;
@@ -553,11 +559,11 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
if (not fixPolicy)
std::stable_sort(workItem.solutions.begin(), workItem.solutions.end(), CompareProviders3{cache, policy, TgtPkg, *this});
- if (std::all_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto V) -> auto
- { return pkgCache::VerIterator(cache, V).ParentPkg()->CurrentVer == 0; }))
+ if (std::all_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto var) -> auto
+ { return var.CastPkg(cache)->CurrentVer == 0; }))
workItem.group = Group::SatisfyNew;
- if (std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto V) -> auto
- { return Obsolete(pkgCache::VerIterator(cache, V).ParentPkg()); }))
+ if (std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto var) -> auto
+ { return Obsolete(var.CastPkg(cache)); }))
workItem.group = Group::SatisfyObsolete;
// Try to perserve satisfied Recommends. FIXME: We should check if the Recommends was there in the installed version?
if (workItem.optional && start.ParentPkg()->CurrentVer)
@@ -569,8 +575,8 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
if (not D.IsCritical() && not D.IsNegative() && D.TargetPkg() == start.TargetPkg())
newOptional = false, wasImportant = policy.IsImportantDep(D);
- bool satisfied = std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto ver)
- { return pkgCache::VerIterator(cache, ver).ParentPkg().CurrentVer() == ver; });
+ bool satisfied = std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto var)
+ { return Var(var.CastPkg(cache).CurrentVer()) == var; });
if (important && wasImportant && not newOptional && not satisfied)
{
@@ -615,7 +621,7 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
}
if (workItem.optional || workItem.solutions.size() > 1)
AddWork(std::move(workItem));
- else if (not Enqueue(Var(pkgCache::VerIterator(cache, workItem.solutions[0])), true, reason))
+ else if (not Enqueue(workItem.solutions[0], true, reason))
return false;
}
else if (start.IsCritical() && not start.IsNegative())
@@ -759,7 +765,7 @@ bool APT::Solver::Pop()
assert(choices.back() < solved.size());
int itemsToUndo = solved.size() - choices.back();
- pkgCache::VerIterator choice(cache, solved[choices.back()].work->choice);
+ auto choice = solved[choices.back()].work->choice;
for (; itemsToUndo; --itemsToUndo)
UndoOne();
@@ -774,14 +780,14 @@ bool APT::Solver::Pop()
std::make_heap(work.begin(), work.end());
if (unlikely(debug >= 2))
- std::cerr << "Backtracking to choice " << choice.ParentPkg().FullName() << "=" << choice.VerStr() << "\n";
+ std::cerr << "Backtracking to choice " << choice.toString(cache) << "\n";
// FIXME: There should be a reason!
- if (not Enqueue(Var(choice), false, {}))
+ if (not Enqueue(choice, false, {}))
return false;
if (unlikely(debug >= 2))
- std::cerr << "Backtracked to choice " << choice.ParentPkg().FullName() << "=" << choice.VerStr() << "\n";
+ std::cerr << "Backtracked to choice " << choice.toString(cache) << "\n";
return true;
}
@@ -892,21 +898,20 @@ bool APT::Solver::Solve()
bool foundSolution = false;
for (auto &sol : item.solutions)
{
- pkgCache::VerIterator ver(cache, sol);
- if ((*this)[ver].decision == Decision::MUSTNOT)
+ if ((*this)[sol].decision == Decision::MUSTNOT)
{
if (unlikely(debug >= 3))
- std::cerr << "(existing conflict: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << ")\n";
+ std::cerr << "(existing conflict: " << sol.toString(cache) << ")\n";
continue;
}
if (item.size > 1 || item.optional)
{
- item.choice = ver;
+ item.choice = sol;
Push(item);
}
if (unlikely(debug >= 3))
- std::cerr << "(try it: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << ")\n";
- if (not Enqueue(Var(ver), true, item.reason) && not Pop())
+ std::cerr << "(try it: " << sol.toString(cache) << ")\n";
+ if (not Enqueue(sol, true, item.reason) && not Pop())
return false;
foundSolution = true;
break;
@@ -916,13 +921,12 @@ bool APT::Solver::Solve()
std::ostringstream dep;
assert(item.solutions.size() > 0);
for (auto &sol : item.solutions)
- dep << (dep.tellp() == 0 ? "" : " | ") << pkgCache::VerIterator(cache, sol).ParentPkg().FullName() << "=" << pkgCache::VerIterator(cache, sol).VerStr();
+ dep << (dep.tellp() == 0 ? "" : " | ") << sol.toString(cache);
_error->Error("Unsatisfiable dependency: %s -> %s", WhyStr(item.reason).c_str(), dep.str().c_str());
for (auto &sol : item.solutions)
if ((*this)[sol].decision == Decision::MUSTNOT)
- _error->Error("Not considered: %s=%s: %s", pkgCache::VerIterator(cache, sol).ParentPkg().FullName().c_str(),
- pkgCache::VerIterator(cache, sol).VerStr(),
- WhyStr(Var(pkgCache::VerIterator(cache, sol))).c_str());
+ _error->Error("Not considered: %s: %s", sol.toString(cache).c_str(),
+ WhyStr(sol).c_str());
if (not Pop())
return false;
}
@@ -1003,7 +1007,7 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache)
{
Work w{Var(), depth(), Group, isOptional};
for (auto V = P.VersionList(); not V.end(); ++V)
- w.solutions.push_back(V);
+ w.solutions.push_back(Var(V));
std::stable_sort(w.solutions.begin(), w.solutions.end(), CompareProviders3{cache, policy, P, *this});
AddWork(std::move(w));
}
diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h
index 282e8865b..4561ffa73 100644
--- a/apt-pkg/solver3.h
+++ b/apt-pkg/solver3.h
@@ -7,6 +7,7 @@
* SPDX-License-Identifier: GPL-2.0+
*/
+#include <cassert>
#include <optional>
#include <queue>
#include <vector>
@@ -242,11 +243,21 @@ struct APT::Solver::Var
{
return IsVersion ? pkgCache::VerIterator(cache, cache.VerP + Ver()) : pkgCache::VerIterator();
}
+ // \brief Return a package, cast from version if needed
+ pkgCache::PkgIterator CastPkg(pkgCache &cache) const
+ {
+ assert(MapPtr != 0);
+ return IsVersion ? Ver(cache).ParentPkg() : Pkg(cache);
+ }
// \brief Check if there is no reason.
bool empty() const
{
return IsVersion == 0 && MapPtr == 0;
}
+ bool operator==(Var const other)
+ {
+ return IsVersion == other.IsVersion && MapPtr == other.MapPtr;
+ }
std::string toString(pkgCache &cache) const
{
@@ -277,14 +288,14 @@ struct APT::Solver::Work
// \brief The group we are in
Group group;
// \brief Possible solutions to this task, ordered in order of preference.
- std::vector<pkgCache::Version *> solutions{};
+ std::vector<Var> solutions{};
// This is a union because we only need to store the choice we made when adding
// to the choice vector, and we don't need the size of valid choices in there.
union
{
// The choice we took
- pkgCache::Version *choice;
+ Var choice;
// Number of valid choices
size_t size;
};