325 lines
10 KiB
C++
325 lines
10 KiB
C++
// Include Files /*{{{*/
|
|
#include <config.h>
|
|
|
|
#include <apt-pkg/algorithms.h>
|
|
#include <apt-pkg/aptconfiguration.h>
|
|
#include <apt-pkg/configuration.h>
|
|
#include <apt-pkg/depcache.h>
|
|
#include <apt-pkg/edsp.h>
|
|
#include <apt-pkg/error.h>
|
|
#include <apt-pkg/pkgcache.h>
|
|
#include <apt-pkg/progress.h>
|
|
#include <apt-pkg/strutl.h>
|
|
#include <apt-pkg/upgrade.h>
|
|
|
|
#include <random>
|
|
#include <string>
|
|
|
|
#include <apti18n.h>
|
|
/*}}}*/
|
|
|
|
// DistUpgrade - Distribution upgrade /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* This autoinstalls every package and then force installs every
|
|
pre-existing package. This creates the initial set of conditions which
|
|
most likely contain problems because too many things were installed.
|
|
|
|
The problem resolver is used to resolve the problems.
|
|
*/
|
|
static bool pkgDistUpgrade(pkgDepCache &Cache, OpProgress * const Progress)
|
|
{
|
|
std::string const solver = _config->Find("APT::Solver", "internal");
|
|
auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, EDSP::Request::UPGRADE_ALL, Progress);
|
|
if (solver != "internal")
|
|
return ret;
|
|
|
|
if (Progress != NULL)
|
|
Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
|
|
/* Upgrade all installed packages first without autoinst to help the resolver
|
|
in versioned or-groups to upgrade the old solver instead of installing
|
|
a new one (if the old solver is not the first one [anymore]) */
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (Cache.PhasingApplied(I))
|
|
continue;
|
|
if (I->CurrentVer != 0)
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(10);
|
|
|
|
/* Auto upgrade all installed packages, this provides the basis
|
|
for the installation */
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (Cache.PhasingApplied(I))
|
|
continue;
|
|
if (I->CurrentVer != 0)
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(50);
|
|
|
|
/* Now, install each essential package which is not installed
|
|
(and not provided by another package in the same name group) */
|
|
std::string essential = _config->Find("pkgCacheGen::Essential", "all");
|
|
if (essential == "all")
|
|
{
|
|
for (pkgCache::GrpIterator G = Cache.GrpBegin(); G.end() == false; ++G)
|
|
{
|
|
bool isEssential = false;
|
|
bool instEssential = false;
|
|
for (pkgCache::PkgIterator P = G.PackageList(); P.end() == false; P = G.NextPkg(P))
|
|
{
|
|
if ((P->Flags & pkgCache::Flag::Essential) != pkgCache::Flag::Essential)
|
|
continue;
|
|
isEssential = true;
|
|
if (Cache[P].Install() == true)
|
|
{
|
|
instEssential = true;
|
|
break;
|
|
}
|
|
}
|
|
if (isEssential == false || instEssential == true)
|
|
continue;
|
|
pkgCache::PkgIterator P = G.FindPreferredPkg();
|
|
if (Cache.PhasingApplied(P))
|
|
continue;
|
|
Cache.MarkInstall(P, true, 0, false);
|
|
}
|
|
}
|
|
else if (essential != "none")
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (Cache.PhasingApplied(I))
|
|
continue;
|
|
if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(55);
|
|
|
|
/* We do it again over all previously installed packages to force
|
|
conflict resolution on them all. */
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (Cache.PhasingApplied(I))
|
|
continue;
|
|
if (I->CurrentVer != 0)
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(65);
|
|
|
|
pkgProblemResolver Fix(&Cache);
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(95);
|
|
|
|
// Hold back held packages.
|
|
if (_config->FindB("APT::Ignore-Hold",false) == false)
|
|
{
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (I->SelectedState == pkgCache::State::Hold)
|
|
{
|
|
Fix.Protect(I);
|
|
Cache.MarkKeep(I, false, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool success = Fix.ResolveInternal(false);
|
|
if (success)
|
|
{
|
|
// Revert phased updates using keeps. An issue with ResolveByKeep is
|
|
// that it also keeps back packages due to (new) broken Recommends,
|
|
// even if Upgrade already decided this is fine, so we will mark all
|
|
// packages that dist-upgrade decided may have a broken policy as allowed
|
|
// to do so such that we do not keep them back again.
|
|
pkgProblemResolver FixPhasing(&Cache);
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
if (Cache[I].InstPolicyBroken())
|
|
FixPhasing.AllowBrokenPolicy(I);
|
|
FixPhasing.KeepPhasedUpdates();
|
|
success = FixPhasing.ResolveByKeepInternal();
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Done();
|
|
return success;
|
|
} /*}}}*/
|
|
// AllUpgradeNoNewPackages - Upgrade but no removals or new pkgs /*{{{*/
|
|
static bool pkgAllUpgradeNoNewPackages(pkgDepCache &Cache, OpProgress * const Progress)
|
|
{
|
|
std::string const solver = _config->Find("APT::Solver", "internal");
|
|
constexpr auto flags = EDSP::Request::UPGRADE_ALL | EDSP::Request::FORBID_NEW_INSTALL | EDSP::Request::FORBID_REMOVE;
|
|
auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, flags, Progress);
|
|
if (solver != "internal")
|
|
return ret;
|
|
|
|
if (Progress != NULL)
|
|
Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
pkgProblemResolver Fix(&Cache);
|
|
// Upgrade all installed packages
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (Cache[I].Install() == true)
|
|
Fix.Protect(I);
|
|
|
|
if (_config->FindB("APT::Ignore-Hold",false) == false)
|
|
if (I->SelectedState == pkgCache::State::Hold)
|
|
continue;
|
|
|
|
if (Cache.PhasingApplied(I))
|
|
continue;
|
|
|
|
if (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(50);
|
|
|
|
Fix.KeepPhasedUpdates();
|
|
|
|
// resolve remaining issues via keep
|
|
bool const success = Fix.ResolveByKeepInternal();
|
|
if (Progress != NULL)
|
|
Progress->Done();
|
|
return success;
|
|
}
|
|
/*}}}*/
|
|
// AllUpgradeWithNewInstalls - Upgrade + install new packages as needed /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* Right now the system must be consistent before this can be called.
|
|
* Upgrade as much as possible without deleting anything (useful for
|
|
* stable systems)
|
|
*/
|
|
static bool pkgAllUpgradeWithNewPackages(pkgDepCache &Cache, OpProgress * const Progress)
|
|
{
|
|
std::string const solver = _config->Find("APT::Solver", "internal");
|
|
constexpr auto flags = EDSP::Request::UPGRADE_ALL | EDSP::Request::FORBID_REMOVE;
|
|
auto const ret = EDSP::ResolveExternal(solver.c_str(), Cache, flags, Progress);
|
|
if (solver != "internal")
|
|
return ret;
|
|
|
|
if (Progress != NULL)
|
|
Progress->OverallProgress(0, 100, 1, _("Calculating upgrade"));
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
pkgProblemResolver Fix(&Cache);
|
|
|
|
// provide the initial set of stuff we want to upgrade by marking
|
|
// all upgradable packages for upgrade
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
if (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
|
|
{
|
|
if (_config->FindB("APT::Ignore-Hold",false) == false)
|
|
if (I->SelectedState == pkgCache::State::Hold)
|
|
continue;
|
|
if (Cache.PhasingApplied(I))
|
|
continue;
|
|
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
}
|
|
}
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(10);
|
|
|
|
// then let auto-install loose
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
if (Cache[I].Install())
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(50);
|
|
|
|
// ... but it may remove stuff, we need to clean up afterwards again
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
if (Cache[I].Delete() == true)
|
|
Cache.MarkKeep(I, false, false);
|
|
|
|
if (Progress != NULL)
|
|
Progress->Progress(60);
|
|
|
|
Fix.KeepPhasedUpdates();
|
|
|
|
// resolve remaining issues via keep
|
|
bool const success = Fix.ResolveByKeepInternal();
|
|
if (Progress != NULL)
|
|
Progress->Done();
|
|
return success;
|
|
}
|
|
/*}}}*/
|
|
// MinimizeUpgrade - Minimizes the set of packages to be upgraded /*{{{*/
|
|
// ---------------------------------------------------------------------
|
|
/* This simply goes over the entire set of packages and tries to keep
|
|
each package marked for upgrade. If a conflict is generated then
|
|
the package is restored. */
|
|
bool pkgMinimizeUpgrade(pkgDepCache &Cache)
|
|
{
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
|
|
if (Cache.BrokenCount() != 0)
|
|
return false;
|
|
|
|
// We loop for 10 tries to get the minimal set size.
|
|
bool Change = false;
|
|
unsigned int Count = 0;
|
|
do
|
|
{
|
|
Change = false;
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; ++I)
|
|
{
|
|
// Not interesting
|
|
if (Cache[I].Upgrade() == false || Cache[I].NewInstall() == true)
|
|
continue;
|
|
|
|
// Keep it and see if that is OK
|
|
Cache.MarkKeep(I, false, false);
|
|
if (Cache.BrokenCount() != 0)
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
else
|
|
{
|
|
// If keep didn't actually do anything then there was no change..
|
|
if (Cache[I].Upgrade() == false)
|
|
Change = true;
|
|
}
|
|
}
|
|
++Count;
|
|
}
|
|
while (Change == true && Count < 10);
|
|
|
|
if (Cache.BrokenCount() != 0)
|
|
return _error->Error("Internal Error in pkgMinimizeUpgrade");
|
|
|
|
return true;
|
|
}
|
|
/*}}}*/
|
|
// APT::Upgrade::Upgrade - Upgrade using a specific strategy /*{{{*/
|
|
bool APT::Upgrade::Upgrade(pkgDepCache &Cache, int mode, OpProgress * const Progress)
|
|
{
|
|
if (mode == ALLOW_EVERYTHING)
|
|
return pkgDistUpgrade(Cache, Progress);
|
|
else if ((mode & ~FORBID_REMOVE_PACKAGES) == 0)
|
|
return pkgAllUpgradeWithNewPackages(Cache, Progress);
|
|
else if ((mode & ~(FORBID_REMOVE_PACKAGES|FORBID_INSTALL_NEW_PACKAGES)) == 0)
|
|
return pkgAllUpgradeNoNewPackages(Cache, Progress);
|
|
else
|
|
_error->Error("pkgAllUpgrade called with unsupported mode %i", mode);
|
|
return false;
|
|
}
|
|
/*}}}*/
|