|
|
|
// -*- mode: cpp; mode: fold -*-
|
|
|
|
// Description /*{{{*/
|
|
|
|
// $Id: algorithms.cc,v 1.44 2002/11/28 18:49:16 jgg Exp $
|
|
|
|
/* ######################################################################
|
|
|
|
|
|
|
|
Algorithms - A set of misc algorithms
|
|
|
|
|
|
|
|
The pkgProblemResolver class has become insanely complex and
|
|
|
|
very sophisticated, it handles every test case I have thrown at it
|
|
|
|
to my satisfaction. Understanding exactly why all the steps the class
|
|
|
|
does are required is difficult and changing though not very risky
|
|
|
|
may result in other cases not working.
|
|
|
|
|
|
|
|
##################################################################### */
|
|
|
|
/*}}}*/
|
|
|
|
// Include Files /*{{{*/
|
|
|
|
#include <apt-pkg/algorithms.h>
|
|
|
|
#include <apt-pkg/error.h>
|
|
|
|
#include <apt-pkg/configuration.h>
|
|
|
|
#include <apt-pkg/version.h>
|
|
|
|
#include <apt-pkg/sptr.h>
|
|
|
|
#include <apt-pkg/acquire-item.h>
|
|
|
|
|
|
|
|
#include <apti18n.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iostream>
|
|
|
|
/*}}}*/
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
pkgProblemResolver *pkgProblemResolver::This = 0;
|
|
|
|
|
|
|
|
// Simulate::Simulate - Constructor /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* The legacy translations here of input Pkg iterators is obsolete,
|
|
|
|
this is not necessary since the pkgCaches are fully shared now. */
|
|
|
|
pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache),
|
|
|
|
iPolicy(Cache),
|
|
|
|
Sim(&Cache->GetCache(),&iPolicy),
|
|
|
|
group(Sim)
|
|
|
|
{
|
|
|
|
Sim.Init(0);
|
|
|
|
Flags = new unsigned char[Cache->Head().PackageCount];
|
|
|
|
memset(Flags,0,sizeof(*Flags)*Cache->Head().PackageCount);
|
|
|
|
|
|
|
|
// Fake a filename so as not to activate the media swapping
|
|
|
|
string Jnk = "SIMULATE";
|
|
|
|
for (unsigned int I = 0; I != Cache->Head().PackageCount; I++)
|
|
|
|
FileNames[I] = Jnk;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// Simulate::Describe - Describe a package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* Parameter Current == true displays the current package version,
|
|
|
|
Parameter Candidate == true displays the candidate package version */
|
|
|
|
void pkgSimulate::Describe(PkgIterator Pkg,ostream &out,bool Current,bool Candidate)
|
|
|
|
{
|
|
|
|
VerIterator Ver(Sim);
|
|
|
|
|
|
|
|
out << Pkg.Name();
|
|
|
|
|
|
|
|
if (Current == true)
|
|
|
|
{
|
|
|
|
Ver = Pkg.CurrentVer();
|
|
|
|
if (Ver.end() == false)
|
|
|
|
out << " [" << Ver.VerStr() << ']';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Candidate == true)
|
|
|
|
{
|
|
|
|
Ver = Sim[Pkg].CandidateVerIter(Sim);
|
|
|
|
if (Ver.end() == true)
|
|
|
|
return;
|
|
|
|
|
|
|
|
out << " (" << Ver.VerStr() << ' ' << Ver.RelStr() << ')';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// Simulate::Install - Simulate unpacking of a package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/)
|
|
|
|
{
|
|
|
|
// Adapt the iterator
|
|
|
|
PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
|
|
|
|
Flags[Pkg->ID] = 1;
|
|
|
|
|
|
|
|
cout << "Inst ";
|
|
|
|
Describe(Pkg,cout,true,true);
|
|
|
|
Sim.MarkInstall(Pkg,false);
|
|
|
|
|
|
|
|
if (strcmp(Pkg.Arch(),"all") == 0)
|
|
|
|
{
|
|
|
|
pkgCache::GrpIterator G = Pkg.Group();
|
|
|
|
pkgCache::GrpIterator iG = iPkg.Group();
|
|
|
|
for (pkgCache::PkgIterator P = G.FindPkg("any"); P.end() != true; P = G.NextPkg(P))
|
|
|
|
{
|
|
|
|
if (strcmp(P.Arch(), "all") == 0)
|
|
|
|
continue;
|
|
|
|
if (iG.FindPkg(P.Arch())->CurrentVer == 0)
|
|
|
|
continue;
|
|
|
|
Flags[P->ID] = 1;
|
|
|
|
Sim.MarkInstall(P, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Look for broken conflicts+predepends.
|
|
|
|
for (PkgIterator I = Sim.PkgBegin(); I.end() == false; I++)
|
|
|
|
{
|
|
|
|
if (Sim[I].InstallVer == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (DepIterator D = Sim[I].InstVerIter(Sim).DependsList(); D.end() == false;)
|
|
|
|
{
|
|
|
|
DepIterator Start;
|
|
|
|
DepIterator End;
|
|
|
|
D.GlobOr(Start,End);
|
|
|
|
if (Start->Type == pkgCache::Dep::Conflicts ||
|
|
|
|
Start->Type == pkgCache::Dep::DpkgBreaks ||
|
|
|
|
Start->Type == pkgCache::Dep::Obsoletes ||
|
|
|
|
End->Type == pkgCache::Dep::PreDepends)
|
|
|
|
{
|
|
|
|
if ((Sim[End] & pkgDepCache::DepGInstall) == 0)
|
|
|
|
{
|
|
|
|
cout << " [" << I.Name() << " on " << Start.TargetPkg().Name() << ']';
|
|
|
|
if (Start->Type == pkgCache::Dep::Conflicts)
|
|
|
|
_error->Error("Fatal, conflicts violated %s",I.Name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Sim.BrokenCount() != 0)
|
|
|
|
ShortBreaks();
|
|
|
|
else
|
|
|
|
cout << endl;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// Simulate::Configure - Simulate configuration of a Package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* This is not an acurate simulation of relatity, we should really not
|
|
|
|
install the package.. For some investigations it may be necessary
|
|
|
|
however. */
|
|
|
|
bool pkgSimulate::Configure(PkgIterator iPkg)
|
|
|
|
{
|
|
|
|
// Adapt the iterator
|
|
|
|
PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
|
|
|
|
|
|
|
|
Flags[Pkg->ID] = 2;
|
|
|
|
|
|
|
|
if (strcmp(Pkg.Arch(),"all") == 0)
|
|
|
|
{
|
|
|
|
pkgCache::GrpIterator G = Pkg.Group();
|
|
|
|
for (pkgCache::PkgIterator P = G.FindPkg("any"); P.end() != true; P = G.NextPkg(P))
|
|
|
|
{
|
|
|
|
if (strcmp(P.Arch(), "all") == 0)
|
|
|
|
continue;
|
|
|
|
if (Flags[P->ID] == 1)
|
|
|
|
Flags[P->ID] = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sim.MarkInstall(Pkg,false);
|
|
|
|
if (Sim[Pkg].InstBroken() == true)
|
|
|
|
{
|
|
|
|
cout << "Conf " << Pkg.Name() << " broken" << endl;
|
|
|
|
|
|
|
|
Sim.Update();
|
|
|
|
|
|
|
|
// Print out each package and the failed dependencies
|
|
|
|
for (pkgCache::DepIterator D = Sim[Pkg].InstVerIter(Sim).DependsList(); D.end() == false; D++)
|
|
|
|
{
|
|
|
|
if (Sim.IsImportantDep(D) == false ||
|
|
|
|
(Sim[D] & pkgDepCache::DepInstall) != 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (D->Type == pkgCache::Dep::Obsoletes)
|
|
|
|
cout << " Obsoletes:" << D.TargetPkg().Name();
|
|
|
|
else if (D->Type == pkgCache::Dep::Conflicts)
|
|
|
|
cout << " Conflicts:" << D.TargetPkg().Name();
|
|
|
|
else if (D->Type == pkgCache::Dep::DpkgBreaks)
|
|
|
|
cout << " Breaks:" << D.TargetPkg().Name();
|
|
|
|
else
|
|
|
|
cout << " Depends:" << D.TargetPkg().Name();
|
|
|
|
}
|
|
|
|
cout << endl;
|
|
|
|
|
|
|
|
_error->Error("Conf Broken %s",Pkg.Name());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cout << "Conf ";
|
|
|
|
Describe(Pkg,cout,false,true);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Sim.BrokenCount() != 0)
|
|
|
|
ShortBreaks();
|
|
|
|
else
|
|
|
|
cout << endl;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// Simulate::Remove - Simulate the removal of a package /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge)
|
|
|
|
{
|
|
|
|
// Adapt the iterator
|
|
|
|
PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
|
|
|
|
|
|
|
|
Flags[Pkg->ID] = 3;
|
|
|
|
Sim.MarkDelete(Pkg);
|
|
|
|
|
|
|
|
if (strcmp(Pkg.Arch(),"all") == 0)
|
|
|
|
{
|
|
|
|
pkgCache::GrpIterator G = Pkg.Group();
|
|
|
|
pkgCache::GrpIterator iG = iPkg.Group();
|
|
|
|
for (pkgCache::PkgIterator P = G.FindPkg("any"); P.end() != true; P = G.NextPkg(P))
|
|
|
|
{
|
|
|
|
if (strcmp(P.Arch(), "all") == 0)
|
|
|
|
continue;
|
|
|
|
if (iG.FindPkg(P.Arch())->CurrentVer == 0)
|
|
|
|
continue;
|
|
|
|
Flags[P->ID] = 3;
|
|
|
|
Sim.MarkDelete(P);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Purge == true)
|
|
|
|
cout << "Purg ";
|
|
|
|
else
|
|
|
|
cout << "Remv ";
|
|
|
|
Describe(Pkg,cout,true,false);
|
|
|
|
|
|
|
|
if (Sim.BrokenCount() != 0)
|
|
|
|
ShortBreaks();
|
|
|
|
else
|
|
|
|
cout << endl;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// Simulate::ShortBreaks - Print out a short line describing all breaks /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
void pkgSimulate::ShortBreaks()
|
|
|
|
{
|
|
|
|
cout << " [";
|
|
|
|
for (PkgIterator I = Sim.PkgBegin(); I.end() == false; I++)
|
|
|
|
{
|
|
|
|
if (Sim[I].InstBroken() == true)
|
|
|
|
{
|
|
|
|
if (Flags[I->ID] == 0)
|
|
|
|
cout << I.Name() << ' ';
|
|
|
|
/* else
|
|
|
|
cout << I.Name() << "! ";*/
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cout << ']' << endl;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// ApplyStatus - Adjust for non-ok packages /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* We attempt to change the state of the all packages that have failed
|
|
|
|
installation toward their real state. The ordering code will perform
|
|
|
|
the necessary calculations to deal with the problems. */
|
|
|
|
bool pkgApplyStatus(pkgDepCache &Cache)
|
|
|
|
{
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
|
|
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
{
|
|
|
|
if (I->VersionList == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Only choice for a ReInstReq package is to reinstall
|
|
|
|
if (I->InstState == pkgCache::State::ReInstReq ||
|
|
|
|
I->InstState == pkgCache::State::HoldReInstReq)
|
|
|
|
{
|
|
|
|
if (I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true)
|
|
|
|
Cache.MarkKeep(I, false, false);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Is this right? Will dpkg choke on an upgrade?
|
|
|
|
if (Cache[I].CandidateVer != 0 &&
|
|
|
|
Cache[I].CandidateVerIter(Cache).Downloadable() == true)
|
|
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
|
|
else
|
|
|
|
return _error->Error(_("The package %s needs to be reinstalled, "
|
|
|
|
"but I can't find an archive for it."),I.Name());
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (I->CurrentState)
|
|
|
|
{
|
|
|
|
/* This means installation failed somehow - it does not need to be
|
|
|
|
re-unpacked (probably) */
|
|
|
|
case pkgCache::State::UnPacked:
|
|
|
|
case pkgCache::State::HalfConfigured:
|
|
|
|
case pkgCache::State::TriggersAwaited:
|
|
|
|
case pkgCache::State::TriggersPending:
|
|
|
|
if ((I->CurrentVer != 0 && I.CurrentVer().Downloadable() == true) ||
|
|
|
|
I.State() != pkgCache::PkgIterator::NeedsUnpack)
|
|
|
|
Cache.MarkKeep(I, false, false);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (Cache[I].CandidateVer != 0 &&
|
|
|
|
Cache[I].CandidateVerIter(Cache).Downloadable() == true)
|
|
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
|
|
else
|
|
|
|
Cache.MarkDelete(I);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// This means removal failed
|
|
|
|
case pkgCache::State::HalfInstalled:
|
|
|
|
Cache.MarkDelete(I);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
if (I->InstState != pkgCache::State::Ok)
|
|
|
|
return _error->Error("The package %s is not ok and I "
|
|
|
|
"don't know how to fix it!",I.Name());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// FixBroken - Fix broken packages /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* This autoinstalls every broken package and then runs the problem resolver
|
|
|
|
on the result. */
|
|
|
|
bool pkgFixBroken(pkgDepCache &Cache)
|
|
|
|
{
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
|
|
|
|
|
|
// Auto upgrade all broken packages
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
if (Cache[I].NowBroken() == true)
|
|
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
|
|
|
|
|
|
/* Fix packages that are in a NeedArchive state but don't have a
|
|
|
|
downloadable install version */
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
{
|
|
|
|
if (I.State() != pkgCache::PkgIterator::NeedsUnpack ||
|
|
|
|
Cache[I].Delete() == true)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (Cache[I].InstVerIter(Cache).Downloadable() == false)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
pkgProblemResolver Fix(&Cache);
|
|
|
|
return Fix.Resolve(true);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// 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.
|
|
|
|
*/
|
|
|
|
bool pkgDistUpgrade(pkgDepCache &Cache)
|
|
|
|
{
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
|
|
|
|
|
|
/* Auto upgrade all installed packages, this provides the basis
|
|
|
|
for the installation */
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
if (I->CurrentVer != 0)
|
|
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
|
|
|
|
|
|
/* Now, auto upgrade all essential packages - this ensures that
|
|
|
|
the essential packages are present and working */
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
|
|
|
|
Cache.MarkInstall(I, true, 0, false);
|
|
|
|
|
|
|
|
/* 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 (I->CurrentVer != 0)
|
|
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
|
|
|
|
|
|
pkgProblemResolver Fix(&Cache);
|
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Fix.Resolve();
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// AllUpgrade - Upgrade as many packages as possible /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* Right now the system must be consistent before this can be called.
|
|
|
|
It also will not change packages marked for install, it only tries
|
|
|
|
to install packages not marked for install */
|
|
|
|
bool pkgAllUpgrade(pkgDepCache &Cache)
|
|
|
|
{
|
|
|
|
pkgDepCache::ActionGroup group(Cache);
|
|
|
|
|
|
|
|
pkgProblemResolver Fix(&Cache);
|
|
|
|
|
|
|
|
if (Cache.BrokenCount() != 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// 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 (I->CurrentVer != 0 && Cache[I].InstallVer != 0)
|
|
|
|
Cache.MarkInstall(I, false, 0, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Fix.ResolveByKeep();
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// 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 didnt 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;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// ProblemResolver::pkgProblemResolver - Constructor /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
pkgProblemResolver::pkgProblemResolver(pkgDepCache *pCache) : Cache(*pCache)
|
|
|
|
{
|
|
|
|
// Allocate memory
|
|
|
|
unsigned long Size = Cache.Head().PackageCount;
|
|
|
|
Scores = new signed short[Size];
|
|
|
|
Flags = new unsigned char[Size];
|
|
|
|
memset(Flags,0,sizeof(*Flags)*Size);
|
|
|
|
|
|
|
|
// Set debug to true to see its decision logic
|
|
|
|
Debug = _config->FindB("Debug::pkgProblemResolver",false);
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// ProblemResolver::~pkgProblemResolver - Destructor /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
pkgProblemResolver::~pkgProblemResolver()
|
|
|
|
{
|
|
|
|
delete [] Scores;
|
|
|
|
delete [] Flags;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// ProblemResolver::ScoreSort - Sort the list by score /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
int pkgProblemResolver::ScoreSort(const void *a,const void *b)
|
|
|
|
{
|
|
|
|
Package const **A = (Package const **)a;
|
|
|
|
Package const **B = (Package const **)b;
|
|
|
|
if (This->Scores[(*A)->ID] > This->Scores[(*B)->ID])
|
|
|
|
return -1;
|
|
|
|
if (This->Scores[(*A)->ID] < This->Scores[(*B)->ID])
|
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/*}}}*/
|
|
|
|
// ProblemResolver::MakeScores - Make the score table /*{{{*/
|
|
|
|
// ---------------------------------------------------------------------
|
|
|
|
/* */
|
|
|
|
void pkgProblemResolver::MakeScores()
|
|
|
|
{
|
|
|
|
unsigned long Size = Cache.Head().PackageCount;
|
|
|
|
memset(Scores,0,sizeof(*Scores)*Size);
|
|
|
|
|
|
|
|
// Important Required Standard Optional Extra
|
|
|
|
signed short PrioMap[] = {
|
|
|
|
0,
|
|
|
|
(signed short) _config->FindI("pkgProblemResolver::Scores::Important",3),
|
|
|
|
(signed short) _config->FindI("pkgProblemResolver::Scores::Required",2),
|
|
|
|
(signed short) _config->FindI("pkgProblemResolver::Scores::Standard",1),
|
|
|
|
(signed short) _config->FindI("pkgProblemResolver::Scores::Optional",-1),
|
|
|
|
(signed short) _config->FindI("pkgProblemResolver::Scores::Extra",-2)
|
|
|
|
};
|
|
|
|
signed short PrioEssentials = _config->FindI("pkgProblemResolver::Scores::Essentials",100);
|
|
|
|
signed short PrioInstalledAndNotObsolete = _config->FindI("pkgProblemResolver::Scores::NotObsolete",1);
|
|
|
|
signed short PrioDepends = _config->FindI("pkgProblemResolver::Scores::Depends",1);
|
|
|
|
signed short PrioRecommends = _config->FindI("pkgProblemResolver::Scores::Recommends",1);
|
|
|
|
signed short AddProtected = _config->FindI("pkgProblemResolver::Scores::AddProtected",10000);
|
|
|
|
signed short AddEssential = _config->FindI("pkgProblemResolver::Scores::AddEssential",5000);
|
|
|
|
|
|
|
|
if (_config->FindB("Debug::pkgProblemResolver::ShowScores",false) == true)
|
|
|
|
clog << "Settings used to calculate pkgProblemResolver::Scores::" << endl
|
|
|
|
<< " Important => " << PrioMap[1] << endl
|
|
|
|
<< " Required => " << PrioMap[2] << endl
|
|
|
|
<< " Standard => " << PrioMap[3] << endl
|
|
|
|
<< " Optional => " << PrioMap[4] << endl
|
|
|
|
<< " Extra => " << PrioMap[5] << endl
|
|
|
|
<< " Essentials => " << PrioEssentials << endl
|
|
|
|
<< " InstalledAndNotObsolete => " << PrioInstalledAndNotObsolete << endl
|
|
|
|
<< " Depends => " << PrioDepends << endl
|
|
|
|
<< " Recommends => " << PrioRecommends << endl
|
|
|
|
<< " AddProtected => " << AddProtected << endl
|
|
|
|
<< " AddEssential => " << AddEssential << endl;
|
|
|
|
|
|
|
|
// Generate the base scores for a package based on its properties
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
{
|
|
|
|
if (Cache[I].InstallVer == 0)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
signed short &Score = Scores[I->ID];
|
|
|
|
|
|
|
|
/* This is arbitrary, it should be high enough to elevate an
|
|
|
|
essantial package above most other packages but low enough
|
|
|
|
to allow an obsolete essential packages to be removed by
|
|
|
|
a conflicts on a powerfull normal package (ie libc6) */
|
|
|
|
if ((I->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential)
|
|
|
|
Score += PrioEssentials;
|
|
|
|
|
|
|
|
// We transform the priority
|
|
|
|
if (Cache[I].InstVerIter(Cache)->Priority <= 5)
|
|
|
|
Score += PrioMap[Cache[I].InstVerIter(Cache)->Priority];
|
|
|
|
|
|
|
|
/* This helps to fix oddball problems with conflicting packages
|
|
|
|
on the same level. We enhance the score of installed packages
|
|
|
|
if those are not obsolete
|
|
|
|
*/
|
|
|
|
if (I->CurrentVer != 0 && Cache[I].CandidateVer != 0 && Cache[I].CandidateVerIter(Cache).Downloadable())
|
|
|
|
Score += PrioInstalledAndNotObsolete;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that we have the base scores we go and propogate dependencies
|
|
|
|
for (pkgCache::PkgIterator I = Cache.PkgBegin(); I.end() == false; I++)
|
|
|
|
{
|
|