X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/1f31762e69fd52fc67306d4b8af98c4e4519e2e4..49e1672884bf8d50713812bdd4eca26ab2076711:/apt-pkg/depcache.cc diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index d447d175e..2c6eb43bf 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -8,26 +8,36 @@ ##################################################################### */ /*}}}*/ // Include Files /*{{{*/ +#include + #include #include +#include #include #include #include - #include +#include #include +#include #include #include +#include +#include +#include #include -#include +#include #include #include -#include +#include + /*}}}*/ + +using std::string; -// helper for Install-Recommends-Sections and Never-MarkAuto-Sections +// helper for Install-Recommends-Sections and Never-MarkAuto-Sections /*{{{*/ static bool ConfigValueInSubTree(const char* SubTree, const char *needle) { @@ -46,9 +56,8 @@ ConfigValueInSubTree(const char* SubTree, const char *needle) } return false; } - - -pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) : + /*}}}*/ +pkgDepCache::ActionGroup::ActionGroup(pkgDepCache &cache) : /*{{{*/ cache(cache), released(false) { ++cache.group_level; @@ -68,7 +77,7 @@ void pkgDepCache::ActionGroup::release() cache.MarkAndSweep(); } - released = false; + released = true; } } @@ -76,13 +85,15 @@ pkgDepCache::ActionGroup::~ActionGroup() { release(); } - + /*}}}*/ // DepCache::pkgDepCache - Constructors /*{{{*/ // --------------------------------------------------------------------- /* */ pkgDepCache::pkgDepCache(pkgCache *pCache,Policy *Plcy) : group_level(0), Cache(pCache), PkgState(0), DepState(0) { + DebugMarker = _config->FindB("Debug::pkgDepCache::Marker", false); + DebugAutoInstall = _config->FindB("Debug::pkgDepCache::AutoInstall", false); delLocalPolicy = 0; LocalPolicy = Plcy; if (LocalPolicy == 0) @@ -125,9 +136,9 @@ bool pkgDepCache::Init(OpProgress *Prog) /* Set the current state of everything. In this state all of the packages are kept exactly as is. See AllUpgrade */ int Done = 0; - for (PkgIterator I = PkgBegin(); I.end() != true; I++,Done++) + 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 @@ -159,58 +170,72 @@ bool pkgDepCache::Init(OpProgress *Prog) return true; } /*}}}*/ - -bool pkgDepCache::readStateFile(OpProgress *Prog) +bool pkgDepCache::readStateFile(OpProgress *Prog) /*{{{*/ { FileFd state_file; - string state = _config->FindDir("Dir::State") + "extended_states"; - if(FileExists(state)) { + string const state = _config->FindFile("Dir::State::extended_states"); + if(RealFileExists(state)) { state_file.Open(state, FileFd::ReadOnly); - int file_size = state_file.Size(); + off_t 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; + off_t amt = 0; + bool const debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false); while(tagfile.Step(section)) { - string pkgname = section.FindS("Package"); - pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname); - // Silently ignore unknown packages and packages with no actual - // version. - if(!pkg.end() && !pkg.VersionList().end()) { - short reason = section.FindI("Auto-Installed", 0); - if(reason > 0) - PkgState[pkg->ID].Flags |= Flag::Auto; - if(_config->FindB("Debug::pkgAutoRemove",false)) - std::cout << "Auto-Installed : " << pkgname << std::endl; - amt+=section.size(); - if(Prog != NULL) - Prog->OverallProgress(amt, file_size, 1, - _("Reading state information")); + 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(file_size, file_size, 1, + Prog->OverallProgress(amt, file_size, 1, _("Reading state information")); } + if(Prog != NULL) + Prog->OverallProgress(file_size, file_size, 1, + _("Reading state information")); } return true; } - -bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) + /*}}}*/ +bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) /*{{{*/ { - if(_config->FindB("Debug::pkgAutoRemove",false)) + bool const debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false); + + if(debug_autoremove) std::clog << "pkgDepCache::writeStateFile()" << std::endl; FileFd StateFile; - string state = _config->FindDir("Dir::State") + "extended_states"; + string const state = _config->FindFile("Dir::State::extended_states"); // if it does not exist, create a empty one - if(!FileExists(state)) + if(!RealFileExists(state)) { - StateFile.Open(state, FileFd::WriteEmpty); + StateFile.Open(state, FileFd::WriteAtomic); StateFile.Close(); } @@ -220,7 +245,7 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) state.c_str()); FILE *OutFile; - string outfile = state + ".tmp"; + 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()); @@ -231,46 +256,72 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) std::set pkgs_seen; const char *nullreorderlist[] = {0}; while(tagfile.Step(section)) { - string pkgname = section.FindS("Package"); + 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); + pkgCache::PkgIterator pkg = Cache->FindPkg(pkgname, pkgarch); if(pkg.end() || pkg.VersionList().end()) continue; - bool newAuto = (PkgState[pkg->ID].Flags & Flag::Auto); + 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 exisiting AutoInstall info: " - << pkg.Name() << std::endl; - TFRewriteData rewrite[2]; - rewrite[0].Tag = "Auto-Installed"; - rewrite[0].Rewrite = newAuto ? "1" : "0"; + 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 = 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(pkgname); + 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++) { - if(PkgState[pkg->ID].Flags & Flag::Auto) { - if (pkgs_seen.find(pkg.Name()) != pkgs_seen.end()) { - if(_config->FindB("Debug::pkgAutoRemove",false)) - std::clog << "Skipping already written " << pkg.Name() << std::endl; + 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) - continue; - if(_config->FindB("Debug::pkgAutoRemove",false)) - std::clog << "Writing new AutoInstall: " - << pkg.Name() << std::endl; + // 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() + ostr << "Package: " << pkg.Name() + << "\nArchitecture: " << pkgarch << "\nAuto-Installed: 1\n\n"; - fprintf(OutFile,ostr.str().c_str()); - fprintf(OutFile,"\n"); + fprintf(OutFile,"%s",ostr.str().c_str()); } } fclose(OutFile); @@ -281,7 +332,7 @@ bool pkgDepCache::writeStateFile(OpProgress *prog, bool InstalledOnly) return true; } - + /*}}}*/ // DepCache::CheckDep - Checks a single dependency /*{{{*/ // --------------------------------------------------------------------- /* This first checks the dependency against the main target package and @@ -295,24 +346,20 @@ 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 && Dep->Type != Dep::DpkgBreaks && Dep->Type != Dep::Obsoletes)) + if (Dep.IsIgnorable(Res) == false) { PkgIterator Pkg = Dep.TargetPkg(); // Check the base package if (Type == NowVersion && Pkg->CurrentVer != 0) - if (VS().CheckDep(Pkg.CurrentVer().VerStr(),Dep->CompareOp, - Dep.TargetVer()) == true) + if (Dep.IsSatisfied(Pkg.CurrentVer()) == true) return true; if (Type == InstallVersion && PkgState[Pkg->ID].InstallVer != 0) - if (VS().CheckDep(PkgState[Pkg->ID].InstVerIter(*this).VerStr(), - Dep->CompareOp,Dep.TargetVer()) == true) + if (Dep.IsSatisfied(PkgState[Pkg->ID].InstVerIter(*this)) == true) return true; if (Type == CandidateVersion && PkgState[Pkg->ID].CandidateVer != 0) - if (VS().CheckDep(PkgState[Pkg->ID].CandidateVerIter(*this).VerStr(), - Dep->CompareOp,Dep.TargetVer()) == true) + if (Dep.IsSatisfied(PkgState[Pkg->ID].CandidateVerIter(*this)) == true) return true; } @@ -321,15 +368,11 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) // Check the providing packages PrvIterator P = Dep.TargetPkg().ProvidesList(); - PkgIterator Pkg = Dep.ParentPkg(); - for (; P.end() != true; P++) + 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 || Dep->Type == Dep::DpkgBreaks)) + if (Dep.IsIgnorable(P) == true) continue; - + // Check if the provides is a hit if (Type == NowVersion) { @@ -352,7 +395,7 @@ bool pkgDepCache::CheckDep(DepIterator Dep,int Type,PkgIterator &Res) } // Compare the versions. - if (VS().CheckDep(P.ProvideVersion(),Dep->CompareOp,Dep.TargetVer()) == true) + if (Dep.IsSatisfied(P) == true) { Res = P.OwnerPkg(); return true; @@ -364,8 +407,8 @@ 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,signed long Mult) +/* Call with Inverse = true to preform the inverse opration */ +void pkgDepCache::AddSizes(const PkgIterator &Pkg, bool const Inverse) { StateCache &P = PkgState[Pkg->ID]; @@ -379,8 +422,13 @@ void pkgDepCache::AddSizes(const PkgIterator &Pkg,signed long Mult) // Compute the size data if (P.NewInstall() == true) { - iUsrSize += (signed)(Mult*P.InstVerIter(*this)->InstalledSize); - iDownloadSize += (signed)(Mult*P.InstVerIter(*this)->Size); + 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; } @@ -389,9 +437,15 @@ void pkgDepCache::AddSizes(const PkgIterator &Pkg,signed long Mult) (P.InstallVer != (Version *)Pkg.CurrentVer() || (P.iFlags & ReInstall) == ReInstall) && P.InstallVer != 0) { - iUsrSize += (signed)(Mult*((signed)P.InstVerIter(*this)->InstalledSize - - (signed)Pkg.CurrentVer()->InstalledSize)); - iDownloadSize += (signed)(Mult*P.InstVerIter(*this)->Size); + 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; } @@ -399,14 +453,20 @@ void pkgDepCache::AddSizes(const PkgIterator &Pkg,signed long Mult) if (Pkg.State() == pkgCache::PkgIterator::NeedsUnpack && P.Delete() == false) { - iDownloadSize += (signed)(Mult*P.InstVerIter(*this)->Size); + if (Inverse == false) + iDownloadSize += P.InstVerIter(*this)->Size; + else + iDownloadSize -= P.InstVerIter(*this)->Size; return; } // Removing if (Pkg->CurrentVer != 0 && P.InstallVer == 0) { - iUsrSize -= (signed)(Mult*Pkg.CurrentVer()->InstalledSize); + if (Inverse == false) + iUsrSize -= Pkg.CurrentVer()->InstalledSize; + else + iUsrSize += Pkg.CurrentVer()->InstalledSize; return; } } @@ -419,8 +479,9 @@ void pkgDepCache::AddSizes(const PkgIterator &Pkg,signed long Mult) calld Remove/Add itself. Remember, dependencies can be circular so while processing a dep for Pkg it is possible that Add/Remove will be called on Pkg */ -void pkgDepCache::AddStates(const PkgIterator &Pkg,int Add) +void pkgDepCache::AddStates(const PkgIterator &Pkg, bool const Invert) { + signed char const Add = (Invert == false) ? 1 : -1; StateCache &State = PkgState[Pkg->ID]; // The Package is broken (either minimal dep or policy dep) @@ -436,8 +497,8 @@ 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) + if (State.Mode == ModeDelete && + (State.iFlags & Purge) == Purge && Pkg.Purge() == false) iDelCount += Add; if (State.Mode == ModeInstall) @@ -474,16 +535,14 @@ void pkgDepCache::BuildGroupOrs(VerIterator const &V) { unsigned char Group = 0; - for (DepIterator D = V.DependsList(); D.end() != true; D++) + for (DepIterator D = V.DependsList(); D.end() != true; ++D) { // Build the dependency state. unsigned char &State = DepState[D->ID]; /* Invert for Conflicts. We have to do this twice to get the right sense for a conflicts group */ - if (D->Type == Dep::Conflicts || - D->Type == Dep::DpkgBreaks || - D->Type == Dep::Obsoletes) + if (D.IsNegative() == true) State = ~State; // Add to the group if we are within an or.. @@ -494,9 +553,7 @@ void pkgDepCache::BuildGroupOrs(VerIterator const &V) Group = 0; // Invert for Conflicts - if (D->Type == Dep::Conflicts || - D->Type == Dep::DpkgBreaks || - D->Type == Dep::Obsoletes) + if (D.IsNegative() == true) State = ~State; } } @@ -518,7 +575,7 @@ unsigned char pkgDepCache::VersionState(DepIterator D,unsigned char Check, // Compute a single dependency element (glob or) DepIterator Start = D; unsigned char State = 0; - for (bool LastOR = true; D.end() == false && LastOR == true; D++) + for (bool LastOR = true; D.end() == false && LastOR == true; ++D) { State |= DepState[D->ID]; LastOR = (D->CompareOp & Dep::Or) == Dep::Or; @@ -605,18 +662,18 @@ void pkgDepCache::Update(OpProgress *Prog) iKeepCount = 0; iBrokenCount = 0; iBadCount = 0; - + // Perform the depends pass int Done = 0; - for (PkgIterator I = PkgBegin(); I.end() != true; I++,Done++) + for (PkgIterator I = PkgBegin(); I.end() != true; ++I, ++Done) { if (Prog != 0 && Done%20 == 0) Prog->Progress(Done); - for (VerIterator V = I.VersionList(); V.end() != true; V++) + for (VerIterator V = I.VersionList(); V.end() != true; ++V) { unsigned char Group = 0; - - for (DepIterator D = V.DependsList(); D.end() != true; D++) + + for (DepIterator D = V.DependsList(); D.end() != true; ++D) { // Build the dependency state. unsigned char &State = DepState[D->ID]; @@ -629,20 +686,18 @@ void pkgDepCache::Update(OpProgress *Prog) Group = 0; // Invert for Conflicts - if (D->Type == Dep::Conflicts || - D->Type == Dep::DpkgBreaks || - D->Type == Dep::Obsoletes) + if (D.IsNegative() == true) 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 (Prog != 0) + if (Prog != 0) Prog->Progress(Done); readStateFile(Prog); @@ -655,15 +710,13 @@ void pkgDepCache::Update(OpProgress *Prog) void pkgDepCache::Update(DepIterator D) { // Update the reverse deps - for (;D.end() != true; D++) + for (;D.end() != true; ++D) { unsigned char &State = DepState[D->ID]; State = DependencyState(D); // Invert for Conflicts - if (D->Type == Dep::Conflicts || - D->Type == Dep::DpkgBreaks || - D->Type == Dep::Obsoletes) + if (D.IsNegative() == true) State = ~State; RemoveStates(D.ParentPkg()); @@ -690,51 +743,46 @@ void pkgDepCache::Update(PkgIterator const &Pkg) // Update the provides map for the current ver if (Pkg->CurrentVer != 0) for (PrvIterator P = Pkg.CurrentVer().ProvidesList(); - P.end() != true; P++) + P.end() != true; ++P) Update(P.ParentPkg().RevDependsList()); // Update the provides map for the candidate ver if (PkgState[Pkg->ID].CandidateVer != 0) for (PrvIterator P = PkgState[Pkg->ID].CandidateVerIter(*this).ProvidesList(); - P.end() != true; P++) + 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, bool FromUser) +bool pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser, + unsigned long Depth) { - // Simplifies other routines. - if (Pkg.end() == true) - return; + if (IsModeChangeOk(ModeKeep, Pkg, Depth, FromUser) == false) + return false; /* 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); + return false; /* We changed the soft state all the time so the UI is a bit nicer to use */ StateCache &P = PkgState[Pkg->ID]; + + // Check that it is not already kept + if (P.Mode == ModeKeep) + return true; + if (Soft == true) P.iFlags |= AutoKept; else P.iFlags &= ~AutoKept; - - // Check that it is not already kept - if (P.Mode == ModeKeep) - return; - // We dont even try to keep virtual packages.. - if (Pkg->VersionList == 0) - return; + ActionGroup group(*this); + #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 @@ -746,6 +794,9 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser) P.Flags &= ~Flag::Auto; #endif + if (DebugMarker == true) + std::clog << OutputInDepth(Depth) << "MarkKeep " << Pkg << " FU=" << FromUser << std::endl; + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -756,36 +807,40 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg, bool Soft, bool FromUser) P.InstallVer = Pkg.CurrentVer(); AddStates(Pkg); - Update(Pkg); - AddSizes(Pkg); + + return true; } /*}}}*/ // DepCache::MarkDelete - Put the package in the delete state /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) +bool pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge, + unsigned long Depth, bool FromUser) { - // Simplifies other routines. - if (Pkg.end() == true) - return; + if (IsModeChangeOk(ModeDelete, Pkg, Depth, FromUser) == false) + return false; - ActionGroup group(*this); + StateCache &P = PkgState[Pkg->ID]; // Check that it is not already marked for delete - StateCache &P = PkgState[Pkg->ID]; + if ((P.Mode == ModeDelete || P.InstallVer == 0) && + (Pkg.Purge() == true || rPurge == false)) + return true; + + // check if we are allowed to remove the package + if (IsDeleteOk(Pkg,rPurge,Depth,FromUser) == false) + return false; + 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; + + ActionGroup group(*this); + + if (DebugMarker == true) + std::clog << OutputInDepth(Depth) << (rPurge ? "MarkPurge " : "MarkDelete ") << Pkg << " FU=" << FromUser << std::endl; RemoveSizes(Pkg); RemoveStates(Pkg); @@ -799,43 +854,212 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) AddStates(Pkg); Update(Pkg); AddSizes(Pkg); + + return true; +} + /*}}}*/ +// DepCache::IsDeleteOk - check if it is ok to remove this package /*{{{*/ +// --------------------------------------------------------------------- +/* The default implementation tries to prevent deletion of install requests. + dpkg holds are enforced by the private IsModeChangeOk */ +bool pkgDepCache::IsDeleteOk(PkgIterator const &Pkg,bool rPurge, + unsigned long Depth, bool FromUser) +{ + return IsDeleteOkProtectInstallRequests(Pkg, rPurge, Depth, FromUser); +} +bool pkgDepCache::IsDeleteOkProtectInstallRequests(PkgIterator const &Pkg, + bool const rPurge, unsigned long const Depth, bool const FromUser) +{ + if (FromUser == false && Pkg->CurrentVer == 0) + { + StateCache &P = PkgState[Pkg->ID]; + if (P.InstallVer != 0 && P.Status == 2 && (P.Flags & Flag::Auto) != Flag::Auto) + { + if (DebugMarker == true) + std::clog << OutputInDepth(Depth) << "Manual install request prevents MarkDelete of " << Pkg << std::endl; + return false; + } + } + return true; +} + /*}}}*/ +// DepCache::IsModeChangeOk - check if it is ok to change the mode /*{{{*/ +// --------------------------------------------------------------------- +/* this is used by all Mark methods on the very first line to check sanity + and prevents mode changes for packages on hold for example. + If you want to check Mode specific stuff you can use the virtual public + IsOk methods instead */ +char const* PrintMode(char const mode) +{ + switch (mode) + { + case pkgDepCache::ModeInstall: return "Install"; + case pkgDepCache::ModeKeep: return "Keep"; + case pkgDepCache::ModeDelete: return "Delete"; + default: return "UNKNOWN"; + } +} +bool pkgDepCache::IsModeChangeOk(ModeList const mode, PkgIterator const &Pkg, + unsigned long const Depth, bool const FromUser) +{ + // we are not trying to hard… + if (unlikely(Depth > 100)) + return false; + + // general sanity + if (unlikely(Pkg.end() == true || Pkg->VersionList == 0)) + return false; + + // the user is always right + if (FromUser == true) + return true; + + StateCache &P = PkgState[Pkg->ID]; + // not changing the mode is obviously also fine as we might want to call + // e.g. MarkInstall multiple times with different arguments for the same package + if (P.Mode == mode) + return true; + + // if previous state was set by user only user can reset it + if ((P.iFlags & Protected) == Protected) + { + if (unlikely(DebugMarker == true)) + std::clog << OutputInDepth(Depth) << "Ignore Mark" << PrintMode(mode) + << " of " << Pkg << " as its mode (" << PrintMode(P.Mode) + << ") is protected" << std::endl; + return false; + } + // enforce dpkg holds + else if (mode != ModeKeep && Pkg->SelectedState == pkgCache::State::Hold && + _config->FindB("APT::Ignore-Hold",false) == false) + { + if (unlikely(DebugMarker == true)) + std::clog << OutputInDepth(Depth) << "Hold prevents Mark" << PrintMode(mode) + << " of " << Pkg << std::endl; + return false; + } + + return true; } /*}}}*/ // DepCache::MarkInstall - Put the package in the install state /*{{{*/ // --------------------------------------------------------------------- /* */ -void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, +struct CompareProviders { + pkgCache::PkgIterator const Pkg; + CompareProviders(pkgCache::DepIterator const &Dep) : Pkg(Dep.TargetPkg()) {}; + //bool operator() (APT::VersionList::iterator const &AV, APT::VersionList::iterator const &BV) + bool operator() (pkgCache::VerIterator const &AV, pkgCache::VerIterator const &BV) + { + pkgCache::PkgIterator const A = AV.ParentPkg(); + pkgCache::PkgIterator const B = BV.ParentPkg(); + // Prefer MA:same packages if other architectures for it are installed + if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same || + (BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same) + { + bool instA = false; + if ((AV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same) + { + pkgCache::GrpIterator Grp = A.Group(); + for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P)) + if (P->CurrentVer != 0) + { + instA = true; + break; + } + } + bool instB = false; + if ((BV->MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same) + { + pkgCache::GrpIterator Grp = B.Group(); + for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P)) + { + if (P->CurrentVer != 0) + { + instB = true; + break; + } + } + } + if (instA != instB) + return instA == false; + } + // Prefer packages in the same group as the target; e.g. foo:i386, foo:amd64 + if (A->Group != B->Group) + { + if (A->Group == Pkg->Group && B->Group != Pkg->Group) + return false; + else if (B->Group == Pkg->Group && A->Group != Pkg->Group) + return true; + } + // we like essentials + if ((A->Flags & pkgCache::Flag::Essential) != (B->Flags & pkgCache::Flag::Essential)) + { + if ((A->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) + return false; + else if ((B->Flags & pkgCache::Flag::Essential) == pkgCache::Flag::Essential) + return true; + } + if ((A->Flags & pkgCache::Flag::Important) != (B->Flags & pkgCache::Flag::Important)) + { + if ((A->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) + return false; + else if ((B->Flags & pkgCache::Flag::Important) == pkgCache::Flag::Important) + return true; + } + // higher priority seems like a good idea + if (AV->Priority != BV->Priority) + return AV->Priority > BV->Priority; + // prefer native architecture + if (strcmp(A.Arch(), B.Arch()) != 0) + { + if (strcmp(A.Arch(), A.Cache()->NativeArch()) == 0) + return false; + else if (strcmp(B.Arch(), B.Cache()->NativeArch()) == 0) + return true; + std::vector archs = APT::Configuration::getArchitectures(); + for (std::vector::const_iterator a = archs.begin(); a != archs.end(); ++a) + if (*a == A.Arch()) + return false; + else if (*a == B.Arch()) + return true; + } + // unable to decide… + return A->ID < B->ID; + } +}; +bool 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); + if (IsModeChangeOk(ModeInstall, Pkg, Depth, FromUser) == false) + return false; + + StateCache &P = PkgState[Pkg->ID]; + + // See if there is even any possible instalation candidate + if (P.CandidateVer == 0) + return false; /* 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.InstPolicyBroken() == false && P.InstBroken() == false) && (P.Mode == ModeInstall || P.CandidateVer == (Version *)Pkg.CurrentVer())) { if (P.CandidateVer == (Version *)Pkg.CurrentVer() && P.InstallVer == 0) - MarkKeep(Pkg, false, FromUser); - return; + return MarkKeep(Pkg, false, FromUser, Depth+1); + return true; } - // 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 we haven't already) + if (P.Mode != ModeInstall || P.InstallVer != P.CandidateVer) + if (IsInstallOk(Pkg,AutoInst,Depth,FromUser) == false) + return false; + + ActionGroup group(*this); + P.iFlags &= ~AutoKept; + /* 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 */ @@ -847,9 +1071,10 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, 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)) + // Set it to manual if it's a new install or already installed, + // but only if its not marked by the autoremover (aptitude depend on this behavior) + // or if we do automatic installation (aptitude never does it) + if(P.Status == 2 || (Pkg->CurrentVer != 0 && (AutoInst == true || P.Marked == false))) P.Flags &= ~Flag::Auto; } else @@ -864,9 +1089,12 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, AddStates(Pkg); Update(Pkg); AddSizes(Pkg); - - if (AutoInst == false) - return; + + if (AutoInst == false || _config->Find("APT::Solver", "internal") != "internal") + return true; + + if (DebugMarker == true) + std::clog << OutputInDepth(Depth) << "MarkInstall " << Pkg << " FU=" << FromUser << std::endl; DepIterator Dep = P.InstVerIter(*this).DependsList(); for (; Dep.end() != true;) @@ -875,7 +1103,7 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, DepIterator Start = Dep; bool Result = true; unsigned Ors = 0; - for (bool LastOR = true; Dep.end() == false && LastOR == true; Dep++,Ors++) + for (bool LastOR = true; Dep.end() == false && LastOR == true; ++Dep, ++Ors) { LastOR = (Dep->CompareOp & Dep::Or) == Dep::Or; @@ -894,125 +1122,206 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, */ if (IsImportantDep(Start) == false) continue; - - /* check if any ImportantDep() (but not Critial) where added - * since we installed the package + + /* 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; + if (Ors == 1 && (DepState[Start->ID] &DepCVer) != DepCVer && Start.IsNegative() == false) + { + if(DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) << Start << " can't be satisfied!" << std::endl; + if (Start.IsCritical() == false) + continue; + // if the dependency was critical, we can't install it, so remove it again + MarkDelete(Pkg,false,Depth + 1, false); + return 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; - if(!ForceImportantDeps && !Start.IsCritical()) + if (Pkg->CurrentVer != 0 && ForceImportantDeps == false && Start.IsCritical() == false) { - bool found=false; - VerIterator instVer = Pkg.CurrentVer(); - if(!instVer.end()) + bool isNewImportantDep = true; + bool isPreviouslySatisfiedImportantDep = false; + for (DepIterator D = Pkg.CurrentVer().DependsList(); D.end() != true; ++D) { - for (DepIterator D = instVer.DependsList(); D.end() != true; D++) - { - //FIXME: deal better with or-groups(?) - DepIterator LocalStart = D; - - if(IsImportantDep(D) && Start.TargetPkg() == D.TargetPkg()) - found=true; - } - // this is a new dep if it was not found to be already - // a important dep of the installed pacakge - isNewImportantDep = !found; + //FIXME: Should we handle or-group better here? + // We do not check if the package we look for is part of the same or-group + // we might find while searching, but could that really be a problem? + if (D.IsCritical() == true || IsImportantDep(D) == false || + Start.TargetPkg() != D.TargetPkg()) + continue; + + isNewImportantDep = false; + + while ((D->CompareOp & Dep::Or) != 0) + ++D; + + isPreviouslySatisfiedImportantDep = (((*this)[D] & DepGNow) != 0); + if (isPreviouslySatisfiedImportantDep == true) + break; + } + + if(isNewImportantDep == true) + { + if (DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) << "new important dependency: " + << Start.TargetPkg().FullName() << std::endl; + } + else if(isPreviouslySatisfiedImportantDep == true) + { + if (DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) << "previously satisfied important dependency on " + << Start.TargetPkg().FullName() << std::endl; + } + else + { + if (DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) << "ignore old unsatisfied important dependency on " + << Start.TargetPkg().FullName() << std::endl; + continue; } } - if(isNewImportantDep) - if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) - std::clog << "new important dependency: " - << Start.TargetPkg().Name() << std::endl; - - // skip important deps if the package is already installed - if (Pkg->CurrentVer != 0 && Start.IsCritical() == false - && !isNewImportantDep && !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++; /* 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) + fixing the problem for "positive" dependencies */ + if (Start.IsNegative() == false && (DepState[Start->ID] & DepCVer) == DepCVer) { - // Right, find the best version to install.. - Version **Cur = List; - PkgIterator P = Start.TargetPkg(); - PkgIterator InstPkg(*Cache,0); - - // See if there are direct matches (at the start of the list) - for (; *Cur != 0 && (*Cur)->ParentPkg == P.Index(); Cur++) + APT::VersionList verlist; + pkgCache::VerIterator Cand = PkgState[Start.TargetPkg()->ID].CandidateVerIter(*this); + if (Cand.end() == false && Start.IsSatisfied(Cand) == true) + verlist.insert(Cand); + for (PrvIterator Prv = Start.TargetPkg().ProvidesList(); Prv.end() != true; ++Prv) { - PkgIterator Pkg(*Cache,Cache->PkgP + (*Cur)->ParentPkg); - if (PkgState[Pkg->ID].CandidateVer != *Cur) + pkgCache::VerIterator V = Prv.OwnerVer(); + pkgCache::VerIterator Cand = PkgState[Prv.OwnerPkg()->ID].CandidateVerIter(*this); + if (Cand.end() == true || V != Cand || Start.IsSatisfied(Prv) == false) continue; - InstPkg = Pkg; - break; + verlist.insert(Cand); } + CompareProviders comp(Start); - // 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; + do { + APT::VersionList::iterator InstVer = std::max_element(verlist.begin(), verlist.end(), comp); + + if (InstVer == verlist.end()) break; - } - } - - if (InstPkg.end() == false) - { - if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) - std::clog << "Installing " << InstPkg.Name() - << " as dep of " << Pkg.Name() + + pkgCache::PkgIterator InstPkg = InstVer.ParentPkg(); + 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(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) - std::clog << "Setting NOT as auto-installed (direct dep of pkg in APT::Never-MarkAuto-Section)" << 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; - } - } + if (MarkInstall(InstPkg, true, Depth + 1, false, ForceImportantDeps) == false) + { + verlist.erase(InstVer); + continue; + } + // now check if we should consider it a automatic dependency or not + if(InstPkg->CurrentVer == 0 && Pkg->Section != 0 && 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; + MarkAuto(InstPkg, false); + } + break; + } while(true); continue; } - - /* 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) + /* Negative dependencies have no or-group + If the dependency isn't versioned, we try if an upgrade might solve the problem. + Otherwise we remove the offender if needed */ + else if (Start.IsNegative() == true && Start->Type != pkgCache::Dep::Obsoletes) { + SPtrArray List = Start.AllTargets(); + pkgCache::PkgIterator TrgPkg = Start.TargetPkg(); for (Version **I = List; *I != 0; I++) { VerIterator Ver(*this,*I); PkgIterator Pkg = Ver.ParentPkg(); - if (Start->Type != Dep::DpkgBreaks) - MarkDelete(Pkg); - else - if (PkgState[Pkg->ID].CandidateVer != *I) - MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps); + /* The List includes all packages providing this dependency, + even providers which are not installed, so skip them. */ + if (PkgState[Pkg->ID].InstallVer == 0) + continue; + + if ((Start->Version != 0 || TrgPkg != Pkg) && + PkgState[Pkg->ID].CandidateVer != PkgState[Pkg->ID].InstallVer && + PkgState[Pkg->ID].CandidateVer != *I && + MarkInstall(Pkg,true,Depth + 1, false, ForceImportantDeps) == true) + continue; + else if (Start->Type == pkgCache::Dep::Conflicts || + Start->Type == pkgCache::Dep::DpkgBreaks) + { + if(DebugAutoInstall == true) + std::clog << OutputInDepth(Depth) + << " Removing: " << Pkg.Name() + << std::endl; + if (MarkDelete(Pkg,false,Depth + 1, false) == false) + break; + } } continue; - } + } } + + return Dep.end() == true; +} + /*}}}*/ +// DepCache::IsInstallOk - check if it is ok to install this package /*{{{*/ +// --------------------------------------------------------------------- +/* The default implementation checks if the installation of an M-A:same + package would lead us into a version-screw and if so forbids it. + dpkg holds are enforced by the private IsModeChangeOk */ +bool pkgDepCache::IsInstallOk(PkgIterator const &Pkg,bool AutoInst, + unsigned long Depth, bool FromUser) +{ + return IsInstallOkMultiArchSameVersionSynced(Pkg,AutoInst, Depth, FromUser); +} +bool pkgDepCache::IsInstallOkMultiArchSameVersionSynced(PkgIterator const &Pkg, + bool const AutoInst, unsigned long const Depth, bool const FromUser) +{ + if (FromUser == true) // as always: user is always right + return true; + + // ignore packages with none-M-A:same candidates + VerIterator const CandVer = PkgState[Pkg->ID].CandidateVerIter(*this); + if (unlikely(CandVer.end() == true) || CandVer == Pkg.CurrentVer() || + (CandVer->MultiArch & pkgCache::Version::Same) != pkgCache::Version::Same) + return true; + + GrpIterator const Grp = Pkg.Group(); + for (PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P)) + { + // not installed or version synced: fine by definition + // (simple string-compare as stuff like '1' == '0:1-0' can't happen here) + if (P->CurrentVer == 0 || strcmp(Pkg.CandVersion(), P.CandVersion()) == 0) + continue; + // packages loosing M-A:same can be out-of-sync + VerIterator CV = PkgState[P->ID].CandidateVerIter(*this); + if (unlikely(CV.end() == true) || + (CV->MultiArch & pkgCache::Version::Same) != pkgCache::Version::Same) + continue; + + // not downloadable means the package is obsolete, so allow out-of-sync + if (CV.Downloadable() == false) + continue; + + PkgState[Pkg->ID].iFlags |= AutoKept; + if (unlikely(DebugMarker == true)) + std::clog << OutputInDepth(Depth) << "Ignore MarkInstall of " << Pkg + << " as its M-A:same siblings are not version-synced" << std::endl; + return false; + } + + return true; } /*}}}*/ // DepCache::SetReInstall - Set the reinstallation flag /*{{{*/ @@ -1020,19 +1329,39 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* */ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) { - ActionGroup group(*this); + if (unlikely(Pkg.end() == true)) + return; - RemoveSizes(Pkg); - RemoveStates(Pkg); - - StateCache &P = PkgState[Pkg->ID]; - if (To == true) - P.iFlags |= ReInstall; + APT::PackageList pkglist; + if (Pkg->CurrentVer != 0 && + (Pkg.CurrentVer()-> MultiArch & pkgCache::Version::Same) == pkgCache::Version::Same) + { + pkgCache::GrpIterator Grp = Pkg.Group(); + for (pkgCache::PkgIterator P = Grp.PackageList(); P.end() == false; P = Grp.NextPkg(P)) + { + if (P->CurrentVer != 0) + pkglist.insert(P); + } + } else - P.iFlags &= ~ReInstall; - - AddStates(Pkg); - AddSizes(Pkg); + pkglist.insert(Pkg); + + ActionGroup group(*this); + + for (APT::PackageList::const_iterator Pkg = pkglist.begin(); Pkg != pkglist.end(); ++Pkg) + { + RemoveSizes(Pkg); + RemoveStates(Pkg); + + StateCache &P = PkgState[Pkg->ID]; + if (To == true) + P.iFlags |= ReInstall; + else + P.iFlags &= ~ReInstall; + + AddStates(Pkg); + AddSizes(Pkg); + } } /*}}}*/ // DepCache::SetCandidateVersion - Change the candidate version /*{{{*/ @@ -1040,15 +1369,18 @@ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) /* */ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer) { - ActionGroup group(*this); - pkgCache::PkgIterator Pkg = TargetVer.ParentPkg(); StateCache &P = PkgState[Pkg->ID]; + if (P.CandidateVer == TargetVer) + return; + + ActionGroup group(*this); + RemoveSizes(Pkg); RemoveStates(Pkg); - if (P.CandidateVer == P.InstallVer) + if (P.CandidateVer == P.InstallVer && P.Install() == true) P.InstallVer = (Version *)TargetVer; P.CandidateVer = (Version *)TargetVer; P.Update(Pkg,*this); @@ -1056,8 +1388,173 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer) AddStates(Pkg); Update(Pkg); AddSizes(Pkg); + } + /*}}}*/ +// DepCache::SetCandidateRelease - Change the candidate version /*{{{*/ +// --------------------------------------------------------------------- +/* changes the candidate of a package and walks over all its dependencies + to check if it needs to change the candidate of the dependency, too, + to reach a installable versionstate */ +bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer, + std::string const &TargetRel) +{ + std::list > Changed; + return SetCandidateRelease(TargetVer, TargetRel, Changed); +} +bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer, + std::string const &TargetRel, + std::list > &Changed) +{ + ActionGroup group(*this); + SetCandidateVersion(TargetVer); + + if (TargetRel == "installed" || TargetRel == "candidate") // both doesn't make sense in this context + return true; + + pkgVersionMatch Match(TargetRel, pkgVersionMatch::Release); + // save the position of the last element we will not undo - if we have to + std::list >::iterator newChanged = --(Changed.end()); + + for (pkgCache::DepIterator D = TargetVer.DependsList(); D.end() == false; ++D) + { + if (D->Type != pkgCache::Dep::PreDepends && D->Type != pkgCache::Dep::Depends && + ((D->Type != pkgCache::Dep::Recommends && D->Type != pkgCache::Dep::Suggests) || + IsImportantDep(D) == false)) + continue; + + // walk over an or-group and check if we need to do anything + // for simpilicity no or-group is handled as a or-group including one dependency + pkgCache::DepIterator Start = D; + bool itsFine = false; + for (bool stillOr = true; stillOr == true; ++Start) + { + stillOr = (Start->CompareOp & Dep::Or) == Dep::Or; + pkgCache::PkgIterator const P = Start.TargetPkg(); + // virtual packages can't be a solution + if (P.end() == true || (P->ProvidesList == 0 && P->VersionList == 0)) + continue; + pkgCache::VerIterator const Cand = PkgState[P->ID].CandidateVerIter(*this); + // no versioned dependency - but is it installable? + if (Start.TargetVer() == 0 || Start.TargetVer()[0] == '\0') + { + // Check if one of the providers is installable + if (P->ProvidesList != 0) + { + pkgCache::PrvIterator Prv = P.ProvidesList(); + for (; Prv.end() == false; ++Prv) + { + pkgCache::VerIterator const C = PkgState[Prv.OwnerPkg()->ID].CandidateVerIter(*this); + if (C.end() == true || C != Prv.OwnerVer() || + (VersionState(C.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin) + continue; + break; + } + if (Prv.end() == true) + continue; + } + // no providers, so check if we have an installable candidate version + else if (Cand.end() == true || + (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin) + continue; + itsFine = true; + break; + } + if (Cand.end() == true) + continue; + // check if the current candidate is enough for the versioned dependency - and installable? + if (Start.IsSatisfied(Cand) == true && + (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) == DepCandMin) + { + itsFine = true; + break; + } + } + + if (itsFine == true) { + // something in the or-group was fine, skip all other members + for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D); + continue; + } + + // walk again over the or-group and check each if a candidate switch would help + itsFine = false; + for (bool stillOr = true; stillOr == true; ++D) + { + stillOr = (D->CompareOp & Dep::Or) == Dep::Or; + // changing candidate will not help if the dependency is not versioned + if (D.TargetVer() == 0 || D.TargetVer()[0] == '\0') + { + if (stillOr == true) + continue; + break; + } + + pkgCache::VerIterator V; + if (TargetRel == "newest") + V = D.TargetPkg().VersionList(); + else + V = Match.Find(D.TargetPkg()); + // check if the version from this release could satisfy the dependency + if (V.end() == true || D.IsSatisfied(V) == false) + { + if (stillOr == true) + continue; + break; + } + + pkgCache::VerIterator oldCand = PkgState[D.TargetPkg()->ID].CandidateVerIter(*this); + if (V == oldCand) + { + // Do we already touched this Version? If so, their versioned dependencies are okay, no need to check again + for (std::list >::const_iterator c = Changed.begin(); + c != Changed.end(); ++c) + { + if (c->first->ParentPkg != V->ParentPkg) + continue; + itsFine = true; + break; + } + } + + if (itsFine == false) + { + // change the candidate + Changed.push_back(make_pair(oldCand, TargetVer)); + if (SetCandidateRelease(V, TargetRel, Changed) == false) + { + if (stillOr == false) + break; + // undo the candidate changing + SetCandidateVersion(oldCand); + Changed.pop_back(); + continue; + } + itsFine = true; + } + + // something in the or-group was fine, skip all other members + for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D); + break; + } + + if (itsFine == false && (D->Type == pkgCache::Dep::PreDepends || D->Type == pkgCache::Dep::Depends)) + { + // undo all changes which aren't lead to a solution + for (std::list >::const_iterator c = ++newChanged; + c != Changed.end(); ++c) + SetCandidateVersion(c->first); + Changed.erase(newChanged, Changed.end()); + return false; + } + } + return true; +} + /*}}}*/ +// DepCache::MarkAuto - set the Auto flag for a package /*{{{*/ +// --------------------------------------------------------------------- +/* */ void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto) { StateCache &state = PkgState[Pkg->ID]; @@ -1114,30 +1611,30 @@ 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 Pkg) +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++) + for (VerIterator I = Pkg.VersionList(); I.end() == false; ++I) { if (Pkg.CurrentVer() == I) return I; - for (VerFileIterator J = I.FileList(); J.end() == false; J++) + 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 ((J.File()->Flags & Flag::NotAutomatic) != 0 || + (J.File()->Flags & Flag::ButAutomaticUpgrades) != 0) { if (Last.end() == true) Last = I; @@ -1151,17 +1648,16 @@ pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator Pkg) return Last; } /*}}}*/ - // Policy::IsImportantDep - True if the dependency is important /*{{{*/ // --------------------------------------------------------------------- /* */ -bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep) +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)) + if (InstallRecommends) return true; // we suport a special mode to only install-recommends for certain // sections @@ -1172,62 +1668,18 @@ bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep) return true; } else if(Dep->Type == pkgCache::Dep::Suggests) - return _config->FindB("APT::Install-Suggests", false); + return InstallSuggests; return false; } /*}}}*/ - -pkgDepCache::DefaultRootSetFunc::DefaultRootSetFunc() - : constructedSuccessfully(false) -{ - Configuration::Item const *Opts; - Opts = _config->Tree("APT::NeverAutoRemove"); - if (Opts != 0 && Opts->Child != 0) - { - Opts = Opts->Child; - for (; Opts != 0; Opts = Opts->Next) - { - if (Opts->Value.empty() == true) - continue; - - regex_t *p = new regex_t; - if(regcomp(p,Opts->Value.c_str(), - REG_EXTENDED | REG_ICASE | REG_NOSUB) != 0) - { - regfree(p); - delete p; - _error->Error("Regex compilation error for APT::NeverAutoRemove"); - return; - } - - rootSetRegexp.push_back(p); - } - } - - constructedSuccessfully = true; -} - -pkgDepCache::DefaultRootSetFunc::~DefaultRootSetFunc() -{ - for(unsigned int i = 0; i < rootSetRegexp.size(); i++) - { - regfree(rootSetRegexp[i]); - delete rootSetRegexp[i]; - } -} - - -bool pkgDepCache::DefaultRootSetFunc::InRootSet(const pkgCache::PkgIterator &pkg) -{ - for(unsigned int i = 0; i < rootSetRegexp.size(); i++) - if (regexec(rootSetRegexp[i], pkg.Name(), 0, 0, 0) == 0) - return true; - - return false; -} - -pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc() +// Policy::GetPriority - Get the priority of the package pin /*{{{*/ +signed short pkgDepCache::Policy::GetPriority(pkgCache::PkgIterator const &Pkg) +{ return 0; }; +signed short pkgDepCache::Policy::GetPriority(pkgCache::PkgFileIterator const &File) +{ return 0; }; + /*}}}*/ +pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc() /*{{{*/ { DefaultRootSetFunc *f = new DefaultRootSetFunc; if(f->wasConstructedSuccessfully()) @@ -1238,7 +1690,7 @@ pkgDepCache::InRootSetFunc *pkgDepCache::GetRootSetFunc() return NULL; } } - + /*}}}*/ bool pkgDepCache::MarkFollowsRecommends() { return _config->FindB("APT::AutoRemove::RecommendsImportant", true); @@ -1246,14 +1698,18 @@ bool pkgDepCache::MarkFollowsRecommends() bool pkgDepCache::MarkFollowsSuggests() { - return _config->FindB("APT::AutoRemove::SuggestsImportant", false); + return _config->FindB("APT::AutoRemove::SuggestsImportant", true); } -// the main mark algorithm +// pkgDepCache::MarkRequired - the main mark algorithm /*{{{*/ bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc) { + if (_config->Find("APT::Solver", "internal") != "internal") + return true; + bool follow_recommends; bool follow_suggests; + bool debug_autoremove = _config->FindB("Debug::pkgAutoRemove",false); // init the states for(PkgIterator p = PkgBegin(); !p.end(); ++p) @@ -1262,9 +1718,8 @@ bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc) PkgState[p->ID].Garbage = false; // debug output - if(_config->FindB("Debug::pkgAutoRemove",false) - && PkgState[p->ID].Flags & Flag::Auto) - std::clog << "AutoDep: " << p.Name() << std::endl; + if(debug_autoremove && PkgState[p->ID].Flags & Flag::Auto) + std::clog << "AutoDep: " << p.FullName() << std::endl; } // init vars @@ -1278,12 +1733,16 @@ bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc) { if(!(PkgState[p->ID].Flags & Flag::Auto) || (p->Flags & Flag::Essential) || - userFunc.InRootSet(p)) - + (p->Flags & Flag::Important) || + 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(), + MarkPackage(p, p.CurrentVer(), follow_recommends, follow_suggests); // the package is to be installed else if(PkgState[p->ID].Install()) @@ -1294,19 +1753,25 @@ bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc) return true; } - -// mark a single package in Mark-and-Sweep + /*}}}*/ +// MarkPackage - mark a single package in Mark-and-Sweep /*{{{*/ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg, const pkgCache::VerIterator &ver, - bool follow_recommends, - bool follow_suggests) + bool const &follow_recommends, + bool const &follow_suggests) { pkgDepCache::StateCache &state = PkgState[pkg->ID]; - VerIterator currver = pkg.CurrentVer(); - VerIterator candver = state.CandidateVerIter(*this); - VerIterator instver = state.InstVerIter(*this); + + // if we are marked already we are done + if(state.Marked) + return; + + VerIterator const currver = pkg.CurrentVer(); + VerIterator const instver = state.InstVerIter(*this); #if 0 + VerIterator const candver = state.CandidateVerIter(*this); + // 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 @@ -1330,15 +1795,25 @@ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg, !(ver == currver && instver.end() && !ver.end())) return; - // if we are marked already we are done - if(state.Marked) - 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; + } - //std::cout << "Setting Marked for: " << pkg.Name() << std::endl; state.Marked=true; - if(!ver.end()) - { + if(ver.end() == true) + return; + for(DepIterator d = ver.DependsList(); !d.end(); ++d) { if(d->Type == Dep::Depends || @@ -1352,9 +1827,21 @@ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg, for(VerIterator V = d.TargetPkg().VersionList(); !V.end(); ++V) { - if(_system->VS->CheckDep(V.VerStr(), d->CompareOp, d.TargetVer())) + if(d.IsSatisfied(V)) { - MarkPackage(V.ParentPkg(), V, + 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); } } @@ -1362,23 +1849,39 @@ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &pkg, for(PrvIterator prv=d.TargetPkg().ProvidesList(); !prv.end(); ++prv) { - if(_system->VS->CheckDep(prv.ProvideVersion(), d->CompareOp, - d.TargetVer())) + if(d.IsSatisfied(prv)) { + 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 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 @@ -1390,11 +1893,11 @@ bool pkgDepCache::Sweep() if(!state.Marked && (!p.CurrentVer().end() || state.Install())) { state.Garbage=true; - if(_config->FindB("Debug::pkgAutoRemove",false)) - std::cout << "Garbage: " << p.Name() << std::endl; + if(debug_autoremove) + std::clog << "Garbage: " << p.FullName() << std::endl; } } return true; } - + /*}}}*/