X-Git-Url: https://git.saurik.com/apt.git/blobdiff_plain/a83d884db24933000f19dbff706529db057d50c1..be615841e27068522c467899c96c541b74e4c349:/apt-pkg/depcache.cc diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc index 552a45a16..27b6134b4 100644 --- a/apt-pkg/depcache.cc +++ b/apt-pkg/depcache.cc @@ -19,16 +19,49 @@ #include #include +#include #include + +#include #include +#include + #include - /*}}}*/ + +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(pkgCache *pCache,Policy *Plcy) : - Cache(pCache), PkgState(0), DepState(0) + group_level(0), Cache(pCache), PkgState(0), DepState(0) { delLocalPolicy = 0; LocalPolicy = Plcy; @@ -51,6 +84,10 @@ 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]; @@ -76,9 +113,6 @@ bool pkgDepCache::Init(OpProgress *Prog) // Find the proper cache slot StateCache &State = PkgState[I->ID]; State.iFlags = 0; - State.DirtyState = pkgCache::State::RemoveUnknown; - //State.AutomaticRemove = I->AutomaticRemove; - State.AutomaticRemove = pkgCache::State::RemoveUnknown; // Figure out the install version State.CandidateVer = GetCandidateVer(I); @@ -98,7 +132,10 @@ bool pkgDepCache::Init(OpProgress *Prog) } Update(Prog); - + + if(Prog != 0) + Prog->Done(); + return true; } /*}}}*/ @@ -106,12 +143,13 @@ bool pkgDepCache::Init(OpProgress *Prog) bool pkgDepCache::readStateFile(OpProgress *Prog) { FileFd state_file; - string state = _config->FindDir("Dir::State") + "pkgstates"; + string state = _config->FindDir("Dir::State") + "extended_states"; if(FileExists(state)) { state_file.Open(state, FileFd::ReadOnly); int file_size = state_file.Size(); - Prog->OverallProgress(0, file_size, 1, - _("Reading extended state information")); + if(Prog != NULL) + Prog->OverallProgress(0, file_size, 1, + _("Reading state information")); pkgTagFile tagfile(&state_file); pkgTagSection section; @@ -122,16 +160,19 @@ bool pkgDepCache::readStateFile(OpProgress *Prog) // Silently ignore unknown packages and packages with no actual // version. if(!pkg.end() && !pkg.VersionList().end()) { - short reason = section.FindI("Remove-Reason", - pkgCache::State::RemoveManual); - PkgState[pkg->ID].AutomaticRemove = reason; - //std::cout << "Set: " << pkgname << " to " << reason << std::endl; + 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(); - Prog->OverallProgress(amt, file_size, 1, - _("Reading extended state information")); + if(Prog != NULL) + Prog->OverallProgress(amt, file_size, 1, + _("Reading state information")); } - Prog->OverallProgress(file_size, file_size, 1, - _("Reading extended state information")); + if(Prog != NULL) + Prog->OverallProgress(file_size, file_size, 1, + _("Reading state information")); } } @@ -140,26 +181,81 @@ bool pkgDepCache::readStateFile(OpProgress *Prog) bool pkgDepCache::writeStateFile(OpProgress *prog) { - // write the auto-mark list ---------------------------------- + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::clog << "pkgDepCache::writeStateFile()" << std::endl; FileFd StateFile; - string state = _config->FindDir("Dir::State") + "pkgstates"; + string state = _config->FindDir("Dir::State") + "extended_states"; - if(!StateFile.Open(state, FileFd::WriteEmpty)) - return _error->Error(_("Failed to write StateFile %s"), + // 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 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 pkgname = section.FindS("Package"); + // Silently ignore unknown packages and packages with no actual + // version. + pkgCache::PkgIterator pkg=Cache->FindPkg(pkgname); + if(pkg.end() || pkg.VersionList().end()) + continue; + bool oldAuto = section.FindI("Auto-Installed"); + bool newAuto = (PkgState[pkg->ID].Flags & Flag::Auto); + 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"; + rewrite[0].NewTag = 0; + rewrite[1].Tag = 0; + TFRewrite(OutFile, section, nullreorderlist, rewrite); + fprintf(OutFile,"\n"); + pkgs_seen.insert(pkgname); + } + + // 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].AutomaticRemove != pkgCache::State::RemoveUnknown) { + 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; + continue; + } + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::clog << "Writing new AutoInstall: " + << pkg.Name() << std::endl; ostr.str(string("")); - ostr << "Package: " << pkg.Name() - << "\nRemove-Reason: " - << (int)(PkgState[pkg->ID].AutomaticRemove) << "\n\n"; - StateFile.Write(ostr.str().c_str(), ostr.str().size()); - //std::cout << "Writing auto-mark: " << ostr.str() << endl; + ostr << "Package: " << pkg.Name() + << "\nAuto-Installed: 1\n\n"; + fprintf(OutFile,ostr.str().c_str()); + fprintf(OutFile,"\n"); } } + fclose(OutFile); + + // move the outfile over the real file + rename(outfile.c_str(), state.c_str()); + return true; } @@ -514,16 +610,16 @@ void pkgDepCache::Update(OpProgress *Prog) AddStates(I); } - readStateFile(Prog); - if (Prog != 0) Prog->Progress(Done); + + readStateFile(Prog); } /*}}}*/ // 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 @@ -575,7 +671,7 @@ void pkgDepCache::Update(PkgIterator const &Pkg) // 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) { // Simplifies other routines. if (Pkg.end() == true) @@ -587,6 +683,9 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) 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]; @@ -603,7 +702,8 @@ void pkgDepCache::MarkKeep(PkgIterator const &Pkg,bool Soft) if (Pkg->VersionList == 0) return; - P.Flags &= ~Flag::Auto; + if(FromUser && !P.Marked) + P.Flags &= ~Flag::Auto; RemoveSizes(Pkg); RemoveStates(Pkg); @@ -629,6 +729,8 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) 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 | Purge); @@ -651,8 +753,6 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) else P.Mode = ModeDelete; P.InstallVer = 0; - // This was not inverted before, but I think it should be - P.Flags &= ~Flag::Auto; AddStates(Pkg); Update(Pkg); @@ -663,7 +763,7 @@ void pkgDepCache::MarkDelete(PkgIterator const &Pkg, bool rPurge) // --------------------------------------------------------------------- /* */ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, - unsigned long Depth) + unsigned long Depth, bool FromUser) { if (Depth > 100) return; @@ -672,26 +772,27 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, 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); 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; - /* 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 */ @@ -700,7 +801,20 @@ 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; @@ -735,7 +849,33 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, it will be installed. Otherwise we only worry about critical deps */ if (IsImportantDep(Start) == false) continue; - if (Pkg->CurrentVer != 0 && Start.IsCritical() == false) + + /* check if any ImportantDep() (but not Critial) where added + * since we installed the thing + */ + bool isNewImportantDep = false; + if(IsImportantDep(Start) && !Start.IsCritical()) + { + bool found=false; + VerIterator instVer = Pkg.CurrentVer(); + for (DepIterator D = instVer.DependsList(); !D.end(); D++) + { + //FIXME: deal better with or-groups(?) + DepIterator LocalStart = D; + + if(IsImportantDep(Dep) && 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; + } + if(isNewImportantDep) + if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) + std::clog << "new important dependency: " + << Start.TargetPkg().Name() << std::endl; + + if (Pkg->CurrentVer != 0 && Start.IsCritical() == false && !isNewImportantDep) continue; /* If we are in an or group locate the first or that can @@ -777,15 +917,14 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, } } - if (InstPkg.end() == false) + if (InstPkg.end() == false) { - MarkInstall(InstPkg,true,Depth + 1); - - // Set the autoflag, after MarkInstall because MarkInstall unsets it - if (P->CurrentVer == 0) - PkgState[InstPkg->ID].Flags |= Flag::Auto; + if(_config->FindB("Debug::pkgDepCache::AutoInstall",false) == true) + std::clog << "Installing " << InstPkg.Name() + << " as dep of " << Pkg.Name() + << std::endl; + MarkInstall(InstPkg, true, Depth + 1, false); } - continue; } @@ -799,7 +938,6 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, PkgIterator Pkg = Ver.ParentPkg(); MarkDelete(Pkg); - PkgState[Pkg->ID].Flags |= Flag::Auto; } continue; } @@ -811,6 +949,8 @@ void pkgDepCache::MarkInstall(PkgIterator const &Pkg,bool AutoInst, /* */ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) { + ActionGroup group(*this); + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -824,23 +964,16 @@ void pkgDepCache::SetReInstall(PkgIterator const &Pkg,bool To) AddSizes(Pkg); } /*}}}*/ -// DepCache::SetDirty - Switch the package between dirty states /*{{{*/ -// --------------------------------------------------------------------- -/* */ -void pkgDepCache::SetDirty(PkgIterator const &Pkg, pkgCache::State::PkgRemoveState To) -{ - StateCache &P = PkgState[Pkg->ID]; - P.DirtyState = To; -} - /*}}}*/ // DepCache::SetCandidateVersion - Change the candidate version /*{{{*/ // --------------------------------------------------------------------- /* */ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer) { + ActionGroup group(*this); + pkgCache::PkgIterator Pkg = TargetVer.ParentPkg(); StateCache &P = PkgState[Pkg->ID]; - + RemoveSizes(Pkg); RemoveStates(Pkg); @@ -853,6 +986,18 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer) Update(Pkg); AddSizes(Pkg); } + +void pkgDepCache::MarkAuto(const PkgIterator &Pkg, bool Auto) +{ + StateCache &state = PkgState[Pkg->ID]; + + ActionGroup group(*this); + + if(Auto) + state.Flags |= Flag::Auto; + else + state.Flags &= ~Flag::Auto; +} /*}}}*/ // StateCache::Update - Compute the various static display things /*{{{*/ // --------------------------------------------------------------------- @@ -935,11 +1080,233 @@ pkgCache::VerIterator pkgDepCache::Policy::GetCandidateVer(PkgIterator Pkg) return Last; } /*}}}*/ + /*}}}*/ + +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() +{ + 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); +} + +// the main mark algorithm +bool pkgDepCache::MarkRequired(InRootSetFunc &userFunc) +{ + bool follow_recommends; + bool follow_suggests; + + // init the states + for(PkgIterator p = PkgBegin(); !p.end(); ++p) + { + PkgState[p->ID].Marked = false; + 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; + } + + // 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)) + + { + // 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; +} + +// 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) +{ + pkgDepCache::StateCache &state = PkgState[pkg->ID]; + VerIterator candver = state.CandidateVerIter(*this); + VerIterator 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 + + // Ignore versions other than the InstVer, and ignore packages + // that are already going to be removed or just left uninstalled. + if(!(ver == instver && !instver.end())) + return; + + // if we are marked already we are done + if(state.Marked) + return; + + //std::cout << "Setting Marked for: " << pkg.Name() << std::endl; + state.Marked=true; + + if(!ver.end()) + { + 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())) + { + 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())) + { + MarkPackage(prv.OwnerPkg(), prv.OwnerVer(), + follow_recommends, follow_suggests); + } + } + } + } + } +} + +bool pkgDepCache::Sweep() +{ + // do the sweep + for(PkgIterator p=PkgBegin(); !p.end(); ++p) + { + StateCache &state=PkgState[p->ID]; + + // if it is not marked and it is installed, it's garbage + if(!state.Marked && (!p.CurrentVer().end() || state.Install()) && + !state.Delete()) + { + state.Garbage=true; + if(_config->FindB("Debug::pkgAutoRemove",false)) + std::cout << "Garbage: " << p.Name() << std::endl; + } + } + + return true; +} + // Policy::IsImportantDep - True if the dependency is important /*{{{*/ // --------------------------------------------------------------------- /* */ bool pkgDepCache::Policy::IsImportantDep(DepIterator Dep) { - return Dep.IsCritical(); + if(Dep.IsCritical()) + return true; + else if(Dep->Type == pkgCache::Dep::Recommends) + return _config->FindB("APT::Install-Recommends", false); + else if(Dep->Type == pkgCache::Dep::Suggests) + return _config->FindB("APT::Install-Suggests", false); + + return false; } /*}}}*/