// -*- mode: cpp; mode: fold -*-
// Description /*{{{*/
-// $Id: depcache.cc,v 1.3 1998/09/07 05:28:32 jgg Exp $
+// $Id: depcache.cc,v 1.25 2001/05/27 05:36:04 jgg Exp $
/* ######################################################################
Dependency Cache - Caches Dependency information.
##################################################################### */
/*}}}*/
// Include Files /*{{{*/
-#ifdef __GNUG__
-#pragma implementation "apt-pkg/depcache.h"
-#endif
#include <apt-pkg/depcache.h>
-
#include <apt-pkg/version.h>
#include <apt-pkg/error.h>
+#include <apt-pkg/sptr.h>
+#include <apt-pkg/algorithms.h>
+
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/aptconfiguration.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/tagfile.h>
+
+#include <iostream>
+#include <sstream>
+#include <set>
+
+#include <sys/stat.h>
+
+#include <apti18n.h>
/*}}}*/
+// helper for Install-Recommends-Sections and Never-MarkAuto-Sections /*{{{*/
+static bool
+ConfigValueInSubTree(const char* SubTree, const char *needle)
+{
+ Configuration::Item const *Opts;
+ Opts = _config->Tree(SubTree);
+ if (Opts != 0 && Opts->Child != 0)
+ {
+ Opts = Opts->Child;
+ for (; Opts != 0; Opts = Opts->Next)
+ {
+ if (Opts->Value.empty() == true)
+ continue;
+ if (strcmp(needle, Opts->Value.c_str()) == 0)
+ return true;
+ }
+ }
+ return false;
+}
+ /*}}}*/
+pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) : /*{{{*/
+ cache(cache), released(false)
+{
+ ++cache.group_level;
+}
+
+void pkgDepCache::ActionGroup::release()
+{
+ if(!released)
+ {
+ if(cache.group_level == 0)
+ std::cerr << "W: Unbalanced action groups, expect badness" << std::endl;
+ else
+ {
+ --cache.group_level;
+
+ if(cache.group_level == 0)
+ cache.MarkAndSweep();
+ }
+ released = false;
+ }
+}
+
+pkgDepCache::ActionGroup::~ActionGroup()
+{
+ release();
+}
+ /*}}}*/
// DepCache::pkgDepCache - Constructors /*{{{*/
// ---------------------------------------------------------------------
/* */
-pkgDepCache::pkgDepCache(MMap &Map,OpProgress &Prog) :
- pkgCache(Map), PkgState(0), DepState(0)
+pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) :
+ group_level(0), Cache(pCache), PkgState(0), DepState(0)
{
- if (_error->PendingError() == false)
- Init(&Prog);
+ DebugMarker = _config->FindB("Debug::pkgDepCache::Marker", false);
+ DebugAutoInstall = _config->FindB("Debug::pkgDepCache::AutoInstall", false);
+ delLocalPolicy = 0;
+ LocalPolicy = Plcy;
+ if (LocalPolicy == 0)
+ delLocalPolicy = LocalPolicy = new Policy;
}
/*}}}*/
// DepCache::~pkgDepCache - Destructor /*{{{*/
{
delete [] PkgState;
delete [] DepState;
+ delete delLocalPolicy;
}
/*}}}*/
// DepCache::Init - Generate the initial extra structures. /*{{{*/
/* This allocats the extension buffers and initializes them. */
bool pkgDepCache::Init(OpProgress *Prog)
{
+ // Suppress mark updates during this operation (just in case) and
+ // run a mark operation when Init terminates.
+ ActionGroup actions(*this);
+
delete [] PkgState;
delete [] DepState;
PkgState = new StateCache[Head().PackageCount];
DepState = new unsigned char[Head().DependsCount];
memset(PkgState,0,sizeof(*PkgState)*Head().PackageCount);
memset(DepState,0,sizeof(*DepState)*Head().DependsCount);
-
+
if (Prog != 0)
{
Prog->OverallProgress(0,2*Head().PackageCount,Head().PackageCount,
- "Building Dependancy Tree");
- Prog->SubProgress(Head().PackageCount,"Candidate Versions");
+ _("Building dependency tree"));
+ Prog->SubProgress(Head().PackageCount,_("Candidate versions"));
}
/* Set the current state of everything. In this state all of the
int Done = 0;
for (PkgIterator I = PkgBegin(); I.end() != true; I++,Done++)
{
- if (Prog != 0)
+ if (Prog != 0 && Done%20 == 0)
Prog->Progress(Done);
// Find the proper cache slot
StateCache &State = PkgState[I->ID];
State.iFlags = 0;
-
+
// Figure out the install version
State.CandidateVer = GetCandidateVer(I);
State.InstallVer = I.CurrentVer();
Prog->OverallProgress(Head().PackageCount,2*Head().PackageCount,
Head().PackageCount,
- "Building Dependancy Tree");
- Prog->SubProgress(Head().PackageCount,"Dependency Generation");
+ _("Building dependency tree"));
+ Prog->SubProgress(Head().PackageCount,_("Dependency generation"));
}
Update(Prog);
-
+
+ if(Prog != 0)
+ Prog->Done();
+
return true;
}
/*}}}*/
-// DepCache::GetCandidateVer - Returns the Candidate install version /*{{{*/
-// ---------------------------------------------------------------------
-/* The default just returns the target version if it exists or the
- highest version. */
-pkgDepCache::VerIterator pkgDepCache::GetCandidateVer(PkgIterator Pkg)
+bool pkgDepCache::readStateFile(OpProgress *Prog) /*{{{*/
{
- // Try to use an explicit target
- if (Pkg->TargetVer == 0)
- {
- /* Not source versions cannot be a candidate version unless they
- are already installed */
- for (VerIterator I = Pkg.VersionList(); I.end() == false; I++)
- {
- if (Pkg.CurrentVer() == I)
- return I;
- for (VerFileIterator J = I.FileList(); J.end() == false; J++)
- if ((J.File()->Flags & Flag::NotSource) == 0)
- return I;
+ FileFd state_file;
+ string const state = _config->FindFile("Dir::State::extended_states");
+ if(FileExists(state)) {
+ state_file.Open(state, FileFd::ReadOnly);
+ int const file_size = state_file.Size();
+ if(Prog != NULL)
+ Prog->OverallProgress(0, file_size, 1,
+ _("Reading state information"));
+
+ pkgTagFile tagfile(&state_file);
+ pkgTagSection section;
+ int amt = 0;
+ bool const debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false);
+ while(tagfile.Step(section)) {
+ string const pkgname = section.FindS("Package");
+ string pkgarch = section.FindS("Architecture");
+ if (pkgarch.empty() == true)
+ pkgarch = "any";
+ pkgCache::PkgIterator pkg = Cache->FindPkg(pkgname, pkgarch);
+ // Silently ignore unknown packages and packages with no actual version.
+ if(pkg.end() == true || pkg->VersionList == 0)
+ continue;
+
+ short const reason = section.FindI("Auto-Installed", 0);
+ if(reason > 0)
+ {
+ PkgState[pkg->ID].Flags |= Flag::Auto;
+ if (unlikely(debug_autoremove))
+ std::clog << "Auto-Installed : " << pkg.FullName() << std::endl;
+ if (pkgarch == "any")
+ {
+ pkgCache::GrpIterator G = pkg.Group();
+ for (pkg = G.NextPkg(pkg); pkg.end() != true; pkg = G.NextPkg(pkg))
+ if (pkg->VersionList != 0)
+ PkgState[pkg->ID].Flags |= Flag::Auto;
+ }
+ }
+ amt += section.size();
+ if(Prog != NULL)
+ Prog->OverallProgress(amt, file_size, 1,
+ _("Reading state information"));
}
-
- return VerIterator(*this,0);
+ if(Prog != NULL)
+ Prog->OverallProgress(file_size, file_size, 1,
+ _("Reading state information"));
}
- else
- return Pkg.TargetVer();
+
+ return true;
}
/*}}}*/
-// DepCache::IsImportantDep - True if the dependency is important /*{{{*/
-// ---------------------------------------------------------------------
-/* */
-bool pkgDepCache::IsImportantDep(DepIterator Dep)
+bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) /*{{{*/
{
- return Dep.IsCritical();
+ bool const debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false);
+
+ if(debug_autoremove)
+ std::clog << "pkgDepCache::writeStateFile()" << std::endl;
+
+ FileFd StateFile;
+ string const state = _config->FindFile("Dir::State::extended_states");
+
+ // if it does not exist, create a empty one
+ if(!FileExists(state))
+ {
+ StateFile.Open(state, FileFd::WriteEmpty);
+ StateFile.Close();
+ }
+
+ // open it
+ if(!StateFile.Open(state, FileFd::ReadOnly))
+ return _error->Error(_("Failed to open StateFile %s"),
+ state.c_str());
+
+ FILE *OutFile;
+ string const outfile = state + ".tmp";
+ if((OutFile = fopen(outfile.c_str(),"w")) == NULL)
+ return _error->Error(_("Failed to write temporary StateFile %s"),
+ outfile.c_str());
+
+ // first merge with the existing sections
+ pkgTagFile tagfile(&StateFile);
+ pkgTagSection section;
+ std::set<string> pkgs_seen;
+ const char *nullreorderlist[] = {0};
+ while(tagfile.Step(section)) {
+ string const pkgname = section.FindS("Package");
+ string pkgarch = section.FindS("Architecture");
+ if (pkgarch.empty() == true)
+ pkgarch = "native";
+ // Silently ignore unknown packages and packages with no actual
+ // version.
+ pkgCache::PkgIterator pkg = Cache->FindPkg(pkgname, pkgarch);
+ if(pkg.end() || pkg.VersionList().end())
+ continue;
+ StateCache const &P = PkgState[pkg->ID];
+ bool newAuto = (P.Flags & Flag::Auto);
+ // skip not installed or now-removed ones if requested
+ if (InstalledOnly && (
+ (pkg->CurrentVer == 0 && P.Mode != ModeInstall) ||
+ (pkg->CurrentVer != 0 && P.Mode == ModeDelete)))
+ {
+ // The section is obsolete if it contains no other tag
+ unsigned int const count = section.Count();
+ if (count < 2 ||
+ (count == 2 && section.Exists("Auto-Installed")) ||
+ (count == 3 && section.Exists("Auto-Installed") && section.Exists("Architecture")))
+ continue;
+ else
+ newAuto = false;
+ }
+ if(_config->FindB("Debug::pkgAutoRemove",false))
+ std::clog << "Update existing AutoInstall info: "
+ << pkg.FullName() << std::endl;
+ TFRewriteData rewrite[3];
+ rewrite[0].Tag = "Architecture";
+ rewrite[0].Rewrite = pkg.Arch();
+ rewrite[0].NewTag = 0;
+ rewrite[1].Tag = "Auto-Installed";
+ rewrite[1].Rewrite = newAuto ? "1" : "0";
+ rewrite[1].NewTag = 0;
+ rewrite[2].Tag = 0;
+ TFRewrite(OutFile, section, nullreorderlist, rewrite);
+ fprintf(OutFile,"\n");
+ pkgs_seen.insert(pkg.FullName());
+ }
+
+ // then write the ones we have not seen yet
+ std::ostringstream ostr;
+ for(pkgCache::PkgIterator pkg=Cache->PkgBegin(); !pkg.end(); pkg++) {
+ StateCache const &P = PkgState[pkg->ID];
+ if(P.Flags & Flag::Auto) {
+ if (pkgs_seen.find(pkg.FullName()) != pkgs_seen.end()) {
+ if(debug_autoremove)
+ std::clog << "Skipping already written " << pkg.FullName() << std::endl;
+ continue;
+ }
+ // skip not installed ones if requested
+ if (InstalledOnly && (
+ (pkg->CurrentVer == 0 && P.Mode != ModeInstall) ||
+ (pkg->CurrentVer != 0 && P.Mode == ModeDelete)))
+ continue;
+ const char* const pkgarch = pkg.Arch();
+ if (strcmp(pkgarch, "all") == 0)
+ continue;
+ if(debug_autoremove)
+ std::clog << "Writing new AutoInstall: " << pkg.FullName() << std::endl;
+ ostr.str(string(""));
+ ostr << "Package: " << pkg.Name()
+ << "\nArchitecture: " << pkgarch
+ << "\nAuto-Installed: 1\n\n";
+ fprintf(OutFile,"%s",ostr.str().c_str());
+ }
+ }
+ fclose(OutFile);
+
+ // move the outfile over the real file and set permissions
+ rename(outfile.c_str(), state.c_str());
+ chmod(state.c_str(), 0644);
+
+ return true;
}
/*}}}*/
-
// DepCache::CheckDep - Checks a single dependency /*{{{*/
// ---------------------------------------------------------------------
/* This first checks the dependency against the main target package and
/* Check simple depends. A depends -should- never self match but
we allow it anyhow because dpkg does. Technically it is a packaging
bug. Conflicts may never self match */
- if (Dep.TargetPkg() != Dep.ParentPkg() || Dep->Type != Dep::Conflicts)
+ if (Dep.TargetPkg()->Group != Dep.ParentPkg()->Group ||
+ (Dep->Type != Dep::Conflicts && Dep->Type != Dep::DpkgBreaks && Dep->Type != Dep::Obsoletes))
{
PkgIterator Pkg = Dep.TargetPkg();
// Check the base package
if (Type == NowVersion && Pkg->CurrentVer != 0)
- if (pkgCheckDep(Dep.TargetVer(),
- Pkg.CurrentVer().VerStr(),Dep->CompareOp) == true)
+ if (VS().CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp,
+ Dep.TargetVer()) == true)
return true;
if (Type == InstallVersion && PkgState[Pkg->ID].InstallVer != 0)
- if (pkgCheckDep(Dep.TargetVer(),
- PkgState[Pkg->ID].InstVerIter(*this).VerStr(),
- Dep->CompareOp) == true)
+ if (VS().CheckDep(PkgState[Pkg->ID].InstVerIter(*this).VerStr(),
+ Dep->CompareOp,Dep.TargetVer()) == true)
return true;
if (Type == CandidateVersion && PkgState[Pkg->ID].CandidateVer != 0)
- if (pkgCheckDep(Dep.TargetVer(),
- PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(),
- Dep->CompareOp) == true)
+ if (VS().CheckDep(PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(),
+ Dep->CompareOp,Dep.TargetVer()) == true)
return true;
}
+ if (Dep->Type == Dep::Obsoletes)
+ return false;
+
// Check the providing packages
PrvIterator P = Dep.TargetPkg().ProvidesList();
PkgIterator Pkg = Dep.ParentPkg();
for (; P.end() != true; P++)
{
- /* Provides may never be applied against the same package if it is
- a conflicts. See the comment above. */
- if (P.OwnerPkg() == Pkg && Dep->Type == Dep::Conflicts)
+ /* Provides may never be applied against the same package (or group)
+ if it is a conflicts. See the comment above. */
+ if (P.OwnerPkg()->Group == Pkg->Group &&
+ (Dep->Type == Dep::Conflicts || Dep->Type == Dep::DpkgBreaks))
continue;
// Check if the provides is a hit
}
// Compare the versions.
- if (pkgCheckDep(Dep.TargetVer(),P.ProvideVersion(),Dep->CompareOp) == true)
+ if (VS().CheckDep(P.ProvideVersion(),Dep->CompareOp,Dep.TargetVer()) == true)
{
Res = P.OwnerPkg();
return true;
/*}}}*/
// DepCache::AddSizes - Add the packages sizes to the counters /*{{{*/
// ---------------------------------------------------------------------
-/* Call with Mult = -1 to preform the inverse opration */
-void pkgDepCache::AddSizes(const PkgIterator &Pkg,long Mult)
+/* Call with Mult = -1 to preform the inverse opration
+ The Mult increases the complexity of the calulations here and is unused -
+ or do we really have a usecase for removing the size of a package two
+ times? So let us replace it with a simple bool and be done with it… */
+__deprecated void pkgDepCache::AddSizes(const PkgIterator &Pkg,signed long Mult)
{
StateCache &P = PkgState[Pkg->ID];
+ if (Pkg->VersionList == 0)
+ return;
+
+ if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure &&
+ P.Keep() == true)
+ return;
+
// Compute the size data
if (P.NewInstall() == true)
{
- iUsrSize += Mult*P.InstVerIter(*this)->InstalledSize;
- iDownloadSize += Mult*P.InstVerIter(*this)->Size;
+ iUsrSize += (signed long long)(Mult*P.InstVerIter(*this)->InstalledSize);
+ iDownloadSize += (signed long long)(Mult*P.InstVerIter(*this)->Size);
+ return;
}
// Upgrading
- if (Pkg->CurrentVer != 0 && P.InstallVer != (Version *)Pkg.CurrentVer() &&
- P.InstallVer != 0)
+ if (Pkg->CurrentVer != 0 &&
+ (P.InstallVer != (Version *)Pkg.CurrentVer() ||
+ (P.iFlags & ReInstall) == ReInstall) && P.InstallVer != 0)
{
- iUsrSize += Mult*(P.InstVerIter(*this)->InstalledSize -
- Pkg.CurrentVer()->InstalledSize);
- iDownloadSize += Mult*P.InstVerIter(*this)->Size;
+ iUsrSize += (signed long long)(Mult*((signed long long)P.InstVerIter(*this)->InstalledSize -
+ (signed long long)Pkg.CurrentVer()->InstalledSize));
+ iDownloadSize += (signed long long)(Mult*P.InstVerIter(*this)->Size);
+ return;
+ }
+
+ // Reinstall
+ if (Pkg.State() == pkgCache::PkgIterator::NeedsUnpack &&
+ P.Delete() == false)
+ {
+ iDownloadSize += (signed long long)(Mult*P.InstVerIter(*this)->Size);
+ return;
}
// Removing
if (Pkg->CurrentVer != 0 && P.InstallVer == 0)
- iUsrSize -= Mult*Pkg.CurrentVer()->InstalledSize;
+ {
+ iUsrSize -= (signed long long)(Mult*Pkg.CurrentVer()->InstalledSize);
+ return;
+ }
+}
+ /*}}}*/
+// DepCache::AddSizes - Add the packages sizes to the counters /*{{{*/
+// ---------------------------------------------------------------------
+/* Call with Inverse = true to preform the inverse opration */
+void pkgDepCache::AddSizes(const PkgIterator &Pkg, bool const &Inverse)
+{
+ StateCache &P = PkgState[Pkg->ID];
+
+ if (Pkg->VersionList == 0)
+ return;
+
+ if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure &&
+ P.Keep() == true)
+ return;
+
+ // Compute the size data
+ if (P.NewInstall() == true)
+ {
+ if (Inverse == false) {
+ iUsrSize += P.InstVerIter(*this)->InstalledSize;
+ iDownloadSize += P.InstVerIter(*this)->Size;
+ } else {
+ iUsrSize -= P.InstVerIter(*this)->InstalledSize;
+ iDownloadSize -= P.InstVerIter(*this)->Size;
+ }
+ return;
+ }
+
+ // Upgrading
+ if (Pkg->CurrentVer != 0 &&
+ (P.InstallVer != (Version *)Pkg.CurrentVer() ||
+ (P.iFlags & ReInstall) == ReInstall) && P.InstallVer != 0)
+ {
+ if (Inverse == false) {
+ iUsrSize -= Pkg.CurrentVer()->InstalledSize;
+ iUsrSize += P.InstVerIter(*this)->InstalledSize;
+ iDownloadSize += P.InstVerIter(*this)->Size;
+ } else {
+ iUsrSize -= P.InstVerIter(*this)->InstalledSize;
+ iUsrSize += Pkg.CurrentVer()->InstalledSize;
+ iDownloadSize -= P.InstVerIter(*this)->Size;
+ }
+ return;
+ }
+
+ // Reinstall
+ if (Pkg.State() == pkgCache::PkgIterator::NeedsUnpack &&
+ P.Delete() == false)
+ {
+ if (Inverse == false)
+ iDownloadSize += P.InstVerIter(*this)->Size;
+ else
+ iDownloadSize -= P.InstVerIter(*this)->Size;
+ return;
+ }
+
+ // Removing
+ if (Pkg->CurrentVer != 0 && P.InstallVer == 0)
+ {
+ if (Inverse == false)
+ iUsrSize -= Pkg.CurrentVer()->InstalledSize;
+ else
+ iUsrSize += Pkg.CurrentVer()->InstalledSize;
+ return;
+ }
}
/*}}}*/
// DepCache::AddStates - Add the package to the state counter /*{{{*/
{
StateCache &State = PkgState[Pkg->ID];
- // The Package is broken
+ // The Package is broken (either minimal dep or policy dep)
if ((State.DepState & DepInstMin) != DepInstMin)
iBrokenCount += Add;
+ if ((State.DepState & DepInstPolicy) != DepInstPolicy)
+ iPolicyBrokenCount += Add;
// Bad state
if (Pkg.State() != PkgIterator::NeedsNothing)
// Not installed
if (Pkg->CurrentVer == 0)
{
+ if (State.Mode == ModeDelete &&
+ (State.iFlags | Purge) == Purge && Pkg.Purge() == false)
+ iDelCount += Add;
+
if (State.Mode == ModeInstall)
iInstCount += Add;
return;
}
// Installed, no upgrade
- if (State.Upgradable() == false)
- {
+ if (State.Status == 0)
+ {
if (State.Mode == ModeDelete)
iDelCount += Add;
+ else
+ if ((State.iFlags & ReInstall) == ReInstall)
+ iInstCount += Add;
+
return;
}
/* Invert for Conflicts. We have to do this twice to get the
right sense for a conflicts group */
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts ||
+ D->Type == Dep::DpkgBreaks ||
+ D->Type == Dep::Obsoletes)
State = ~State;
// Add to the group if we are within an or..
+ State &= 0x7;
Group |= State;
State |= Group << 3;
if ((D->CompareOp & Dep::Or) != Dep::Or)
Group = 0;
// Invert for Conflicts
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts ||
+ D->Type == Dep::DpkgBreaks ||
+ D->Type == Dep::Obsoletes)
State = ~State;
}
}
}
}
/*}}}*/
+// DepCache::RemovePseudoInstalledPkg - MultiArch helper for Update() /*{{{*/
+// ---------------------------------------------------------------------
+/* We "install" arch all packages for all archs if it is installed. Many
+ of these will be broken. This method will look at these broken Pkg and
+ "remove" it. */
+bool pkgDepCache::RemovePseudoInstalledPkg(PkgIterator &Pkg, std::set<unsigned long> &recheck) {
+ if (unlikely(Pkg->CurrentVer == 0))
+ return false;
+
+ VerIterator V = Pkg.CurrentVer();
+ if (V->MultiArch != Version::All)
+ return false;
+
+ // Never ever kill an "all" package - they have no dependency so they can't be broken
+ if (strcmp(Pkg.Arch(),"all") == 0)
+ return false;
+
+ unsigned char const CurDepState = VersionState(V.DependsList(),DepInstall,DepInstMin,DepInstPolicy);
+ if ((CurDepState & DepInstMin) == DepInstMin) {
+ // okay, the package isn't broken, but is the package also required?
+ // If it has no real dependencies, no installed rdepends and doesn't
+ // provide something of value, we will kill it as not required.
+ // These pseudopackages have otherwise interesting effects if they get
+ // a new dependency in a newer version…
+ for (pkgCache::DepIterator D = V.DependsList();
+ D.end() != true; ++D)
+ if (D.IsCritical() == true && D.ParentPkg()->Group != Pkg->Group)
+ return false;
+ for (DepIterator D = Pkg.RevDependsList(); D.end() != true; ++D)
+ {
+ if (D.IsCritical() == false)
+ continue;
+ PkgIterator const P = D.ParentPkg();
+ if (P->Group == Pkg->Group)
+ continue;
+ if (P->CurrentVer != 0)
+ return false;
+ }
+ for (PrvIterator Prv = V.ProvidesList(); Prv.end() != true; Prv++)
+ for (DepIterator d = Prv.ParentPkg().RevDependsList();
+ d.end() != true; ++d)
+ {
+ PkgIterator const P = d.ParentPkg();
+ if (P->CurrentVer != 0 &&
+ P->Group != Pkg->Group)
+ return false;
+ }
+ }
+
+ // Dependencies for this arch all package are not statisfied
+ // so we installed it only for our convenience: get right of it now.
+ RemoveSizes(Pkg);
+ RemoveStates(Pkg);
+
+ Pkg->CurrentVer = 0;
+ PkgState[Pkg->ID].InstallVer = 0;
+
+ AddStates(Pkg);
+ Update(Pkg);
+ AddSizes(Pkg);
+
+ // After the remove previously satisfied pseudo pkg could be now
+ // no longer satisfied, so we need to recheck the reverse dependencies
+ for (DepIterator d = Pkg.RevDependsList(); d.end() != true; ++d)
+ {
+ PkgIterator const P = d.ParentPkg();
+ if (P->CurrentVer != 0)
+ recheck.insert(P.Index());
+ }
+
+ for (DepIterator d = V.DependsList(); d.end() != true; ++d)
+ {
+ PkgIterator const P = d.TargetPkg();
+ for (PrvIterator Prv = P.ProvidesList(); Prv.end() != true; ++Prv)
+ {
+ PkgIterator const O = Prv.OwnerPkg();
+ if (O->CurrentVer != 0)
+ recheck.insert(O.Index());
+ }
+
+ if (P->CurrentVer != 0)
+ recheck.insert(P.Index());
+ }
+
+ for (PrvIterator Prv = V.ProvidesList(); Prv.end() != true; Prv++)
+ {
+ for (DepIterator d = Prv.ParentPkg().RevDependsList();
+ d.end() != true; ++d)
+ {
+ PkgIterator const P = d.ParentPkg();
+ if (P->CurrentVer == 0)
+ continue;
+
+ recheck.insert(P.Index());
+ }
+ }
+
+
+ return true;
+}
+ /*}}}*/
// DepCache::Update - Figure out all the state information /*{{{*/
// ---------------------------------------------------------------------
/* This will figure out the state of all the packages and all the
iKeepCount = 0;
iBrokenCount = 0;
iBadCount = 0;
-
+
+ std::set<unsigned long> recheck;
+
// Perform the depends pass
int Done = 0;
+ bool const checkMultiArch = APT::Configuration::getArchitectures().size() > 1;
+ unsigned long killed = 0;
for (PkgIterator I = PkgBegin(); I.end() != true; I++,Done++)
{
if (Prog != 0 && Done%20 == 0)
for (VerIterator V = I.VersionList(); V.end() != true; V++)
{
unsigned char Group = 0;
-
+
for (DepIterator D = V.DependsList(); D.end() != true; D++)
{
// Build the dependency state.
unsigned char &State = DepState[D->ID];
- State = DependencyState(D);;
+ State = DependencyState(D);
// Add to the group if we are within an or..
Group |= State;
Group = 0;
// Invert for Conflicts
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts ||
+ D->Type == Dep::DpkgBreaks ||
+ D->Type == Dep::Obsoletes)
State = ~State;
- }
+ }
}
- // Compute the pacakge dependency state and size additions
+ // Compute the package dependency state and size additions
AddSizes(I);
UpdateVerState(I);
AddStates(I);
+
+ if (checkMultiArch != true || I->CurrentVer == 0)
+ continue;
+
+ VerIterator const V = I.CurrentVer();
+ if (V->MultiArch != Version::All)
+ continue;
+
+ recheck.insert(I.Index());
+ --Done; // no progress if we need to recheck the package
}
- if (Prog != 0)
+ if (checkMultiArch == true) {
+ /* FIXME: recheck breaks proper progress reporting as we don't know
+ how many packages we need to recheck. To lower the effect
+ a bit we increase with a kill, but we should do something more clever… */
+ while(recheck.empty() == false)
+ for (std::set<unsigned long>::const_iterator p = recheck.begin();
+ p != recheck.end();) {
+ if (Prog != 0 && Done%20 == 0)
+ Prog->Progress(Done);
+ PkgIterator P = PkgIterator(*Cache, Cache->PkgP + *p);
+ if (RemovePseudoInstalledPkg(P, recheck) == true) {
+ ++killed;
+ ++Done;
+ }
+ recheck.erase(p++);
+ }
+
+ /* Okay, we have killed a great amount of pseudopackages -
+ we have killed so many that we have now arch "all" packages
+ without an installed pseudo package, but we NEED an installed
+ pseudo package, so we will search now for a pseudo package
+ we can install without breaking everything. */
+ for (GrpIterator G = Cache->GrpBegin(); G.end() != true; ++G)
+ {
+ PkgIterator P = G.FindPkg("all");
+ if (P.end() == true)
+ continue;
+ if (P->CurrentVer == 0)
+ continue;
+ bool installed = false;
+ for (P = G.FindPkg("any"); P.end() != true; P = G.NextPkg(P))
+ {
+ if (strcmp(P.Arch(), "all") == 0)
+ continue;
+ if (P->CurrentVer == 0)
+ continue;
+ installed = true;
+ break;
+ }
+ if (installed == false)
+ recheck.insert(G.Index());
+ }
+
+ while (recheck.empty() != true)
+ {
+ std::set<unsigned long>::const_iterator g = recheck.begin();
+ unsigned long const G = *g;
+ recheck.erase(g);
+ if (unlikely(ReInstallPseudoForGroup(G, recheck) == false))
+ _error->Warning(_("Internal error, group '%s' has no installable pseudo package"), GrpIterator(*Cache, Cache->GrpP + G).Name());
+ }
+ }
+
+ if (Prog != 0)
Prog->Progress(Done);
+
+ readStateFile(Prog);
+}
+ /*}}}*/
+// DepCache::ReInstallPseudoForGroup - MultiArch helper for Update() /*{{{*/
+// ---------------------------------------------------------------------
+/* RemovePseudoInstalledPkg() is very successful. It even kills packages
+ to an amount that no pseudo package is left, but we need a pseudo package
+ for upgrading senarios so we need to reinstall one pseudopackage which
+ doesn't break everything. Thankfully we can't have architecture depending
+ negative dependencies so this problem is already eliminated */
+bool pkgDepCache::ReInstallPseudoForGroup(pkgCache::PkgIterator const &P, std::set<unsigned long> &recheck)
+{
+ if (P->CurrentVer != 0)
+ return true;
+ // recursive call for packages which provide this package
+ for (pkgCache::PrvIterator Prv = P.ProvidesList(); Prv.end() != true; ++Prv)
+ ReInstallPseudoForGroup(Prv.OwnerPkg(), recheck);
+ // check if we actually need to look at this group
+ unsigned long const G = P->Group;
+ std::set<unsigned long>::const_iterator Pi = recheck.find(G);
+ if (Pi == recheck.end())
+ return true;
+ recheck.erase(Pi); // remove here, so we can't fall into an endless loop
+ if (unlikely(ReInstallPseudoForGroup(G, recheck) == false))
+ {
+ recheck.insert(G);
+ return false;
+ }
+ return true;
+}
+bool pkgDepCache::ReInstallPseudoForGroup(unsigned long const &G, std::set<unsigned long> &recheck)
+{
+ std::vector<std::string> static const Archs = APT::Configuration::getArchitectures();
+ pkgCache::GrpIterator Grp(*Cache, Cache->GrpP + G);
+ if (unlikely(Grp.end() == true))
+ return false;
+ for (std::vector<std::string>::const_iterator a = Archs.begin();
+ a != Archs.end(); ++a)
+ {
+ pkgCache::PkgIterator P = Grp.FindPkg(*a);
+ if (P.end() == true)
+ continue;
+ pkgCache::VerIterator allV = Grp.FindPkg("all").CurrentVer();
+ for (VerIterator V = P.VersionList(); V.end() != true; ++V)
+ {
+ // search for the same version as the all package
+ if (allV->Hash != V->Hash || strcmp(allV.VerStr(),V.VerStr()) != 0)
+ continue;
+ unsigned char const CurDepState = VersionState(V.DependsList(),DepInstall,DepInstMin,DepInstPolicy);
+ // If it is broken, try to install dependencies first before retry
+ if ((CurDepState & DepInstMin) != DepInstMin)
+ {
+ for (pkgCache::DepIterator D = V.DependsList(); D.end() != true; ++D)
+ {
+ if (D->Type != pkgCache::Dep::PreDepends && D->Type != pkgCache::Dep::Depends)
+ continue;
+ ReInstallPseudoForGroup(D.TargetPkg(), recheck);
+ }
+ unsigned char const CurDepState = VersionState(V.DependsList(),DepInstall,DepInstMin,DepInstPolicy);
+ // if package ist still broken… try another arch
+ if ((CurDepState & DepInstMin) != DepInstMin)
+ break;
+ }
+ // dependencies satisfied: reinstall the package
+ RemoveSizes(P);
+ RemoveStates(P);
+ P->CurrentVer = V.Index();
+ PkgState[P->ID].InstallVer = V;
+ AddStates(P);
+ Update(P);
+ AddSizes(P);
+ return true;
+ }
+ }
+ return false;
}
/*}}}*/
// DepCache::Update - Update the deps list of a package /*{{{*/
// ---------------------------------------------------------------------
/* This is a helper for update that only does the dep portion of the scan.
- It is mainly ment to scan reverse dependencies. */
+ It is mainly meant to scan reverse dependencies. */
void pkgDepCache::Update(DepIterator D)
{
// Update the reverse deps
State = DependencyState(D);
// Invert for Conflicts
- if (D->Type == Dep::Conflicts)
+ if (D->Type == Dep::Conflicts ||
+ D->Type == Dep::DpkgBreaks ||
+ D->Type == Dep::Obsoletes)
State = ~State;
-
+
RemoveStates(D.ParentPkg());
BuildGroupOrs(D.ParentVer());
UpdateVerState(D.ParentPkg());
/* This is called whenever the state of a package changes. It updates
all cached dependencies related to this package. */
void pkgDepCache::Update(PkgIterator const &Pkg)
-{
+{
// Recompute the dep of the package
RemoveStates(Pkg);
UpdateVerState(Pkg);
Update(P.ParentPkg().RevDependsList());
// Update the provides map for the candidate ver
- for (PrvIterator P = PkgState[Pkg->ID].CandidateVerIter(*this).ProvidesList();
- P.end() != true; P++)
- Update(P.ParentPkg().RevDependsList());
+ if (PkgState[Pkg->ID].CandidateVer != 0)
+ for (PrvIterator P = PkgState[Pkg->ID].CandidateVerIter(*this).ProvidesList();
+ P.end() != true; P++)
+ Update(P.ParentPkg().RevDependsList());
}
-
/*}}}*/
-
// DepCache::MarkKeep - Put the package in the keep state /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft)
+void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser,
+ unsigned long Depth)
{
// Simplifies other routines.
if (Pkg.end() == true)
return;
+
+ /* Reject an attempt to keep a non-source broken installed package, those
+ must be upgraded */
+ if (Pkg.State() == PkgIterator::NeedsUnpack &&
+ Pkg.CurrentVer().Downloadable() == false)
+ return;
+ /** \todo Can this be moved later in the method? */
+ ActionGroup group(*this);
+
/* We changed the soft state all the time so the UI is a bit nicer
to use */
StateCache &P = PkgState[Pkg->ID];
// We dont even try to keep virtual packages..
if (Pkg->VersionList == 0)
return;
-
- P.Flags &= ~Flag::Auto;
+#if 0 // reseting the autoflag here means we lose the
+ // auto-mark information if a user selects a package for removal
+ // but changes his mind then and sets it for keep again
+ // - this makes sense as default when all Garbage dependencies
+ // are automatically marked for removal (as aptitude does).
+ // setting a package for keep then makes it no longer autoinstalled
+ // for all other use-case this action is rather suprising
+ if(FromUser && !P.Marked)
+ P.Flags &= ~Flag::Auto;
+#endif
+
+ if (DebugMarker == true)
+ std::clog << OutputInDepth(Depth) << "MarkKeep " << Pkg << " FU=" << FromUser << std::endl;
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
// DepCache::MarkDelete - Put the package in the delete state /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::MarkDelete(PkgIterator const &Pkg)
+void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge,
+ unsigned long Depth, bool FromUser)
{
// Simplifies other routines.
if (Pkg.end() == true)
return;
+ ActionGroup group(*this);
+
// Check that it is not already marked for delete
StateCache &P = PkgState[Pkg->ID];
- P.iFlags &= ~AutoKept;
- if (P.Mode == ModeDelete || P.InstallVer == 0)
+ P.iFlags &= ~(AutoKept | Purge);
+ if (rPurge == true)
+ P.iFlags |= Purge;
+
+ if ((P.Mode == ModeDelete || P.InstallVer == 0) &&
+ (Pkg.Purge() == true || rPurge == false))
return;
-
+
// We dont even try to delete virtual packages..
if (Pkg->VersionList == 0)
return;
+ // check if we are allowed to install the package
+ if (IsDeleteOk(Pkg,rPurge,Depth,FromUser) == false)
+ return;
+
+ if (DebugMarker == true)
+ std::clog << OutputInDepth(Depth) << "MarkDelete " << Pkg << " FU=" << FromUser << std::endl;
+
RemoveSizes(Pkg);
RemoveStates(Pkg);
- P.Mode = ModeDelete;
+ if (Pkg->CurrentVer == 0 && (Pkg.Purge() == true || rPurge == false))
+ P.Mode = ModeKeep;
+ else
+ P.Mode = ModeDelete;
P.InstallVer = 0;
- P.Flags &= Flag::Auto;
AddStates(Pkg);
Update(Pkg);
AddSizes(Pkg);
+
+ // if we remove the pseudo package, we also need to remove the "real"
+ if (Pkg->CurrentVer != 0 && Pkg.CurrentVer().Pseudo() == true)
+ MarkDelete(Pkg.Group().FindPkg("all"), rPurge, Depth+1, FromUser);
+}
+ /*}}}*/
+// DepCache::IsDeleteOk - check if it is ok to remove this package /*{{{*/
+// ---------------------------------------------------------------------
+/* The default implementation just honors dpkg hold
+ But an application using this library can override this method
+ to control the MarkDelete behaviour */
+bool pkgDepCache::IsDeleteOk(PkgIterator const &Pkg,bool rPurge,
+ unsigned long Depth, bool FromUser)
+{
+ if (FromUser == false && Pkg->SelectedState == pkgCache::State::Hold && _config->FindB("APT::Ignore-Hold",false) == false)
+ {
+ if (DebugMarker == true)
+ std::clog << OutputInDepth(Depth) << "Hold prevents MarkDelete of " << Pkg << " FU=" << FromUser << std::endl;
+ return false;
+ }
+ return true;
}
/*}}}*/
// DepCache::MarkInstall - Put the package in the install state /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst)
-{
+void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst,
+ unsigned long Depth, bool FromUser,
+ bool ForceImportantDeps)
+{
+ if (Depth > 100)
+ return;
+
// Simplifies other routines.
if (Pkg.end() == true)
return;
+ ActionGroup group(*this);
+
/* Check that it is not already marked for install and that it can be
installed */
StateCache &P = PkgState[Pkg->ID];
P.iFlags &= ~AutoKept;
- if (P.InstBroken() == false && (P.Mode == ModeInstall ||
+ if ((P.InstPolicyBroken() == false && P.InstBroken() == false) &&
+ (P.Mode == ModeInstall ||
P.CandidateVer == (Version *)Pkg.CurrentVer()))
{
if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0)
- MarkKeep(Pkg);
+ MarkKeep(Pkg, false, FromUser, Depth+1);
return;
}
-
+
+ // See if there is even any possible instalation candidate
+ if (P.CandidateVer == 0)
+ return;
// We dont even try to install virtual packages..
if (Pkg->VersionList == 0)
return;
-
+
+ // check if we are allowed to install the package
+ if (IsInstallOk(Pkg,AutoInst,Depth,FromUser) == false)
+ return;
+
/* Target the candidate version and remove the autoflag. We reset the
autoflag below if this was called recursively. Otherwise the user
should have the ability to de-auto a package by changing its state */
P.Mode = ModeInstall;
P.InstallVer = P.CandidateVer;
- P.Flags &= ~Flag::Auto;
+
+ if(FromUser)
+ {
+ // Set it to manual if it's a new install or cancelling the
+ // removal of a garbage package.
+ if(P.Status == 2 || (!Pkg.CurrentVer().end() && !P.Marked))
+ P.Flags &= ~Flag::Auto;
+ }
+ else
+ {
+ // Set it to auto if this is a new install.
+ if(P.Status == 2)
+ P.Flags |= Flag::Auto;
+ }
if (P.CandidateVer == (Version *)Pkg.CurrentVer())
P.Mode = ModeKeep;
AddStates(Pkg);
Update(Pkg);
AddSizes(Pkg);
-
+
+ // always trigger the install of the all package for a pseudo package
+ if (P.CandidateVerIter(*Cache).Pseudo() == true)
+ MarkInstall(Pkg.Group().FindPkg("all"), AutoInst, Depth, FromUser, ForceImportantDeps);
+
if (AutoInst == false)
return;
+ if (DebugMarker == true)
+ std::clog << OutputInDepth(Depth) << "MarkInstall " << Pkg << " FU=" << FromUser << std::endl;
+
DepIterator Dep = P.InstVerIter(*this).DependsList();
for (; Dep.end() != true;)
{
// Grok or groups
DepIterator Start = Dep;
bool Result = true;
- for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++)
+ unsigned Ors = 0;
+ for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++,Ors++)
{
LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or;
/* Check if this dep should be consider for install. If it is a user
defined important dep and we are installed a new package then
- it will be installed. Otherwise we only worry about critical deps */
+ it will be installed. Otherwise we only check for important
+ deps that have changed from the installed version
+ */
if (IsImportantDep(Start) == false)
continue;
- if (Pkg->CurrentVer != 0 && Start.IsCritical() == false)
+
+ /* Check if any ImportantDep() (but not Critical) were added
+ * since we installed the package. Also check for deps that
+ * were satisfied in the past: for instance, if a version
+ * restriction in a Recommends was tightened, upgrading the
+ * package should follow that Recommends rather than causing the
+ * dependency to be removed. (bug #470115)
+ */
+ bool isNewImportantDep = false;
+ bool isPreviouslySatisfiedImportantDep = false;
+ if(!ForceImportantDeps && !Start.IsCritical())
+ {
+ bool found=false;
+ VerIterator instVer = Pkg.CurrentVer();
+ if(!instVer.end())
+ {
+ for (DepIterator D = instVer.DependsList(); D.end() != true; D++)
+ {
+ //FIXME: deal better with or-groups(?)
+ DepIterator LocalStart = D;
+
+ if(IsImportantDep(D) && !D.IsCritical() &&
+ Start.TargetPkg() == D.TargetPkg())
+ {
+ if(!isPreviouslySatisfiedImportantDep)
+ {
+ DepIterator D2 = D;
+ while((D2->CompareOp & Dep::Or) != 0)
+ ++D2;
+
+ isPreviouslySatisfiedImportantDep =
+ (((*this)[D2] & DepGNow) != 0);
+ }
+
+ found=true;
+ }
+ }
+ // this is a new dep if it was not found to be already
+ // a important dep of the installed pacakge
+ isNewImportantDep = !found;
+ }
+ }
+ if(isNewImportantDep)
+ if(DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "new important dependency: "
+ << Start.TargetPkg().Name() << std::endl;
+ if(isPreviouslySatisfiedImportantDep)
+ if(DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "previously satisfied important dependency on "
+ << Start.TargetPkg().Name() << std::endl;
+
+ // skip important deps if the package is already installed
+ if (Pkg->CurrentVer != 0 && Start.IsCritical() == false
+ && !isNewImportantDep && !isPreviouslySatisfiedImportantDep
+ && !ForceImportantDeps)
continue;
+
+ /* If we are in an or group locate the first or that can
+ succeed. We have already cached this.. */
+ for (; Ors > 1 && (DepState[Start->ID] & DepCVer) != DepCVer; Ors--)
+ Start++;
- // Now we have to take action...
- PkgIterator P = Start.SmartTargetPkg();
- if ((DepState[Start->ID] & DepCVer) == DepCVer)
+ /* This bit is for processing the possibilty of an install/upgrade
+ fixing the problem */
+ SPtrArray<Version *> List = Start.AllTargets();
+ if (Start->Type != Dep::DpkgBreaks &&
+ (DepState[Start->ID] & DepCVer) == DepCVer)
{
- MarkInstall(P,true);
+ // Right, find the best version to install..
+ Version **Cur = List;
+ PkgIterator P = Start.TargetPkg();
+ PkgIterator InstPkg(*Cache,0);
- // Set the autoflag, after MarkInstall because MarkInstall unsets it
- if (P->CurrentVer == 0)
- PkgState[P->ID].Flags |= Flag::Auto;
+ // See if there are direct matches (at the start of the list)
+ for (; *Cur != 0 && (*Cur)->ParentPkg == P.Index(); Cur++)
+ {
+ PkgIterator Pkg(*Cache,Cache->PkgP + (*Cur)->ParentPkg);
+ if (PkgState[Pkg->ID].CandidateVer != *Cur)
+ continue;
+ InstPkg = Pkg;
+ break;
+ }
+ // Select the highest priority providing package
+ if (InstPkg.end() == true)
+ {
+ pkgPrioSortList(*Cache,Cur);
+ for (; *Cur != 0; Cur++)
+ {
+ PkgIterator Pkg(*Cache,Cache->PkgP + (*Cur)->ParentPkg);
+ if (PkgState[Pkg->ID].CandidateVer != *Cur)
+ continue;
+ InstPkg = Pkg;
+ break;
+ }
+ }
+
+ if (InstPkg.end() == false)
+ {
+ if(DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "Installing " << InstPkg.Name()
+ << " as " << Start.DepType() << " of " << Pkg.Name()
+ << std::endl;
+ // now check if we should consider it a automatic dependency or not
+ if(Pkg.Section() && ConfigValueInSubTree("APT::Never-MarkAuto-Sections", Pkg.Section()))
+ {
+ if(DebugAutoInstall == true)
+ std::clog << OutputInDepth(Depth) << "Setting NOT as auto-installed (direct "
+ << Start.DepType() << " of pkg in APT::Never-MarkAuto-Sections)" << std::endl;
+ MarkInstall(InstPkg,true,Depth + 1, true);
+ }
+ else
+ {
+ // mark automatic dependency
+ MarkInstall(InstPkg,true,Depth + 1, false, ForceImportantDeps);
+ // Set the autoflag, after MarkInstall because MarkInstall unsets it
+ if (P->CurrentVer == 0)
+ PkgState[InstPkg->ID].Flags |= Flag::Auto;
+ }
+ }
continue;
}
-
- // For conflicts we just de-install the package and mark as auto
- if (Start->Type == Dep::Conflicts)
+
+ /* For conflicts we just de-install the package and mark as auto,
+ Conflicts may not have or groups. For dpkg's Breaks we try to
+ upgrade the package. */
+ if (Start->Type == Dep::Conflicts || Start->Type == Dep::Obsoletes ||
+ Start->Type == Dep::DpkgBreaks)
{
- Version **List = Start.AllTargets();
for (Version **I = List; *I != 0; I++)
{
VerIterator Ver(*this,*I);
PkgIterator Pkg = Ver.ParentPkg();
-
- MarkDelete(Pkg);
- PkgState[Pkg->ID].Flags |= Flag::Auto;
+
+ if (Start->Type != Dep::DpkgBreaks)
+ MarkDelete(Pkg,false,Depth + 1, false);
+ else if (PkgState[Pkg->ID].CandidateVer != *I)
+ MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps);
}
- delete [] List;
continue;
}
}
}
/*}}}*/
-
-#if 0
-// DepCache::ResolveConflicts - Figure the auto upgrades /*{{{*/
+// DepCache::IsInstallOk - check if it is ok to install this package /*{{{*/
// ---------------------------------------------------------------------
-/* This routine attempts to resolve conflicts generated by automatic
- upgrading. It is known as 'Stage 1' but that name isn't as proper anymore.
-
- It's most important function is during the initial load of APT. The
- loading code will mark every package for upgrade to it's candidate version
- and then call this routine. This routine will then 'soft keep' every
- package that causes conflict, is conflicted, or so on. It is a bit
- agressive in that it may unselect more packages in some odd cases
- than are strictly necessary but in the case where no packages were
- conflicting before it will always produce a system with no packages
- conflicting after.
-
- This routine is also used during state changes that require autoupgrade
- scanning. That is, if a new package is marked for install then all packages
- that have been soft kept are reconsidered for upgrade.
-
- It is called with state information about what can be un-upgraded and
- what the pre-upgrade install state was. It is expected that the caller
- has already marked the desired packages to the install state. Bit 0 is
- the original install state and Bit 1 is controls whether the package
- should be touched.
-*/
-void pkgDepCache::ResolveConflicts(unsigned char *Touched)
-{
- bool Again = false;
- do
- {
- Again = false;
- for (PkgIterator I = PkgBegin(); I.end() != true; I++)
- {
- // The package will install OK
- if ((PkgState[I->ID].DepState & DepInstMin) == DepInstMin)
- continue;
-
- /* The package was broken before and this upgrade will not
- make things better. We simply mark the package for keep
- and assume an upgrade attempt will be hopeless. This might
- not be ideal. */
- if ((Touched[I->ID] & (1 << 0)) != (1 << 0))
- {
- // The package isnt to be touched
- if ((Touched[I->ID] & (1 << 1)) == (1 << 1))
- MarkKeep(I,true);
-
- continue;
- }
-
- // Check to see if not upgrading it will solve the problem
- if (I->CurrentVer != 0)
- {
- // The package isnt to be touched
- if ((Touched[I->ID] & (1 << 1)) == (1 << 1))
- {
- if (PkgState[I->ID].Mode != ModeKeep)
- Again = true;
-
- MarkKeep(I,true);
- }
-
- /* Check if the package is sill broken. If so then we cant leave
- it as is and get a working system. Lets try marking some
- depends for 'keep'. This is brutal, it keeps everything in
- sight to fix the problem. */
- DepIterator D = I.CurrentVer().DependsList();
- for (;(PkgState[I->ID].DepState & DepInstMin) != DepInstMin &&
- D.end() != true; D++)
- {
- // We only worry about critical deps.
- if (D.IsCritical() != true)
- continue;
-
- unsigned char State = DepState[D->ID];
-
- // This dep never was set before so we dont need to set it now
- if ((State & DepNow) != DepNow)
- continue;
-
- // The dep is okay now no worries.
- if ((State & DepInstall) == DepInstall)
- continue;
-
- // Locate a target to keep
- PkgIterator P(*this);
- if (CheckDep(D,NowVersion,P) == true)
- {
- // We cant touch this package
- if ((Touched[P->ID] & (1 << 1)) == (1 << 1))
- MarkKeep(P,true);
- }
- }
- }
- }
+/* The default implementation just honors dpkg hold
+ But an application using this library can override this method
+ to control the MarkInstall behaviour */
+bool pkgDepCache::IsInstallOk(PkgIterator const &Pkg,bool AutoInst,
+ unsigned long Depth, bool FromUser)
+{
+ if (FromUser == false && Pkg->SelectedState == pkgCache::State::Hold && _config->FindB("APT::Ignore-Hold",false) == false)
+ {
+ if (DebugMarker == true)
+ std::clog << OutputInDepth(Depth) << "Hold prevents MarkInstall of " << Pkg << " FU=" << FromUser << std::endl;
+ return false;
}
- while (Again == true);
+ return true;
}
/*}}}*/
-// DepCache::PromoteAutoKeep - Gentler version of the above /*{{{*/
+// DepCache::SetReInstall - Set the reinstallation flag /*{{{*/
// ---------------------------------------------------------------------
-/* This is used when installing packages, all it does is attempt to promote
- everything that has been auto-kept. It simply promotes everything
- irregardless of it having a chance to work and then runs ResolveConflicts
- on the result. This allows circular depends loops to work but isn't
- terribly fast. */
-void pkgDepCache::PromoteAutoKeep()
-{
- /* Initialize the touchd array. Bit 0 is the old install state bit 1
- is the touch value */
- unsigned char *Touch = new unsigned char[Head().PackageCount];
- for (unsigned int I = 0; I != Head().PackageCount; I++)
- {
- if ((PkgState[I].DepState & DepInstMin) == DepInstMin)
- Touch[I] = 1 << 0;
- else
- Touch[I] = 0;
- }
+/* */
+void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To)
+{
+ if (unlikely(Pkg.end() == true))
+ return;
- // This allows circular depends to work
- for (PkgIterator I = PkgBegin(); I.end() != true; I++)
- {
- /* It wasnt installed before or it is not autokept or it is not
- upgradeable */
- StateCache &P = PkgState[I->ID];
- if (I->CurrentVer == 0 || P.Mode != ModeKeep || I->VersionList == 0 ||
- P.CandidateVer == (Version *)I.CurrentVer() ||
- (P.iFlags & AutoKept) != AutoKept)
- continue;
+ ActionGroup group(*this);
- P.Mode = ModeInstall;
- P.InstallVer = P.CandidateVer;
- if (P.CandidateVer == (Version *)I.CurrentVer())
- P.Mode = ModeKeep;
-
- // Okay autoupgrade it.
- Touch[I->ID] |= 1 << 1;
- }
+ RemoveSizes(Pkg);
+ RemoveStates(Pkg);
- Update();
- ResolveConflicts(Touch);
+ StateCache &P = PkgState[Pkg->ID];
+ if (To == true)
+ P.iFlags |= ReInstall;
+ else
+ P.iFlags &= ~ReInstall;
- delete [] Touch;
+ AddStates(Pkg);
+ AddSizes(Pkg);
+
+ if (unlikely(Pkg.CurrentVer().end() == true) || Pkg.CurrentVer().Pseudo() == false)
+ return;
+
+ SetReInstall(Pkg.Group().FindPkg("all"), To);
}
/*}}}*/
-// DepCache::AllUpgrade - Try to upgrade everything /*{{{*/
+// DepCache::SetCandidateVersion - Change the candidate version /*{{{*/
// ---------------------------------------------------------------------
/* */
-void pkgDepCache::AllUpgrade()
+void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo)
{
- // Set everything to an upgrade mode
- for (PkgIterator I = PkgBegin(); I.end() != true; I++)
+ ActionGroup group(*this);
+
+ pkgCache::PkgIterator Pkg = TargetVer.ParentPkg();
+ StateCache &P = PkgState[Pkg->ID];
+
+ RemoveSizes(Pkg);
+ RemoveStates(Pkg);
+
+ if (P.CandidateVer == P.InstallVer)
+ P.InstallVer = (Version *)TargetVer;
+ P.CandidateVer = (Version *)TargetVer;
+ P.Update(Pkg,*this);
+
+ AddStates(Pkg);
+ Update(Pkg);
+ AddSizes(Pkg);
+
+ if (TargetVer.Pseudo() == false || Pseudo == false)
+ return;
+
+ // the version was pseudo: set all other pseudos also
+ pkgCache::GrpIterator Grp = Pkg.Group();
+ for (Pkg = Grp.FindPkg("any"); Pkg.end() == false; ++Pkg)
{
- StateCache &State = PkgState[I->ID];
-
- /* We dont upgrade packages marked for deletion or that are
- not installed or that don't have an upgrade */
- if (State.Mode == ModeDelete || I->CurrentVer == 0 ||
- (Version *)I.CurrentVer() == State.CandidateVer)
+ StateCache &P = PkgState[Pkg->ID];
+ if (TargetVer.SimilarVer(P.CandidateVerIter(*this)) == true ||
+ (P.CandidateVerIter(*this).Pseudo() == false &&
+ strcmp(Pkg.Arch(), "all") != 0))
continue;
-
- // Set the state to upgrade
- State.iFlags = 0;
- State.Mode = ModeInstall;
- State.InstallVer = State.CandidateVer;
- if (State.CandidateVer == (Version *)I.CurrentVer())
- State.Mode = ModeKeep;
-
- // Do not upgrade things that have the hold flag set.
- if (I->SelectedState == State::Hold)
+
+ for (pkgCache::VerIterator Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
{
- State.InstallVer = I.CurrentVer();
- State.Mode = ModeKeep;
+ if (TargetVer.SimilarVer(Ver) == false)
+ continue;
+ SetCandidateVersion(Ver, false);
+ break;
}
- State.Update(I,*this);
- }
-
- Update();
-
- /* Initialize the touchd array. Bit 0 is the old install state bit 1
- is the touch value */
- unsigned char *Touch = new unsigned char[Head().PackageCount];
- for (unsigned int I = 0; I != Head().PackageCount; I++)
- {
- if ((PkgState[I].DepState & DepNowMin) == DepNowMin)
- Touch[I] = (1 << 0) | (1 << 1);
- else
- Touch[I] = 1 << 1;
}
+}
+
+void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto)
+{
+ StateCache &state = PkgState[Pkg->ID];
+
+ ActionGroup group(*this);
- // Now downgrade everything that is broken
- ResolveConflicts(Touch);
- delete [] Touch;
+ if(Auto)
+ state.Flags |= Flag::Auto;
+ else
+ state.Flags &= ~Flag::Auto;
}
/*}}}*/
-#endif
-
// StateCache::Update - Compute the various static display things /*{{{*/
// ---------------------------------------------------------------------
/* This is called whenever the Candidate version changes. */
return Ver;
}
/*}}}*/
+// Policy::GetCandidateVer - Returns the Candidate install version /*{{{*/
+// ---------------------------------------------------------------------
+/* The default just returns the highest available version that is not
+ a source and automatic. */
+pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator const &Pkg)
+{
+ /* Not source/not automatic versions cannot be a candidate version
+ unless they are already installed */
+ VerIterator Last(*(pkgCache *)this,0);
+
+ for (VerIterator I = Pkg.VersionList(); I.end() == false; I++)
+ {
+ if (Pkg.CurrentVer() == I)
+ return I;
+
+ for (VerFileIterator J = I.FileList(); J.end() == false; J++)
+ {
+ if ((J.File()->Flags & Flag::NotSource) != 0)
+ continue;
+
+ /* Stash the highest version of a not-automatic source, we use it
+ if there is nothing better */
+ if ((J.File()->Flags & Flag::NotAutomatic) != 0)
+ {
+ if (Last.end() == true)
+ Last = I;
+ continue;
+ }
+
+ return I;
+ }
+ }
+
+ return Last;
+}
+ /*}}}*/
+// Policy::IsImportantDep - True if the dependency is important /*{{{*/
+// ---------------------------------------------------------------------
+/* */
+bool pkgDepCache::Policy::IsImportantDep(DepIterator const &Dep)
+{
+ if(Dep.IsCritical())
+ return true;
+ else if(Dep->Type == pkgCache::Dep::Recommends)
+ {
+ if ( _config->FindB("APT::Install-Recommends", false))
+ return true;
+ // we suport a special mode to only install-recommends for certain
+ // sections
+ // FIXME: this is a meant as a temporarly solution until the
+ // recommends are cleaned up
+ const char *sec = Dep.ParentVer().Section();
+ if (sec && ConfigValueInSubTree("APT::Install-Recommends-Sections", sec))
+ return true;
+ }
+ else if(Dep->Type == pkgCache::Dep::Suggests)
+ return _config->FindB("APT::Install-Suggests", false);
+
+ return false;
+}
+ /*}}}*/
+pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc() /*{{{*/
+{
+ DefaultRootSetFunc *f = new DefaultRootSetFunc;
+ if(f->wasConstructedSuccessfully())
+ return f;
+ else
+ {
+ delete f;
+ return NULL;
+ }
+}
+ /*}}}*/
+bool pkgDepCache::MarkFollowsRecommends()
+{
+ return _config->FindB("APT::AutoRemove::RecommendsImportant", true);
+}
+
+bool pkgDepCache::MarkFollowsSuggests()
+{
+ return _config->FindB("APT::AutoRemove::SuggestsImportant", false);
+}
+
+// pkgDepCache::MarkRequired - the main mark algorithm /*{{{*/
+bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc)
+{
+ bool follow_recommends;
+ bool follow_suggests;
+ bool debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false);
+
+ // init the states
+ for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+ {
+ PkgState[p->ID].Marked = false;
+ PkgState[p->ID].Garbage = false;
+
+ // debug output
+ if(debug_autoremove && PkgState[p->ID].Flags & Flag::Auto)
+ std::clog << "AutoDep: " << p.FullName() << std::endl;
+ }
+
+ // init vars
+ follow_recommends = MarkFollowsRecommends();
+ follow_suggests = MarkFollowsSuggests();
+
+
+
+ // do the mark part, this is the core bit of the algorithm
+ for(PkgIterator p = PkgBegin(); !p.end(); ++p)
+ {
+ if(!(PkgState[p->ID].Flags & Flag::Auto) ||
+ (p->Flags & Flag::Essential) ||
+ userFunc.InRootSet(p) ||
+ // be nice even then a required package violates the policy (#583517)
+ // and do the full mark process also for required packages
+ (p.CurrentVer().end() != true &&
+ p.CurrentVer()->Priority == pkgCache::State::Required))
+ {
+ // the package is installed (and set to keep)
+ if(PkgState[p->ID].Keep() && !p.CurrentVer().end())
+ MarkPackage(p, p.CurrentVer(),
+ follow_recommends, follow_suggests);
+ // the package is to be installed
+ else if(PkgState[p->ID].Install())
+ MarkPackage(p, PkgState[p->ID].InstVerIter(*this),
+ follow_recommends, follow_suggests);
+ }
+ }
+
+ return true;
+}
+ /*}}}*/
+// MarkPackage - mark a single package in Mark-and-Sweep /*{{{*/
+void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg,
+ const pkgCache::VerIterator &ver,
+ bool const &follow_recommends,
+ bool const &follow_suggests)
+{
+ pkgDepCache::StateCache &state = PkgState[pkg->ID];
+
+ // if we are marked already we are done
+ if(state.Marked)
+ return;
+
+ VerIterator const currver = pkg.CurrentVer();
+ VerIterator const candver = state.CandidateVerIter(*this);
+ VerIterator const instver = state.InstVerIter(*this);
+
+#if 0
+ // If a package was garbage-collected but is now being marked, we
+ // should re-select it
+ // For cases when a pkg is set to upgrade and this trigger the
+ // removal of a no-longer used dependency. if the pkg is set to
+ // keep again later it will result in broken deps
+ if(state.Delete() && state.RemoveReason = Unused)
+ {
+ if(ver==candver)
+ mark_install(pkg, false, false, NULL);
+ else if(ver==pkg.CurrentVer())
+ MarkKeep(pkg, false, false);
+
+ instver=state.InstVerIter(*this);
+ }
+#endif
+
+ // For packages that are not going to be removed, ignore versions
+ // other than the InstVer. For packages that are going to be
+ // removed, ignore versions other than the current version.
+ if(!(ver == instver && !instver.end()) &&
+ !(ver == currver && instver.end() && !ver.end()))
+ return;
+
+ bool const debug_autoremove = _config->FindB("Debug::pkgAutoRemove", false);
+
+ if(debug_autoremove)
+ {
+ std::clog << "Marking: " << pkg.FullName();
+ if(!ver.end())
+ std::clog << " " << ver.VerStr();
+ if(!currver.end())
+ std::clog << ", Curr=" << currver.VerStr();
+ if(!instver.end())
+ std::clog << ", Inst=" << instver.VerStr();
+ std::clog << std::endl;
+ }
+
+ state.Marked=true;
+
+ if(ver.end() == true)
+ return;
+
+ // If the version belongs to a Multi-Arch all package
+ // we will mark all others in this Group with this version also
+ if (ver->MultiArch == pkgCache::Version::All &&
+ strcmp(ver.Arch(true), "all") == 0)
+ {
+ GrpIterator G = pkg.Group();
+ const char* const VerStr = ver.VerStr();
+ for (PkgIterator P = G.FindPkg("any");
+ P.end() != true; P = G.NextPkg(P))
+ {
+ for (VerIterator V = P.VersionList();
+ V.end() != true; ++V)
+ {
+ if (ver->Hash != V->Hash ||
+ strcmp(VerStr, V.VerStr()) != 0)
+ continue;
+ MarkPackage(P, V, follow_recommends, follow_suggests);
+ break;
+ }
+ }
+ }
+
+ for(DepIterator d = ver.DependsList(); !d.end(); ++d)
+ {
+ if(d->Type == Dep::Depends ||
+ d->Type == Dep::PreDepends ||
+ (follow_recommends &&
+ d->Type == Dep::Recommends) ||
+ (follow_suggests &&
+ d->Type == Dep::Suggests))
+ {
+ // Try all versions of this package.
+ for(VerIterator V = d.TargetPkg().VersionList();
+ !V.end(); ++V)
+ {
+ if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer()))
+ {
+ if(debug_autoremove)
+ {
+ std::clog << "Following dep: " << d.ParentPkg().FullName()
+ << " " << d.ParentVer().VerStr() << " "
+ << d.DepType() << " " << d.TargetPkg().FullName();
+ if((d->CompareOp & ~pkgCache::Dep::Or) != pkgCache::Dep::NoOp)
+ {
+ std::clog << " (" << d.CompType() << " "
+ << d.TargetVer() << ")";
+ }
+ std::clog << std::endl;
+ }
+ MarkPackage(V.ParentPkg(), V,
+ follow_recommends, follow_suggests);
+ }
+ }
+ // Now try virtual packages
+ for(PrvIterator prv=d.TargetPkg().ProvidesList();
+ !prv.end(); ++prv)
+ {
+ if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp,
+ d.TargetVer()))
+ {
+ if(debug_autoremove)
+ {
+ std::clog << "Following dep: " << d.ParentPkg().FullName() << " "
+ << d.ParentVer().VerStr() << " "
+ << d.DepType() << " " << d.TargetPkg().FullName() << " ";
+ if((d->CompareOp & ~pkgCache::Dep::Or) != pkgCache::Dep::NoOp)
+ {
+ std::clog << " (" << d.CompType() << " "
+ << d.TargetVer() << ")";
+ }
+ std::clog << ", provided by "
+ << prv.OwnerPkg().FullName() << " "
+ << prv.OwnerVer().VerStr()
+ << std::endl;
+ }
+
+ MarkPackage(prv.OwnerPkg(), prv.OwnerVer(),
+ follow_recommends, follow_suggests);
+ }
+ }
+ }
+ }
+}
+ /*}}}*/
+bool pkgDepCache::Sweep() /*{{{*/
+{
+ bool debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false);
+
+ // do the sweep
+ for(PkgIterator p=PkgBegin(); !p.end(); ++p)
+ {
+ StateCache &state=PkgState[p->ID];
+
+ // skip required packages
+ if (!p.CurrentVer().end() &&
+ (p.CurrentVer()->Priority == pkgCache::State::Required))
+ continue;
+
+ // if it is not marked and it is installed, it's garbage
+ if(!state.Marked && (!p.CurrentVer().end() || state.Install()))
+ {
+ state.Garbage=true;
+ if(debug_autoremove)
+ std::clog << "Garbage: " << p.FullName() << std::endl;
+ }
+ }
+
+ return true;
+}
+ /*}}}*/