summaryrefslogtreecommitdiffstats
path: root/apt-pkg
diff options
context:
space:
mode:
Diffstat (limited to 'apt-pkg')
-rw-r--r--apt-pkg/acquire-worker.cc5
-rw-r--r--apt-pkg/contrib/gpgv.cc32
-rw-r--r--apt-pkg/contrib/gpgv.h1
-rw-r--r--apt-pkg/deb/deblistparser.cc85
-rw-r--r--apt-pkg/deb/debmetaindex.cc2
-rw-r--r--apt-pkg/depcache.cc19
-rw-r--r--apt-pkg/edsp.cc23
-rw-r--r--apt-pkg/edsp/edsplistparser.cc15
-rw-r--r--apt-pkg/init.cc4
-rw-r--r--apt-pkg/pkgcachegen.cc4
-rw-r--r--apt-pkg/solver3.cc326
-rw-r--r--apt-pkg/solver3.h63
-rw-r--r--apt-pkg/tagfile-keys.list1
13 files changed, 404 insertions, 176 deletions
diff --git a/apt-pkg/acquire-worker.cc b/apt-pkg/acquire-worker.cc
index 4f247cf..696a3e0 100644
--- a/apt-pkg/acquire-worker.cc
+++ b/apt-pkg/acquire-worker.cc
@@ -200,6 +200,7 @@ enum class APT_HIDDEN MessageType
STATUS = 102,
REDIRECT = 103,
WARNING = 104,
+ AUDIT = 105,
URI_START = 200,
URI_DONE = 201,
AUX_REQUEST = 351,
@@ -388,6 +389,10 @@ bool pkgAcquire::Worker::RunMessages()
_error->Warning("%s: %s", Itm ? Itm->Owner ? Itm->Owner->DescURI().c_str() : Access.c_str() : Access.c_str(), LookupTag(Message, "Message").c_str());
break;
+ case MessageType::AUDIT:
+ _error->Audit("%s: %s", Itm ? Itm->Owner ? Itm->Owner->DescURI().c_str() : Access.c_str() : Access.c_str(), LookupTag(Message, "Message").c_str());
+ break;
+
case MessageType::URI_START:
{
if (Itm == nullptr)
diff --git a/apt-pkg/contrib/gpgv.cc b/apt-pkg/contrib/gpgv.cc
index 2fa5b0c..225acae 100644
--- a/apt-pkg/contrib/gpgv.cc
+++ b/apt-pkg/contrib/gpgv.cc
@@ -566,3 +566,35 @@ bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &Me
return not MessageFile.Failed();
}
/*}}}*/
+bool IsAssertedPubKeyAlgo(std::string const &pkstr, std::string const &option) /*{{{*/
+{
+ auto fullAss = APT::String::Startswith(option, "APT::Key") ? _config->Find(option) : option;
+ for (auto &ass : VectorizeString(fullAss, ','))
+ {
+ if (ass == pkstr)
+ return true;
+ // We only implement >= for rsa
+ if (APT::String::Startswith(ass, ">=rsa"))
+ {
+ if (not APT::String::Startswith(pkstr, "rsa"))
+ continue;
+ if (not std::all_of(ass.begin() + 5, ass.end(), isdigit))
+ return _error->Error("Unrecognized public key specification '%s' in option %s: expect only digits after >=rsa", ass.c_str(), option.c_str());
+
+ int assBits = std::stoi(ass.substr(5));
+ int pkBits = std::stoi(pkstr.substr(3));
+
+ if (pkBits >= assBits)
+ return true;
+
+ continue;
+ }
+ if (ass.empty())
+ return _error->Error("Empty item in public key assertion string option %s", option.c_str());
+ if (not std::all_of(ass.begin(), ass.end(), [](char c)
+ { return isalpha(c) || isdigit(c); }))
+ return _error->Error("Unrecognized public key specification '%s' in option %s", ass.c_str(), option.c_str());
+ }
+ return false;
+}
+ /*}}}*/
diff --git a/apt-pkg/contrib/gpgv.h b/apt-pkg/contrib/gpgv.h
index 1cabed4..1f3ef26 100644
--- a/apt-pkg/contrib/gpgv.h
+++ b/apt-pkg/contrib/gpgv.h
@@ -86,4 +86,5 @@ APT_PUBLIC bool SplitClearSignedFile(std::string const &InFile, FileFd * const C
*/
APT_PUBLIC bool OpenMaybeClearSignedFile(std::string const &ClearSignedFileName, FileFd &MessageFile);
+APT_PUBLIC bool IsAssertedPubKeyAlgo(std::string const &pkstr, std::string const &option);
#endif
diff --git a/apt-pkg/deb/deblistparser.cc b/apt-pkg/deb/deblistparser.cc
index 9177d54..13e8fd0 100644
--- a/apt-pkg/deb/deblistparser.cc
+++ b/apt-pkg/deb/deblistparser.cc
@@ -635,63 +635,60 @@ const char *debListParser::ParseDepends(const char *Start, const char *Stop,
// Skip whitespace
for (;I != Stop && isspace_ascii(*I) != 0; I++);
- if (unlikely(ParseArchFlags == true))
+ // Parse architecture restrictions
+ if (ParseArchFlags && I != Stop && *I == '[')
{
+ for (++I; I != Stop && isspace_ascii(*I) != 0 && *I != ']'; ++I);
+ // malformed
+ if (unlikely(I == Stop || *I == ']'))
+ return 0;
+
APT::CacheFilter::PackageArchitectureMatchesSpecification matchesArch(Arch, false);
- // Parse an architecture
- if (I != Stop && *I == '[')
+ bool Found = false;
+ bool NegArch = false;
+ while (I < Stop && *I != ']')
{
- ++I;
- // malformed
- if (unlikely(I == Stop))
- return 0;
-
+ // look for whitespace or ending ']' to end current Arch
const char *End = I;
- bool Found = false;
- bool NegArch = false;
- while (I != Stop)
- {
- // look for whitespace or ending ']'
- for (;End != Stop && !isspace_ascii(*End) && *End != ']'; ++End);
-
- if (unlikely(End == Stop))
- return 0;
-
- if (*I == '!')
- {
- NegArch = true;
- ++I;
- }
-
- std::string const arch(I, End);
- if (arch.empty() == false && matchesArch(arch.c_str()) == true)
- {
- Found = true;
- if (I[-1] != '!')
- NegArch = false;
- // we found a match, so fast-forward to the end of the wildcards
- for (; End != Stop && *End != ']'; ++End);
- }
+ for (;End < Stop && isspace_ascii(*End) == 0 && *End != ']'; ++End);
- if (*End++ == ']') {
- I = End;
- break;
- }
+ if (unlikely(End >= Stop))
+ return 0;
- I = End;
- for (;I != Stop && isspace_ascii(*I) != 0; I++);
+ bool CurNegArch = false;
+ if (*I == '!')
+ {
+ NegArch = true;
+ CurNegArch = true;
+ ++I;
}
- if (NegArch == true)
- Found = !Found;
+ if (I >= End)
+ return 0;
+ std::string const arch(I, End);
+ if (matchesArch(arch.c_str()))
+ {
+ Found = true;
+ if (not CurNegArch)
+ NegArch = false;
+ // we found a match, so fast-forward to the end of the wildcards
+ for (; End < Stop && *End != ']'; ++End);
+ }
+ else
+ for (; End < Stop && isspace_ascii(*End) != 0; ++End);
- if (Found == false)
- Package = ""; /* not for this arch */
+ I = End;
}
+ if (NegArch == true)
+ Found = not Found;
+
+ if (Found == false)
+ Package = ""; /* not for this arch */
+
// Skip whitespace
- for (;I != Stop && isspace_ascii(*I) != 0; I++);
+ for (++I; I < Stop && isspace_ascii(*I) != 0; ++I);
}
if (unlikely(ParseRestrictionsList == true))
diff --git a/apt-pkg/deb/debmetaindex.cc b/apt-pkg/deb/debmetaindex.cc
index 5158931..266313b 100644
--- a/apt-pkg/deb/debmetaindex.cc
+++ b/apt-pkg/deb/debmetaindex.cc
@@ -817,6 +817,8 @@ bool debReleaseIndex::SetSignedBy(std::string const &pSignedBy)
else
{
auto const normalSignedBy = NormalizeSignedBy(pSignedBy, true);
+ if (normalSignedBy.empty() == true)
+ return true;
if (normalSignedBy != SignedBy)
return _error->Error(_("Conflicting values set for option %s regarding source %s %s: %s != %s"), "Signed-By", URI.c_str(), Dist.c_str(), SignedBy.c_str(), normalSignedBy.c_str());
}
diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc
index 72cbf8d..4e8f522 100644
--- a/apt-pkg/depcache.cc
+++ b/apt-pkg/depcache.cc
@@ -2455,6 +2455,25 @@ static bool MarkPackage(pkgCache::PkgIterator const &Pkg,
if (not unsatisfied_choice)
fullyExplored[T->ID] = true;
+
+ // do not follow newly installed providers if we have already installed providers
+ if (providers_by_source.size() >= 2)
+ {
+ if (std::any_of(providers_by_source.begin(), providers_by_source.end(), [](auto const PV) {
+ return std::any_of(PV.second.begin(), PV.second.end(), [](auto const &Prv) {
+ auto const PP = Prv.ParentPkg();
+ return not PP.end() && PP->CurrentVer != 0;
+ });}))
+ {
+ for (auto &providers : providers_by_source)
+ providers.second.erase(std::remove_if(providers.second.begin(), providers.second.end(),
+ [](auto const &Prv) {
+ auto const PP = Prv.ParentPkg();
+ return not PP.end() && PP->CurrentVer == 0;
+ }), providers.second.end());
+ }
+ }
+
for (auto const &providers : providers_by_source)
{
for (auto const &PV : providers.second)
diff --git a/apt-pkg/edsp.cc b/apt-pkg/edsp.cc
index 5894008..0ffde46 100644
--- a/apt-pkg/edsp.cc
+++ b/apt-pkg/edsp.cc
@@ -770,13 +770,22 @@ bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
{
APT::Solver s(Cache.GetCache(), Cache.GetPolicy());
FileFd output;
- if (not s.FromDepCache(Cache))
- return false;
- if (not s.Solve())
- return false;
- if (not s.ToDepCache(Cache))
- return false;
- return true;
+ bool res = true;
+ if (Progress != NULL)
+ Progress->OverallProgress(0, 100, 1, _config->FindB("APT::Solver::Upgrade") ? _("Calculating upgrade") : _("Solving dependencies"));
+ if (res && not s.FromDepCache(Cache))
+ res = false;
+ if (Progress != NULL)
+ Progress->Progress(10);
+ if (res && not s.Solve())
+ res = false;
+ if (Progress != NULL)
+ Progress->Progress(90);
+ if (res && not s.ToDepCache(Cache))
+ res = false;
+ if (Progress != NULL)
+ Progress->Done();
+ return res;
}
if (strcmp(solver, "internal") == 0)
{
diff --git a/apt-pkg/edsp/edsplistparser.cc b/apt-pkg/edsp/edsplistparser.cc
index 5419069..183ace6 100644
--- a/apt-pkg/edsp/edsplistparser.cc
+++ b/apt-pkg/edsp/edsplistparser.cc
@@ -44,7 +44,20 @@ edspListParser::edspListParser(FileFd * const File) : edspLikeListParser(File)
bool edspLikeListParser::NewVersion(pkgCache::VerIterator &Ver)
{
_system->SetVersionMapping(Ver->ID, Section.FindI("APT-ID", Ver->ID));
- return debListParser::NewVersion(Ver);
+ if (not debListParser::NewVersion(Ver))
+ return false;
+
+ // Patch up the source version, it is stored in the Source-Version field in EDSP.
+ if (APT::StringView version = Section.Find(pkgTagSection::Key::Source_Version); not version.empty())
+ {
+ if (version != Ver.VerStr())
+ {
+ map_stringitem_t const idx = StoreString(pkgCacheGenerator::VERSIONNUMBER, version);
+ Ver->SourceVerStr = idx;
+ }
+ }
+
+ return true;
}
/*}}}*/
// ListParser::Description - Return the description string /*{{{*/
diff --git a/apt-pkg/init.cc b/apt-pkg/init.cc
index 487f94f..d71d954 100644
--- a/apt-pkg/init.cc
+++ b/apt-pkg/init.cc
@@ -131,7 +131,9 @@ bool pkgInitConfig(Configuration &Cnf)
Cnf.Set("APT::Build-Essential::", "build-essential");
Cnf.CndSet("APT::Install-Recommends", true);
Cnf.CndSet("APT::Install-Suggests", false);
- Cnf.CndSet("APT::Key::Assert-Pubkey-Algo", ">=rsa2048,ed25519,ed448");
+ Cnf.CndSet("APT::Key::Assert-Pubkey-Algo", ">=rsa2048,ed25519,ed448,nistp256,nistp384,nistp512,brainpoolP256r1,brainpoolP320r1,brainpoolP384r1,brainpoolP512r1,secp256k1");
+ Cnf.CndSet("APT::Key::Assert-Pubkey-Algo::Next", ">=rsa2048,ed25519,ed448,nistp256,nistp384,nistp512");
+ Cnf.CndSet("APT::Key::Assert-Pubkey-Algo::Future", ">=rsa3072,ed25519,ed448");
Cnf.CndSet("Dir","/");
// State
diff --git a/apt-pkg/pkgcachegen.cc b/apt-pkg/pkgcachegen.cc
index 5047561..981d360 100644
--- a/apt-pkg/pkgcachegen.cc
+++ b/apt-pkg/pkgcachegen.cc
@@ -1637,6 +1637,10 @@ static DynamicMMap* CreateDynamicMMap(FileFd * const CacheF, unsigned long Flags
static bool writeBackMMapToFile(pkgCacheGenerator * const Gen, DynamicMMap * const Map,
std::string const &FileName)
{
+ // Do not write the file back to /dev/null or try to change its mode...
+ if (FileName == "/dev/null")
+ return true;
+
FileFd SCacheF(FileName, FileFd::WriteAtomic);
if (SCacheF.IsOpen() == false || SCacheF.Failed())
return false;
diff --git a/apt-pkg/solver3.cc b/apt-pkg/solver3.cc
index d43bd5b..2ba6f60 100644
--- a/apt-pkg/solver3.cc
+++ b/apt-pkg/solver3.cc
@@ -26,11 +26,12 @@
#include <sstream>
// FIXME: Helpers stolen from DepCache, please give them back.
-struct CompareProviders3 /*{{{*/
+struct APT::Solver::CompareProviders3 /*{{{*/
{
pkgCache &Cache;
pkgDepCache::Policy &Policy;
pkgCache::PkgIterator const Pkg;
+ APT::Solver &Solver;
bool upgrade{_config->FindB("APT::Solver::Upgrade", false)};
bool operator()(pkgCache::Version *AV, pkgCache::Version *BV)
@@ -46,13 +47,26 @@ struct CompareProviders3 /*{{{*/
{
if (AV == BV)
return false;
- if (not upgrade && A->CurrentVer != 0 && A.CurrentVer() == AV)
- return true;
+ // The current version should win, unless we are upgrading and the other is the
+ // candidate.
+ // If AV is the current version, AV only wins on upgrades if BV is not the candidate.
+ if (A.CurrentVer() == AV)
+ return upgrade ? Policy.GetCandidateVer(A) != BV : true;
+ // If BV is the current version, AV only wins on upgrades if it is the candidate.
+ if (A.CurrentVer() == BV)
+ return upgrade ? Policy.GetCandidateVer(A) == AV : false;
+ // If neither are the current version, order them by priority.
if (Policy.GetPriority(AV) < Policy.GetPriority(BV))
return false;
return _system->VS->CmpVersion(AV.VerStr(), BV.VerStr()) > 0;
}
+ // Try obsolete choices only after exhausting non-obsolete choices such that we install
+ // packages replacing them and don't keep back upgrades depending on the replacement to
+ // keep the obsolete package installed.
+ if (upgrade)
+ if (auto obsoleteA = Solver.Obsolete(A), obsoleteB = Solver.Obsolete(B); obsoleteA != obsoleteB)
+ return obsoleteB;
// Prefer MA:same packages if other architectures for it are installed
if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same ||
(BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same)
@@ -141,8 +155,8 @@ class DefaultRootSetFunc2 : public pkgDepCache::DefaultRootSetFunc
std::unique_ptr<APT::CacheFilter::Matcher> Kernels;
public:
- DefaultRootSetFunc2(pkgCache *cache) : Kernels(APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(cache)){};
- virtual ~DefaultRootSetFunc2(){};
+ DefaultRootSetFunc2(pkgCache *cache) : Kernels(APT::KernelAutoRemoveHelper::GetProtectedKernelsFilter(cache)) {};
+ virtual ~DefaultRootSetFunc2() {};
bool InRootSet(const pkgCache::PkgIterator &pkg) APT_OVERRIDE { return pkg.end() == false && ((*Kernels)(pkg) || DefaultRootSetFunc::InRootSet(pkg)); };
}; // FIXME: DEDUP with pkgDepCache.
@@ -151,8 +165,9 @@ class DefaultRootSetFunc2 : public pkgDepCache::DefaultRootSetFunc
APT::Solver::Solver(pkgCache &cache, pkgDepCache::Policy &policy)
: cache(cache),
policy(policy),
- pkgStates{cache.Head().PackageCount},
- verStates{cache.Head().VersionCount}
+ pkgStates(cache.Head().PackageCount),
+ verStates(cache.Head().VersionCount),
+ pkgObsolete(cache.Head().PackageCount)
{
static_assert(sizeof(APT::Solver::State<pkgCache::PkgIterator>) == 3 * sizeof(int));
static_assert(sizeof(APT::Solver::State<pkgCache::VerIterator>) == 3 * sizeof(int));
@@ -163,31 +178,15 @@ APT::Solver::Solver(pkgCache &cache, pkgDepCache::Policy &policy)
// This function determines if a work item is less important than another.
bool APT::Solver::Work::operator<(APT::Solver::Work const &b) const
{
- if (optional && b.optional && reason.empty() && b.reason.empty() && upgrade != b.upgrade)
- {
- // Assuming we have libfoo-dev=5.1 Depends libfoo5.1-dev upgrade to libfoo-dev=5.3 Depends libfoo5.3-dev,
- // We schedule libfoo-dev=5.3|libfoo-dev=5.1, libfoo5.1-dev. The latter would be resolved first, resulting
- // in libfoo-dev being kept back.
- //
- // However, if we schedule not libfoo5.1-dev but bar Recommends libfoo5.1-dev, we should not be breaking that
- // Recommends, hence we need to ensure that if we order an upgrade before an optional package that this optional
- // package was a top level package, i.e. b.reason is empty (or our reason in the reverse case).
- //
- // So if we are the upgrade, and b also Depends on one of our versions, we need to satisfy b after we
- // have scheduled the upgrade.
- if (upgrade)
- return std::any_of(b.solutions.begin(), b.solutions.end(), [this](auto bsol) -> bool
- { return std::find(solutions.begin(), solutions.end(), bsol) != solutions.end(); });
- else
- return std::any_of(solutions.begin(), solutions.end(), [b](auto sol) -> bool
- { return std::find(b.solutions.begin(), b.solutions.end(), sol) != b.solutions.end(); });
- }
+ if ((not optional && size < 2) != (not b.optional && b.size < 2))
+ return not b.optional && b.size < 2;
+ if (group != b.group)
+ return group > b.group;
+ if (optional && b.optional && reason.empty() != b.reason.empty())
+ return reason.empty();
// An optional item is less important than a required one.
if (optional != b.optional)
return optional;
- // More solutions to explore are more expensive.
- if (size != b.size)
- return size > b.size;
// We enqueue common dependencies at the package level to avoid choosing versions, so let's solve package items first,
// this improves the implication graph as it now tells you that common dependencies were installed by the package.
if (reason.Pkg() != b.reason.Pkg())
@@ -202,11 +201,11 @@ void APT::Solver::Work::Dump(pkgCache &cache)
std::cerr << "Dirty ";
if (optional)
std::cerr << "Optional ";
- std::cerr << "Item (" << size << "@" << depth << (upgrade ? "u" : "") << ") ";
- if (auto Pkg = reason.Pkg(); Pkg != 0)
- std::cerr << pkgCache::PkgIterator(cache, cache.PkgP + Pkg).FullName();
- if (auto Ver = reason.Ver(); Ver != 0)
- std::cerr << pkgCache::VerIterator(cache, cache.VerP + Ver).ParentPkg().FullName() << "=" << pkgCache::VerIterator(cache, cache.VerP + Ver).VerStr();
+ std::cerr << "Item (" << ssize_t(size <= solutions.size() ? size : -1) << "@" << depth << (upgrade ? "u" : "") << ") ";
+ if (auto Pkg = reason.Pkg(cache); not Pkg.end())
+ std::cerr << Pkg.FullName();
+ if (auto Ver = reason.Ver(cache); not Ver.end())
+ std::cerr << Ver.ParentPkg().FullName() << "=" << Ver.VerStr();
std::cerr << " -> ";
for (auto sol : solutions)
{
@@ -222,7 +221,7 @@ std::string APT::Solver::WhyStr(Reason reason)
while (not reason.empty())
{
- if (auto Pkg = pkgCache::PkgIterator(cache, cache.PkgP + reason.Pkg()); not Pkg.end())
+ if (auto Pkg = reason.Pkg(cache); not Pkg.end())
{
if ((*this)[Pkg].decision == Decision::MUSTNOT)
out.push_back(std::string("not ") + Pkg.FullName());
@@ -230,7 +229,7 @@ std::string APT::Solver::WhyStr(Reason reason)
out.push_back(Pkg.FullName());
reason = (*this)[Pkg].reason;
}
- if (auto Ver = pkgCache::VerIterator(cache, cache.VerP + reason.Ver()); not Ver.end())
+ if (auto Ver = reason.Ver(cache); not Ver.end())
{
if ((*this)[Ver].decision == Decision::MUSTNOT)
out.push_back(std::string("not ") + Ver.ParentPkg().FullName() + "=" + Ver.VerStr());
@@ -248,7 +247,66 @@ std::string APT::Solver::WhyStr(Reason reason)
return outstr;
}
-bool APT::Solver::Install(pkgCache::PkgIterator Pkg, Reason reason)
+// This is essentially asking whether any other binary in the source package has a higher candidate
+// version. This pretends that each package is installed at the same source version as the package
+// under consideration.
+bool APT::Solver::ObsoletedByNewerSourceVersion(pkgCache::VerIterator cand) const
+{
+ const auto pkg = cand.ParentPkg();
+ const int candPriority = policy.GetPriority(cand);
+
+ for (auto ver = cand.Cache()->FindGrp(cand.SourcePkgName()).VersionsInSource(); not ver.end(); ver = ver.NextInSource())
+ {
+ // We are only interested in other packages in the same source package; built for the same architecture.
+ if (ver->ParentPkg == cand->ParentPkg || ver.ParentPkg()->Arch != cand.ParentPkg()->Arch || cache.VS->CmpVersion(ver.SourceVerStr(), cand.SourceVerStr()) <= 0)
+ continue;
+
+ // We also take equal priority here, given that we have a higher version
+ const int priority = policy.GetPriority(ver, true);
+ if (priority == 0 || priority < candPriority)
+ continue;
+
+ pkgObsolete[pkg->ID] = 2;
+ if (debug >= 3)
+ std::cerr << "Obsolete: " << cand.ParentPkg().FullName() << "=" << cand.VerStr() << " due to " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << "\n";
+ return true;
+ }
+
+ return false;
+}
+
+bool APT::Solver::Obsolete(pkgCache::PkgIterator pkg) const
+{
+ if (pkgObsolete[pkg->ID] != 0)
+ return pkgObsolete[pkg->ID] == 2;
+
+ auto ver = policy.GetCandidateVer(pkg);
+
+ if (ver.end() && not StrictPinning)
+ ver = pkg.VersionList();
+ if (ver.end())
+ {
+ std::cerr << "Obsolete: " << pkg.FullName() << " - not installable\n";
+ pkgObsolete[pkg->ID] = 2;
+ return true;
+ }
+
+ if (ObsoletedByNewerSourceVersion(ver))
+ return true;
+
+ for (auto file = ver.FileList(); !file.end(); file++)
+ if ((file.File()->Flags & pkgCache::Flag::NotSource) == 0)
+ {
+ pkgObsolete[pkg->ID] = 1;
+ return false;
+ }
+ if (debug >= 3)
+ std::cerr << "Obsolete: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << " - not installable\n";
+ pkgObsolete[pkg->ID] = 2;
+ return true;
+}
+
+bool APT::Solver::Install(pkgCache::PkgIterator Pkg, Reason reason, Group group)
{
if ((*this)[Pkg].decision == Decision::MUST)
return true;
@@ -274,19 +332,19 @@ bool APT::Solver::Install(pkgCache::PkgIterator Pkg, Reason reason)
// Note decision
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Install:" << Pkg.FullName() << " (" << WhyStr(reason) << ")\n";
- (*this)[Pkg] = {reason, depth(), Decision::MUST,};
+ (*this)[Pkg] = {reason, depth(), Decision::MUST};
// Insert the work item.
- Work workItem{Reason(Pkg), depth()};
+ Work workItem{Reason(Pkg), depth(), group};
for (auto ver = Pkg.VersionList(); not ver.end(); ver++)
if (IsAllowedVersion(ver))
workItem.solutions.push_back(ver);
- std::sort(workItem.solutions.begin(), workItem.solutions.end(), CompareProviders3{cache, policy, Pkg});
+ std::stable_sort(workItem.solutions.begin(), workItem.solutions.end(), CompareProviders3{cache, policy, Pkg, *this});
assert(workItem.solutions.size() > 0);
if (workItem.solutions.size() > 1 || workItem.optional)
AddWork(std::move(workItem));
- else if (not Install(pkgCache::VerIterator(cache, workItem.solutions[0]), workItem.reason))
+ else if (not Install(pkgCache::VerIterator(cache, workItem.solutions[0]), workItem.reason, group))
return false;
if (not EnqueueCommonDependencies(Pkg))
@@ -295,7 +353,7 @@ bool APT::Solver::Install(pkgCache::PkgIterator Pkg, Reason reason)
return true;
}
-bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason)
+bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason, Group group)
{
if ((*this)[Ver].decision == Decision::MUST)
return true;
@@ -324,13 +382,13 @@ bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason)
// Note decision
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Install:" << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " (" << WhyStr(reason) << ")\n";
- (*this)[Ver] = {reason, depth(), Decision::MUST,};
+ (*this)[Ver] = {reason, depth(), Decision::MUST};
if ((*this)[Ver.ParentPkg()].decision != Decision::MUST)
- (*this)[Ver.ParentPkg()] = {Reason(Ver), depth(), Decision::MUST,};
+ (*this)[Ver.ParentPkg()] = {Reason(Ver), depth(), Decision::MUST};
for (auto OV = Ver.ParentPkg().VersionList(); not OV.end(); ++OV)
{
- if (OV != Ver && not Reject(OV, Reason(Ver)))
+ if (OV != Ver && not Reject(OV, Reason(Ver), group))
return false;
}
@@ -341,8 +399,6 @@ bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason)
pkgCache::DepIterator end;
dep.GlobOr(start, end); // advances dep
- if (not policy.IsImportantDep(start))
- continue;
if (not EnqueueOrGroup(start, end, Reason(Ver)))
return false;
}
@@ -350,7 +406,7 @@ bool APT::Solver::Install(pkgCache::VerIterator Ver, Reason reason)
return true;
}
-bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason)
+bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason, Group group)
{
if ((*this)[Pkg].decision == Decision::MUSTNOT)
return true;
@@ -365,9 +421,9 @@ bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason)
// Reject the package and its versions.
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Reject:" << Pkg.FullName() << " (" << WhyStr(reason) << ")\n";
- (*this)[Pkg] = {reason, depth(), Decision::MUSTNOT,};
+ (*this)[Pkg] = {reason, depth(), Decision::MUSTNOT};
for (auto ver = Pkg.VersionList(); not ver.end(); ver++)
- if (not Reject(ver, Reason(Pkg)))
+ if (not Reject(ver, Reason(Pkg), group))
return false;
needsRescore = true;
@@ -376,8 +432,10 @@ bool APT::Solver::Reject(pkgCache::PkgIterator Pkg, Reason reason)
}
// \brief Do not install this version
-bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason)
+bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason, Group group)
{
+ (void)group;
+
if ((*this)[Ver].decision == Decision::MUSTNOT)
return true;
@@ -391,7 +449,7 @@ bool APT::Solver::Reject(pkgCache::VerIterator Ver, Reason reason)
// Mark the package as rejected and propagate up as needed.
if (unlikely(debug >= 1))
std::cerr << "[" << depth() << "] Reject:" << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " (" << WhyStr(reason) << ")\n";
- (*this)[Ver] = {reason, depth(), Decision::MUSTNOT,};
+ (*this)[Ver] = {reason, depth(), Decision::MUSTNOT};
if (auto pkg = Ver.ParentPkg(); (*this)[pkg].decision != Decision::MUSTNOT)
{
bool anyInstallable = false;
@@ -445,8 +503,6 @@ bool APT::Solver::EnqueueCommonDependencies(pkgCache::PkgIterator Pkg)
}
if (not allHaveDep)
continue;
- if (not policy.IsImportantDep(start))
- continue;
if (not EnqueueOrGroup(start, end, Reason(Pkg)))
return false;
}
@@ -460,10 +516,17 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
auto Ver = start.ParentVer();
auto fixPolicy = _config->FindB("APT::Get::Fix-Policy-Broken");
+ // Non-important dependencies can only be installed if they are currently satisfied, see the check further
+ // below once we have calculated all possible solutions.
+ if (start.ParentPkg()->CurrentVer == 0 && not policy.IsImportantDep(start))
+ return true;
+ // Replaces and Enhances are not a real dependency.
+ if (start->Type == pkgCache::Dep::Replaces || start->Type == pkgCache::Dep::Enhances)
+ return true;
if (unlikely(debug >= 3))
std::cerr << "Found dependency critical " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " -> " << start.TargetPkg().FullName() << "\n";
- Work workItem{reason, depth(), not start.IsCritical() /* optional */};
+ Work workItem{reason, depth(), Group::Satisfy, not start.IsCritical() /* optional */};
do
{
@@ -479,7 +542,7 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
if (unlikely(debug >= 3))
std::cerr << "Reject: " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " -> " << tgti.ParentPkg().FullName() << "=" << tgti.VerStr() << "\n";
// FIXME: We should be collecting these and marking the heap only once.
- if (not Reject(pkgCache::VerIterator(cache, *tgt), Reason(Ver)))
+ if (not Reject(pkgCache::VerIterator(cache, *tgt), Reason(Ver), Group::HoldOrDelete))
return false;
}
else
@@ -496,7 +559,7 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
// FIXME: This is not really true, though, we should fix the CompareProviders to ignore the
// installed state
if (fixPolicy)
- std::sort(workItem.solutions.begin() + begin, workItem.solutions.end(), CompareProviders3{cache, policy, TgtPkg});
+ std::stable_sort(workItem.solutions.begin() + begin, workItem.solutions.end(), CompareProviders3{cache, policy, TgtPkg, *this});
if (start == end)
break;
@@ -504,40 +567,73 @@ bool APT::Solver::EnqueueOrGroup(pkgCache::DepIterator start, pkgCache::DepItera
} while (1);
if (not fixPolicy)
- std::sort(workItem.solutions.begin(), workItem.solutions.end(), CompareProviders3{cache, policy, TgtPkg});
-
- // Figure out if the reason is installed
- bool reasonInstalled = false;
- if (auto p = workItem.reason.Pkg())
- reasonInstalled = pkgCache::PkgIterator(cache, cache.PkgP + p)->CurrentVer != 0;
- else if (auto v = workItem.reason.Ver())
- reasonInstalled = pkgCache::VerIterator(cache, cache.VerP + v).ParentPkg()->CurrentVer != 0;
-
+ 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; }))
+ 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()); }))
+ 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 && reasonInstalled && not fixPolicy &&
- not std::any_of(workItem.solutions.begin(), workItem.solutions.end(), [this](auto ver)
- { return pkgCache::VerIterator(cache, ver).ParentPkg()->CurrentVer != 0; }))
+ if (workItem.optional && start.ParentPkg()->CurrentVer)
{
- if (unlikely(debug >= 3))
+ bool important = policy.IsImportantDep(start);
+ bool newOptional = true;
+ bool wasImportant = false;
+ for (auto D = start.ParentPkg().CurrentVer().DependsList(); not D.end(); D++)
+ 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 != 0; });
+
+ if (important && wasImportant && not newOptional && not satisfied)
{
- std::cerr << "Ignoring currently unsatisfied Recommends ";
- workItem.Dump(cache);
- std::cerr << "\n";
+ if (unlikely(debug >= 3))
+ {
+ std::cerr << "Ignoring unsatisfied Recommends ";
+ workItem.Dump(cache);
+ std::cerr << "\n";
+ }
+ return true;
+ }
+ if (not important && not wasImportant && not newOptional && satisfied)
+ {
+ if (unlikely(debug >= 3))
+ {
+ std::cerr << "Promoting satisfied Suggests to Recommends: ";
+ workItem.Dump(cache);
+ std::cerr << "\n";
+ }
+ important = true;
+ }
+ if (not important)
+ {
+ if (unlikely(debug >= 3))
+ {
+ std::cerr << "Ignoring Suggests ";
+ workItem.Dump(cache);
+ std::cerr << "\n";
+ }
+ return true;
}
- return true;
}
+ else if (workItem.optional && start.ParentPkg()->CurrentVer == 0)
+ workItem.group = Group::NewUnsatRecommends;
+
if (not workItem.solutions.empty())
{
- // std::sort(workItem.solutions.begin(), workItem.solutions.end(), CompareProviders3{cache, TgtPkg});
+ // std::stable_sort(workItem.solutions.begin(), workItem.solutions.end(), CompareProviders3{cache, TgtPkg});
if (unlikely(debug >= 3 && workItem.optional))
{
- std::cerr << "Enqueuing currently satisfied Recommends ";
+ std::cerr << "Enqueuing Recommends ";
workItem.Dump(cache);
std::cerr << "\n";
}
if (workItem.optional || workItem.solutions.size() > 1)
AddWork(std::move(workItem));
- else if (not Install(pkgCache::VerIterator(cache, workItem.solutions[0]), reason))
+ else if (not Install(pkgCache::VerIterator(cache, workItem.solutions[0]), reason, workItem.group))
return false;
}
else if (start.IsCritical() && not start.IsNegative())
@@ -608,7 +704,7 @@ bool APT::Solver::RejectReverseDependencies(pkgCache::VerIterator Ver)
if (unlikely(debug >= 3))
std::cerr << "Propagate NOT " << Ver.ParentPkg().FullName() << "=" << Ver.VerStr() << " to " << RDV.ParentPkg().FullName() << "=" << RDV.VerStr() << " for dependency group starting with" << start.TargetPkg().FullName() << std::endl;
- if (not Reject(RDV, Reason(Ver)))
+ if (not Reject(RDV, Reason(Ver), Group::HoldOrDelete))
return false;
}
return true;
@@ -675,7 +771,7 @@ bool APT::Solver::Pop()
if (w.depth > depth) // Deeper decision level is no longer valid.
return true;
// This item is still solved, keep it on the solved list.
- if (not std::any_of(w.solutions.begin(), w.solutions.end(), [this](auto ver)
+ if (std::any_of(w.solutions.begin(), w.solutions.end(), [this](auto ver)
{ return (*this)[ver].decision == Decision::MUST; }))
return false;
// We are not longer solved, move it back to work.
@@ -703,7 +799,7 @@ bool APT::Solver::Pop()
assert(w.choice != nullptr);
// FIXME: There should be a reason!
- if (not Reject(pkgCache::VerIterator(cache, w.choice), {}))
+ if (not Reject(pkgCache::VerIterator(cache, w.choice), {}, Group::HoldOrDelete))
return false;
w.choice = nullptr;
@@ -822,7 +918,7 @@ bool APT::Solver::Solve()
}
if (unlikely(debug >= 3))
std::cerr << "(try it: " << ver.ParentPkg().FullName() << "=" << ver.VerStr() << ")\n";
- if (not Install(pkgCache::VerIterator(cache, ver), item.reason) && not Pop())
+ if (not Install(pkgCache::VerIterator(cache, ver), item.reason, Group::Satisfy) && not Pop())
return false;
foundSolution = true;
break;
@@ -850,9 +946,7 @@ bool APT::Solver::Solve()
// \brief Apply the selections from the dep cache to the solver
bool APT::Solver::FromDepCache(pkgDepCache &depcache)
{
- bool KeepAuto = not _config->FindB("APT::Get::AutomaticRemove");
- bool AllowRemove = _config->FindB("APT::Solver::Remove", true);
- bool AllowInstall = _config->FindB("APT::Solver::Install", true);
+ bool AllowRemoveManual = AllowRemove && _config->FindB("APT::Solver::RemoveManual", false);
DefaultRootSetFunc2 rootSet(&cache);
for (auto P = cache.PkgBegin(); not P.end(); P++)
@@ -861,64 +955,60 @@ bool APT::Solver::FromDepCache(pkgDepCache &depcache)
continue;
auto state = depcache[P];
- auto maybeInstall = state.Install() || (state.Keep() && P->CurrentVer);
- auto reject = state.Delete() || (depcache[P].Keep() && not P->CurrentVer && depcache[P].Protect());
if (P->SelectedState == pkgCache::State::Hold && not state.Protect())
{
if (unlikely(debug >= 1))
std::cerr << "Hold " << P.FullName() << "\n";
- if (P->CurrentVer ? not Install(P.CurrentVer(), {}) : not Reject(P, {}))
+ if (P->CurrentVer ? not Install(P.CurrentVer(), {}, Group::HoldOrDelete) : not Reject(P, {}, Group::HoldOrDelete))
return false;
}
- else if (reject)
+ else if (state.Delete() // Normal delete request.
+ || (not P->CurrentVer && state.Keep() && state.Protect()) // Delete request of not installed package.
+ || (not P->CurrentVer && state.Keep() && not AllowInstall) // New package installs not allowed.
+ )
{
if (unlikely(debug >= 1))
std::cerr << "Delete " << P.FullName() << "\n";
- if (!Reject(P, {}))
- return false;
- }
- else if (maybeInstall && P->Flags & (pkgCache::Flag::Essential | pkgCache::Flag::Important))
- {
- if (unlikely(debug >= 1))
- std::cerr << "ESSENTIAL " << P.FullName() << "\n";
- if (depcache[P].Keep() ? not Install(P, {}) : not Install(depcache.GetCandidateVersion(P), {}))
+ if (!Reject(P, {}, Group::HoldOrDelete))
return false;
}
- else if (maybeInstall && not(depcache[P].Flags & pkgCache::Flag::Auto))
- {
- if (unlikely(debug >= 1))
- std::cerr << "MANUAL " << P.FullName() << "\n";
- if (depcache[P].Keep() ? not Install(P, {}) : not Install(depcache.GetCandidateVersion(P), {}))
- return false;
- }
- else if (maybeInstall && (KeepAuto || rootSet.InRootSet(P)) && (depcache[P].Flags & pkgCache::Flag::Auto))
+ else if (state.Install() || (state.Keep() && P->CurrentVer))
{
+ auto isEssential = P->Flags & (pkgCache::Flag::Essential | pkgCache::Flag::Important);
+ auto isAuto = (depcache[P].Flags & pkgCache::Flag::Auto);
+ auto isOptional = ((isAuto && AllowRemove) || AllowRemoveManual) && not isEssential && not depcache[P].Protect();
+ auto Root = rootSet.InRootSet(P);
auto Upgrade = depcache.GetCandidateVersion(P) != P.CurrentVer();
+ auto Group = isAuto ? (Upgrade ? Group::UpgradeAuto : Group::KeepAuto)
+ : (Upgrade ? Group::UpgradeManual : Group::InstallManual);
+
+ if (isAuto && not depcache[P].Protect() && not isEssential && not KeepAuto && not rootSet.InRootSet(P))
+ {
+ if (unlikely(debug >= 1))
+ std::cerr << "Ignore automatic install " << P.FullName() << " (" << (isEssential ? "E" : "") << (isAuto ? "M" : "") << (Root ? "R" : "") << ")"
+ << "\n";
+ continue;
+ }
if (unlikely(debug >= 1))
- std::cerr << "AUTOMATIC " << P.FullName() << (Upgrade ? " - upgrade" : "") << "\n";
+ std::cerr << "Install " << P.FullName() << " (" << (isEssential ? "E" : "") << (isAuto ? "M" : "") << (Root ? "R" : "") << ")"
+ << "\n";
- if (not AllowRemove)
+ if (not isOptional)
{
- if (depcache[P].Keep() ? not Install(P, {}) : not Install(depcache.GetCandidateVersion(P), {}))
+ // Pre-empt the non-optional requests, as we don't want to queue them, we can just "unit propagate" here.
+ if (depcache[P].Keep() ? not Install(P, {}, Group) : not Install(depcache.GetCandidateVersion(P), {}, Group))
return false;
}
else
{
- Work w{Reason(), depth(), true, Upgrade};
+ Work w{Reason(), depth(), Group, isOptional, Upgrade};
for (auto V = P.VersionList(); not V.end(); ++V)
if (IsAllowedVersion(V))
w.solutions.push_back(V);
- std::sort(w.solutions.begin(), w.solutions.end(), CompareProviders3{cache, policy, P});
+ std::stable_sort(w.solutions.begin(), w.solutions.end(), CompareProviders3{cache, policy, P, *this});
AddWork(std::move(w));
}
}
- else if (P->CurrentVer == 0 && not AllowInstall)
- {
- if (unlikely(debug >= 1))
- std::cerr << "NOT ALLOWING INSTALL OF " << P.FullName() << "\n";
- if (not Reject(P, {}))
- return false;
- }
}
return true;
@@ -929,6 +1019,8 @@ bool APT::Solver::ToDepCache(pkgDepCache &depcache)
pkgDepCache::ActionGroup group(depcache);
for (auto P = cache.PkgBegin(); not P.end(); P++)
{
+ depcache[P].Marked = 0;
+ depcache[P].Garbage = 0;
if ((*this)[P].decision == Decision::MUST)
{
for (auto V = P.VersionList(); not V.end(); V++)
@@ -949,7 +1041,7 @@ bool APT::Solver::ToDepCache(pkgDepCache &depcache)
depcache[P].Marked = 1;
depcache[P].Garbage = 0;
}
- else if (P->CurrentVer)
+ else if (P->CurrentVer || depcache[P].Install())
{
depcache.MarkDelete(P, false, 0, (*this)[P].reason.empty());
depcache[P].Marked = 0;
diff --git a/apt-pkg/solver3.h b/apt-pkg/solver3.h
index cf2fb2e..33067a0 100644
--- a/apt-pkg/solver3.h
+++ b/apt-pkg/solver3.h
@@ -32,10 +32,46 @@ class Solver
enum class Decision : uint16_t;
enum class Hint : uint16_t;
struct Reason;
+ struct CompareProviders3;
template <typename T>
struct State;
struct Work;
+ // \brief Groups of works, these are ordered.
+ //
+ // Later items will be skipped if they are optional, or we will when backtracking,
+ // try a different choice for them.
+ enum class Group : uint8_t
+ {
+ HoldOrDelete,
+ NewUnsatRecommends,
+
+ // Satisfying dependencies on entirely new packages first is a good idea because
+ // it may contain replacement packages like libfoo1t64 whereas we later will see
+ // Depends: libfoo1 where libfoo1t64 Provides libfoo1 and we'd have to choose.
+ SatisfyNew,
+ Satisfy,
+ // On a similar note as for SatisfyNew, if the dependency contains obsolete packages
+ // try it last.
+ SatisfyObsolete,
+
+ // My intuition tells me that we should try to schedule upgrades first, then
+ // any non-obsolete installed packages, and only finally obsolete ones, such
+ // that newer packages guide resolution of dependencies for older ones, they
+ // may have more stringent dependencies, like a (>> 2) whereas an obsolete
+ // package may have a (>> 1), for example.
+ UpgradeManual,
+ InstallManual,
+ ObsoleteManual,
+
+ // Automatically installed packages must come last in the group, this allows
+ // us to see if they were installed as a dependency of a manually installed package,
+ // allowing a simple implementation of an autoremoval code.
+ UpgradeAuto,
+ KeepAuto,
+ ObsoleteAuto
+ };
+
// \brief Type to record depth at. This may very well be a 16-bit
// unsigned integer, then change Solver::State::Decision to be a
// uint16_t class enum as well to get a more compact space.
@@ -68,6 +104,10 @@ class Solver
return verStates[V->ID];
}
+ mutable std::vector<char> pkgObsolete;
+ bool Obsolete(pkgCache::PkgIterator pkg) const;
+ bool ObsoletedByNewerSourceVersion(pkgCache::VerIterator cand) const;
+
// \brief Heap of the remaining work.
//
// We are using an std::vector with std::make_heap(), std::push_heap(),
@@ -131,13 +171,13 @@ class Solver
Solver(pkgCache &Cache, pkgDepCache::Policy &Policy);
// \brief Mark the package for install. This is annoying as it incurs a decision
- bool Install(pkgCache::PkgIterator Pkg, Reason reason);
+ bool Install(pkgCache::PkgIterator Pkg, Reason reason, Group group);
// \brief Install a version.
- bool Install(pkgCache::VerIterator Ver, Reason reason);
+ bool Install(pkgCache::VerIterator Ver, Reason reason, Group group);
// \brief Do not install this package
- bool Reject(pkgCache::PkgIterator Pkg, Reason reason);
+ bool Reject(pkgCache::PkgIterator Pkg, Reason reason, Group group);
// \brief Do not install this version.
- bool Reject(pkgCache::VerIterator Ver, Reason reason);
+ bool Reject(pkgCache::VerIterator Ver, Reason reason, Group group);
// \brief Apply the selections from the dep cache to the solver
bool FromDepCache(pkgDepCache &depcache);
@@ -180,6 +220,16 @@ struct APT::Solver::Reason
{
return IsVersion ? map_pointer<pkgCache::Version>{(uint32_t)MapPtr} : 0;
}
+ // \brief Return the package iterator if storing a package, or an empty one
+ pkgCache::PkgIterator Pkg(pkgCache &cache) const
+ {
+ return IsVersion ? pkgCache::PkgIterator() : pkgCache::PkgIterator(cache, cache.PkgP + Pkg());
+ }
+ // \brief Return the version iterator if storing a package, or an empty end.
+ pkgCache::VerIterator Ver(pkgCache &cache) const
+ {
+ return IsVersion ? pkgCache::VerIterator(cache, cache.VerP + Ver()) : pkgCache::VerIterator();
+ }
// \brief Check if there is no reason.
bool empty() const
{
@@ -203,7 +253,8 @@ struct APT::Solver::Work
Reason reason;
// \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{};
@@ -228,7 +279,7 @@ struct APT::Solver::Work
// \brief Dump the work item to std::cerr
void Dump(pkgCache &cache);
- inline Work(Reason reason, depth_type depth, bool optional = false, bool upgrade = false) : reason(reason), depth(depth), size(0), optional(optional), upgrade(upgrade), dirty(false) {}
+ inline Work(Reason reason, depth_type depth, Group group, bool optional = false, bool upgrade = false) : reason(reason), depth(depth), group(group), size(0), optional(optional), upgrade(upgrade), dirty(false) {}
};
// \brief This essentially describes the install state in RFC2119 terms.
diff --git a/apt-pkg/tagfile-keys.list b/apt-pkg/tagfile-keys.list
index 4b57e46..d198ea0 100644
--- a/apt-pkg/tagfile-keys.list
+++ b/apt-pkg/tagfile-keys.list
@@ -80,3 +80,4 @@ Vcs-Mtn
Vcs-Svn
Version
### APPEND BELOW, sort in with next ABI break ###
+Source-Version