diff options
Diffstat (limited to 'apt-pkg/deb/dpkgpm.cc')
-rw-r--r-- | apt-pkg/deb/dpkgpm.cc | 166 |
1 files changed, 140 insertions, 26 deletions
diff --git a/apt-pkg/deb/dpkgpm.cc b/apt-pkg/deb/dpkgpm.cc index 025dfbfd3..d14155d01 100644 --- a/apt-pkg/deb/dpkgpm.cc +++ b/apt-pkg/deb/dpkgpm.cc @@ -670,27 +670,23 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) // if there are multiple pkgs dpkg would send us a full pkgname:arch pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname); if (Grp.end() == false) - { - pkgCache::PkgIterator P = Grp.PackageList(); - for (; P.end() != true; P = Grp.NextPkg(P)) - { + for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P)) if(Cache[P].Keep() == false || Cache[P].ReInstall() == true) { - pkgname = P.FullName(); + auto fullname = P.FullName(); + if (Cache[P].Delete() && PackageOps[fullname].size() <= PackageOpsDone[fullname]) + continue; + pkgname = std::move(fullname); break; } - } - } } - const char* const pkg = pkgname.c_str(); - std::string short_pkgname = StringSplit(pkgname, ":")[0]; std::string arch = ""; if (pkgname.find(":") != string::npos) arch = StringSplit(pkgname, ":")[1]; std::string i18n_pkgname = pkgname; if (arch.size() != 0) - strprintf(i18n_pkgname, "%s (%s)", short_pkgname.c_str(), arch.c_str()); + strprintf(i18n_pkgname, "%s (%s)", StringSplit(pkgname, ":")[0].c_str(), arch.c_str()); // 'processing' from dpkg looks like // 'processing: action: pkg' @@ -720,21 +716,21 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) if (prefix == "status") { - std::vector<struct DpkgState> &states = PackageOps[pkg]; + std::vector<struct DpkgState> &states = PackageOps[pkgname]; if (action == "triggers-pending") { if (Debug == true) - std::clog << "(parsed from dpkg) pkg: " << short_pkgname + std::clog << "(parsed from dpkg) pkg: " << pkgname << " action: " << action << " (prefix 2 to " - << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; + << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl; states.insert(states.begin(), {"installed", N_("Installed %s")}); states.insert(states.begin(), {"half-configured", N_("Configuring %s")}); PackagesTotal += 2; } - else if(PackageOpsDone[pkg] < states.size()) + else if(PackageOpsDone[pkgname] < states.size()) { - char const * next_action = states[PackageOpsDone[pkg]].state; + char const * next_action = states[PackageOpsDone[pkgname]].state; if (next_action) { /* @@ -751,24 +747,52 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line) } */ if (Debug == true) - std::clog << "(parsed from dpkg) pkg: " << short_pkgname + std::clog << "(parsed from dpkg) pkg: " << pkgname << " action: " << action << " (expected: '" << next_action << "' " - << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl; + << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl; // check if the package moved to the next dpkg state if(action == next_action) { // only read the translation if there is actually a next action - char const * const translation = _(states[PackageOpsDone[pkg]].str); + char const * const translation = _(states[PackageOpsDone[pkgname]].str); // we moved from one dpkg state to a new one, report that - ++PackageOpsDone[pkg]; + ++PackageOpsDone[pkgname]; ++PackagesDone; std::string msg; strprintf(msg, translation, i18n_pkgname.c_str()); d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg); } + else if (action == "unpacked" && strcmp(next_action, "config-files") == 0) + { + // in a crossgrade what looked like a remove first is really an unpack over it + ++PackageOpsDone[pkgname]; + ++PackagesDone; + + auto const Pkg = Cache.FindPkg(pkgname); + if (likely(Pkg.end() == false)) + { + auto const Grp = Pkg.Group(); + if (likely(Grp.end() == false)) + { + for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P)) + if(Cache[P].Install()) + { + auto && Ops = PackageOps[P.FullName()]; + auto const unpackOp = std::find_if(Ops.cbegin(), Ops.cend(), [](DpkgState const &s) { return strcmp(s.state, "unpacked") == 0; }); + if (unpackOp != Ops.cend()) + { + auto const skipped = std::distance(Ops.cbegin(), unpackOp); + PackagesDone += skipped; + PackageOpsDone[P.FullName()] += skipped; + break; + } + } + } + } + } } } } @@ -1277,7 +1301,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) { // we remove the last configures (and after that removes) from the list here // as they will be covered by the pending calls, so explicit calls are busy work - decltype(List)::const_iterator::difference_type const explicitIdx = + decltype(List)::const_iterator::difference_type explicitIdx = std::distance(List.cbegin(), _config->FindB("Dpkg::ExplicitLastConfigure", false) ? List.cend() : std::find_if_not( @@ -1379,6 +1403,101 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) // for the progress BuildPackagesProgressMap(); + APT::StateChanges approvedStates; + if (_config->FindB("dpkg::selection::remove::approved", true)) + { + for (auto && I : List) + if (I.Op == Item::Purge) + approvedStates.Purge(FindToBeRemovedVersion(I.Pkg)); + else if (I.Op == Item::Remove) + approvedStates.Remove(FindToBeRemovedVersion(I.Pkg)); + } + + // Skip removes if we install another architecture of this package soon (crossgrade) + // We can't just skip them all the time as it could be an ordering requirement [of another package] + if ((approvedStates.Remove().empty() == false || approvedStates.Purge().empty() == false) && + _config->FindB("dpkg::remove::crossgrade::implicit", true) == true) + { + std::unordered_set<decltype(pkgCache::Package::ID)> crossgraded; + std::vector<std::pair<Item*, std::string>> toCrossgrade; + auto const PlanedEnd = std::next(List.begin(), explicitIdx); + for (auto I = List.begin(); I != PlanedEnd; ++I) + { + if (I->Op != Item::Remove && I->Op != Item::Purge) + continue; + + auto const Grp = I->Pkg.Group(); + size_t installedInstances = 0; + for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg)) + if (Pkg->CurrentVer != 0 || Cache[Pkg].Install()) + ++installedInstances; + if (installedInstances == 2) + { + auto const FirstInstall = std::find_if_not(I, List.end(), + [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; }); + auto const LastInstall = std::find_if_not(FirstInstall, List.end(), + [](Item const &i) { return i.Op == Item::Install; }); + auto const crosser = std::find_if(FirstInstall, LastInstall, + [&I](Item const &i) { return i.Pkg->Group == I->Pkg->Group; }); + if (crosser != LastInstall) + { + crossgraded.insert(I->Pkg->ID); + toCrossgrade.emplace_back(&(*I), crosser->Pkg.FullName()); + } + } + } + for (auto I = PlanedEnd; I != List.end(); ++I) + { + if (I->Op != Item::Remove && I->Op != Item::Purge) + continue; + + auto const Grp = I->Pkg.Group(); + for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg)) + { + if (Pkg == I->Pkg || Cache[Pkg].Install() == false) + continue; + toCrossgrade.emplace_back(&(*I), Pkg.FullName()); + break; + } + } + for (auto C : toCrossgrade) + { + // we never do purges on packages which are crossgraded, even if "requested" + if (C.first->Op == Item::Purge) + { + C.first->Op = Item::Remove; // crossgrades should never be purged + auto && Purges = approvedStates.Purge(); + auto const Ver = std::find_if( +#if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4) + Purges.cbegin(), Purges.cend(), +#else + Purges.begin(), Purges.end(), +#endif + [&C](pkgCache::VerIterator const &V) { return V.ParentPkg() == C.first->Pkg; }); + approvedStates.Remove(*Ver); + Purges.erase(Ver); + auto && RemOp = PackageOps[C.first->Pkg.FullName()]; + if (RemOp.size() == 5) + { + RemOp.erase(std::next(RemOp.begin(), 3), RemOp.end()); + PackagesTotal -= 2; + } + else + _error->Warning("Unexpected amount of planned ops for package %s: %lu", C.first->Pkg.FullName().c_str(), RemOp.size()); + } + } + if (crossgraded.empty() == false) + { + auto const oldsize = List.size(); + List.erase(std::remove_if(List.begin(), PlanedEnd, + [&crossgraded](Item const &i){ + return (i.Op == Item::Remove || i.Op == Item::Purge) && + crossgraded.find(i.Pkg->ID) != crossgraded.end(); + }), PlanedEnd); + explicitIdx -= (oldsize - List.size()); + } + } + APT::StateChanges currentStates; if (_config->FindB("dpkg::selection::current::saveandrestore", true)) { @@ -1400,14 +1519,9 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress) return _error->Error("Couldn't clean the currently selected dpkg states"); } } - APT::StateChanges approvedStates; + if (_config->FindB("dpkg::selection::remove::approved", true)) { - for (auto && I: List) - if (I.Op == Item::Remove && Cache[I.Pkg].Delete()) - approvedStates.Remove(FindToBeRemovedVersion(I.Pkg)); - else if (I.Op == Item::Purge && Cache[I.Pkg].Purge()) - approvedStates.Purge(FindToBeRemovedVersion(I.Pkg)); if (approvedStates.Save(false) == false) { _error->Error("Couldn't record the approved state changes as dpkg selection states"); |