X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/a246f2dc8c6084d857edb2792ce6b37c794e3d69..8d3489abd90a64715b92976a7c60b42a386a4c5c:/apt-pkg/depcache.cc diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index a0896b3dc..bc663a8e9 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -1,6 +1,6 @@ // -*- 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. @@ -8,23 +8,88 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ -#ifdef __GNUG__ -#pragma implementation "apt-pkg/depcache.h" -#endif #include - #include #include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include /*}}}*/ +// 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 /*{{{*/ @@ -34,6 +99,7 @@ pkgDepCache::~pkgDepCache() { delete [] PkgState; delete [] DepState; + delete delLocalPolicy; } /*}}}*/ // DepCache::Init - Generate the initial extra structures. /*{{{*/ @@ -41,18 +107,22 @@ pkgDepCache::~pkgDepCache() /* 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 @@ -60,13 +130,13 @@ bool pkgDepCache::Init(OpProgress *Prog) 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(); @@ -80,50 +150,181 @@ bool pkgDepCache::Init(OpProgress *Prog) 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 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 @@ -137,36 +338,39 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) /* 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 @@ -191,7 +395,7 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) } // 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; @@ -203,30 +407,120 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) /*}}}*/ // 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 /*{{{*/ @@ -241,9 +535,11 @@ void pkgDepCache::AddStates(const PkgIterator &Pkg,int Add) { 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) @@ -252,16 +548,24 @@ void pkgDepCache::AddStates(const PkgIterator &Pkg,int Add) // 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; } @@ -289,17 +593,22 @@ void pkgDepCache::BuildGroupOrs(VerIterator const &V) /* 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; } } @@ -395,6 +704,107 @@ void pkgDepCache::UpdateVerState(PkgIterator Pkg) } } /*}}}*/ +// 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 &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 @@ -408,9 +818,13 @@ void pkgDepCache::Update(OpProgress *Prog) iKeepCount = 0; iBrokenCount = 0; iBadCount = 0; - + + std::set 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) @@ -418,12 +832,12 @@ void pkgDepCache::Update(OpProgress *Prog) 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; @@ -432,25 +846,166 @@ void pkgDepCache::Update(OpProgress *Prog) 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::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::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 &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::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 &recheck) +{ + std::vector static const Archs = APT::Configuration::getArchitectures(); + pkgCache::GrpIterator Grp(*Cache, Cache->GrpP + G); + if (unlikely(Grp.end() == true)) + return false; + for (std::vector::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 @@ -460,9 +1015,11 @@ void pkgDepCache::Update(DepIterator D) 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()); @@ -475,7 +1032,7 @@ void pkgDepCache::Update(DepIterator D) /* 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); @@ -491,22 +1048,31 @@ void pkgDepCache::Update(PkgIterator const &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]; @@ -522,8 +1088,20 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) // 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); @@ -543,59 +1121,111 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) // 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 */ @@ -604,24 +1234,45 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst) 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; @@ -635,241 +1286,257 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst) /* 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 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. */ @@ -914,3 +1581,304 @@ const char *pkgDepCache::StateCache::StripEpoch(const char *Ver) 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; +} + /*}}}*/