summaryrefslogtreecommitdiff
path: root/apt-pkg/solver3.h
diff options
context:
space:
mode:
Diffstat (limited to 'apt-pkg/solver3.h')
-rw-r--r--apt-pkg/solver3.h212
1 files changed, 160 insertions, 52 deletions
diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h
index 4f6f83d15..65cd9f017 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>
@@ -19,6 +20,48 @@
namespace APT
{
+/**
+ * \brief A simple mapping from objects in the cache to user-defined types.
+ *
+ * This default initializes an array with the specified value type for each
+ * object in the cache of that type.
+ */
+template <typename K, typename V, bool fast = false>
+class ContiguousCacheMap
+{
+ V *data_; // Avoid std::unique_ptr() as it may check that it's non-null.
+
+ public:
+ ContiguousCacheMap(pkgCache &cache)
+ {
+ static_assert(std::is_constructible_v<V>);
+ if constexpr (fast)
+ {
+ static_assert(std::is_trivially_constructible_v<V>);
+ static_assert(std::is_trivially_destructible_v<V>);
+ }
+
+ size_t size;
+ if constexpr (std::is_same_v<K, pkgCache::Version>)
+ size = cache.Head().VersionCount;
+ else if constexpr (std::is_same_v<K, pkgCache::Package>)
+ size = cache.Head().PackageCount;
+ else
+ static_assert(false, "Cannot construct map for key type");
+
+ data_ = new V[size]{};
+ }
+ V &operator[](const K *key) { return data_[key->ID]; }
+ const V &operator[](const K *key) const { return data_[key->ID]; }
+ ~ContiguousCacheMap() { delete[] data_; }
+};
+
+/**
+ * \brief A version of ContiguousCacheMap that ensures allocation and deallocation is trivial.
+ */
+template <typename K, typename V>
+using FastContiguousCacheMap = ContiguousCacheMap<K, V, true>;
+
/*
* \brief APT 3.0 solver
*
@@ -36,6 +79,7 @@ class Solver
struct Var;
struct CompareProviders3;
struct State;
+ struct Clause;
struct Work;
struct Solved;
@@ -91,29 +135,56 @@ class Solver
pkgCache &cache;
// Policy is needed for determining candidate version.
pkgDepCache::Policy &policy;
+ // Root state
+ std::unique_ptr<State> rootState;
// States for packages
- std::vector<State> pkgStates{};
+ ContiguousCacheMap<pkgCache::Package, State> pkgStates;
// States for versions
- std::vector<State> verStates{};
+ ContiguousCacheMap<pkgCache::Version, State> verStates;
// \brief Helper function for safe access to package state.
inline State &operator[](pkgCache::Package *P)
{
- return pkgStates[P->ID];
+ return pkgStates[P];
+ }
+ inline const State &operator[](pkgCache::Package *P) const
+ {
+ return pkgStates[P];
}
// \brief Helper function for safe access to version state.
inline State &operator[](pkgCache::Version *V)
{
- return verStates[V->ID];
+ return verStates[V];
+ }
+ inline const State &operator[](pkgCache::Version *V) const
+ {
+ return verStates[V];
}
// \brief Helper function for safe access to either state.
inline State &operator[](Var r);
+ inline const State &operator[](Var r) const;
- mutable std::vector<char> pkgObsolete;
+ mutable FastContiguousCacheMap<pkgCache::Package, char> pkgObsolete;
bool Obsolete(pkgCache::PkgIterator pkg) const;
bool ObsoletedByNewerSourceVersion(pkgCache::VerIterator cand) const;
+ mutable FastContiguousCacheMap<pkgCache::Version, short> priorities;
+ short GetPriority(pkgCache::VerIterator ver) const
+ {
+ if (priorities[ver] == 0)
+ priorities[ver] = policy.GetPriority(ver);
+ return priorities[ver];
+ }
+
+ mutable ContiguousCacheMap<pkgCache::Package, pkgCache::VerIterator> candidates;
+ pkgCache::VerIterator GetCandidateVer(pkgCache::PkgIterator pkg) const
+ {
+ if (candidates[pkg].end())
+ candidates[pkg] = policy.GetCandidateVer(pkg);
+ return candidates[pkg];
+ }
+
// \brief Heap of the remaining work.
//
// We are using an std::vector with std::make_heap(), std::push_heap(),
@@ -145,25 +216,34 @@ class Solver
int debug{_config->FindI("Debug::APT::Solver")};
// \brief If set, we try to keep automatically installed packages installed.
bool KeepAuto{not _config->FindB("APT::Get::AutomaticRemove")};
+ // \brief Determines if we are in upgrade mode.
+ bool IsUpgrade{_config->FindB("APT::Solver::Upgrade", false)};
// \brief If set, removals are allowed.
bool AllowRemove{_config->FindB("APT::Solver::Remove", true)};
+ // \brief If set, removal of manual packages is allowed.
+ bool AllowRemoveManual{AllowRemove && _config->FindB("APT::Solver::RemoveManual", false)};
// \brief If set, installs are allowed.
bool AllowInstall{_config->FindB("APT::Solver::Install", true)};
// \brief If set, we use strict pinning.
bool StrictPinning{_config->FindB("APT::Solver::Strict-Pinning", true)};
+ // \brief If set, we install missing recommends and pick new best packages.
+ bool FixPolicyBroken{_config->FindB("APT::Get::Fix-Policy-Broken")};
+ // \brief Discover a variable, translating the underlying dependencies to the SAT presentation
+ void Discover(Var var);
+ // \brief Link a clause into the watchers
+ void RegisterClause(Clause &&clause);
// \brief Enqueue dependencies shared by all versions of the package.
- bool EnqueueCommonDependencies(pkgCache::PkgIterator Pkg);
+ void RegisterCommonDependencies(pkgCache::PkgIterator Pkg);
+
// \brief Reject reverse dependencies. Must call std::make_heap() after.
- bool RejectReverseDependencies(pkgCache::VerIterator Ver);
- // \brief Enqueue a single or group
- bool EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepIterator end, Var reason);
+ [[nodiscard]] bool RejectReverseDependencies(pkgCache::VerIterator Ver);
+ // \brief Translate an or group into a clause object
+ [[nodiscard]] Clause TranslateOrGroup(pkgCache::DepIterator start, pkgCache::DepIterator end, Var reason);
// \brief Propagate all pending propagations
- bool Propagate();
- // \brief Propagate a "true" value of a variable
- bool PropagateInstall(Var var);
+ [[nodiscard]] bool Propagate();
// \brief Propagate a rejection of a variable
- bool PropagateReject(Var var);
+ [[nodiscard]] bool PropagateReject(Var var);
// \brief Return the current depth (choices.size() with casting)
depth_type depth()
@@ -175,11 +255,11 @@ class Solver
// \brief Create a new decision level.
void Push(Work work);
// \brief Revert to the previous decision level.
- bool Pop();
+ [[nodiscard]] bool Pop();
// \brief Undo a single assignment / solved work item
void UndoOne();
// \brief Add work to our work queue.
- void AddWork(Work &&work);
+ [[nodiscard]] bool AddWork(Work &&work);
// \brief Rescore the work after a reject or a pop
void RescoreWorkIfNeeded();
@@ -187,20 +267,20 @@ class Solver
Solver(pkgCache &Cache, pkgDepCache::Policy &Policy);
// Assume that the variable is decided as specified.
- bool Assume(Var var, bool decision, Var reason);
+ [[nodiscard]] bool Assume(Var var, bool decision, Var reason);
// Enqueue a decision fact
- bool Enqueue(Var var, bool decision, Var reason);
+ [[nodiscard]] bool Enqueue(Var var, bool decision, Var reason);
// \brief Apply the selections from the dep cache to the solver
- bool FromDepCache(pkgDepCache &depcache);
+ [[nodiscard]] bool FromDepCache(pkgDepCache &depcache);
// \brief Apply the solver result to the depCache
- bool ToDepCache(pkgDepCache &depcache);
+ [[nodiscard]] bool ToDepCache(pkgDepCache &depcache) const;
// \brief Solve the dependencies
- bool Solve();
+ [[nodiscard]] bool Solve();
// Print dependency chain
- std::string WhyStr(Var reason);
+ std::string WhyStr(Var reason) const;
};
}; // namespace APT
@@ -242,11 +322,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
{
@@ -259,6 +349,33 @@ struct APT::Solver::Var
};
/**
+ * \brief A single clause
+ *
+ * A clause is a normalized, expanded dependency, translated into an implication
+ * in terms of Var objects, that is, `reason -> solutions[0] | ... | solutions[n]`
+ */
+struct APT::Solver::Clause
+{
+ // \brief Underyling dependency
+ pkgCache::Dependency *dep = nullptr;
+ // \brief Var for the work
+ Var reason;
+ // \brief The group we are in
+ Group group;
+ // \brief Possible solutions to this task, ordered in order of preference.
+ std::vector<Var> solutions{};
+ // \brief An optional clause does not need to be satisfied
+ bool optional;
+
+ // \brief A negative clause negates the solutions, that is X->A|B you get X->!(A|B), aka X->!A&!B
+ bool negative;
+
+ inline Clause(Var reason, Group group, bool optional = false, bool negative = false) : reason(reason), group(group), optional(optional), negative(negative) {}
+
+ std::string toString(pkgCache &cache) const;
+};
+
+/**
* \brief A single work item
*
* A work item is a positive dependency that still needs to be resolved. Work
@@ -270,37 +387,27 @@ struct APT::Solver::Var
*/
struct APT::Solver::Work
{
- // \brief Var for the work
- Var reason;
+ const Clause *clause;
+
// \brief The depth at which the item has been added
depth_type depth;
- // \brief The group we are in
- Group group;
- // \brief Possible solutions to this task, ordered in order of preference.
- std::vector<pkgCache::Version *> 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;
+ size_t size{0};
};
- // \brief Whether this is an optional work item, they will be processed last
- bool optional;
- // \brief Whether this is an ugprade
- bool upgrade;
// \brief This item should be removed from the queue.
- bool erased;
+ bool erased{false};
bool operator<(APT::Solver::Work const &b) const;
- // \brief Dump the work item to std::cerr
- void Dump(pkgCache &cache);
-
- inline Work(Var reason, depth_type depth, Group group, bool optional = false, bool upgrade = false) : reason(reason), depth(depth), group(group), size(0), optional(optional), upgrade(upgrade), erased(false) {}
+ std::string toString(pkgCache &cache) const;
+ inline Work(const Clause *clause, depth_type depth) : clause(clause), depth(depth) {}
};
// \brief This essentially describes the install state in RFC2119 terms.
@@ -314,17 +421,6 @@ enum class APT::Solver::Decision : uint16_t
MUSTNOT,
};
-// \brief Hints for the solver about the item.
-enum class APT::Solver::Hint : uint16_t
-{
- // \brief We have not made a choice about the package yet
- NONE,
- // \brief This package was listed as a Recommends of a must package,
- SHOULD,
- // \brief This package was listed as a Suggests of a must-not package
- MAY,
-};
-
/**
* \brief The solver state
*
@@ -352,8 +448,16 @@ struct APT::Solver::State
// \brief This essentially describes the install state in RFC2119 terms.
Decision decision{Decision::NONE};
- // \brief Any hint.
- Hint hint{Hint::NONE};
+ // \brief Flags.
+ struct
+ {
+ bool discovered{};
+ } flags;
+
+ static_assert(sizeof(flags) <= sizeof(int));
+
+ // \brief Clauses owned by this package/version
+ std::vector<std::unique_ptr<Clause>> clauses;
};
/**
@@ -376,6 +480,10 @@ inline APT::Solver::State &APT::Solver::operator[](Var r)
return (*this)[cache.PkgP + P];
if (auto V = r.Ver())
return (*this)[cache.VerP + V];
+ return *rootState.get();
+}
- abort();
+inline const APT::Solver::State &APT::Solver::operator[](Var r) const
+{
+ return const_cast<Solver &>(*this)[r];
}