summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Kalnischkies <david@kalnischkies.de>2016-07-01 10:06:53 +0200
committerJulian Andres Klode <jak@debian.org>2016-08-31 13:49:37 +0200
commit267314664da07b892bff7c2ee8060faaa3bed74d (patch)
treed51bc705fe1df0eacaefad5a6b37a09d21b1f382
parent3880929d386eb93e390543e44bf4abdde2ec8c78 (diff)
protect only the latest same-source providers from autoremove
Traditionally all providers are protected providing something as apt can't know which of them is actually really providing the functionality for the user ensuring that we don't propose the removal of used stuff, but that is of course also keeping stuff around which could be removed. That can cause the collection of multiple old providers until the provided package is itself no longer needed (e.g. out-of-tree kernel modules). We combat this by marking providers only from the newest source package version so that old providers built by older versions of the same source package can be garbage collected. (cherry picked from commit a0ed43f7323b9d7976ed0ba8d437a42e24af9eaf)
-rw-r--r--apt-pkg/depcache.cc39
-rwxr-xr-xtest/integration/test-apt-get-autoremove36
2 files changed, 70 insertions, 5 deletions
diff --git a/apt-pkg/depcache.cc b/apt-pkg/depcache.cc
index eb8f6460d..1301cbdf9 100644
--- a/apt-pkg/depcache.cc
+++ b/apt-pkg/depcache.cc
@@ -12,6 +12,7 @@
#include <apt-pkg/depcache.h>
#include <apt-pkg/versionmatch.h>
+#include <apt-pkg/version.h>
#include <apt-pkg/error.h>
#include <apt-pkg/fileutl.h>
#include <apt-pkg/strutl.h>
@@ -1927,10 +1928,11 @@ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &Pkg,
continue;
// handle the virtual part first
+ APT::VersionVector providers;
for(auto Prv = T.ProvidesList(); Prv.end() == false; ++Prv)
{
auto PP = Prv.OwnerPkg();
- if (PkgState[PP->ID].Marked || IsPkgInBoringState(PP, PkgState))
+ if (IsPkgInBoringState(PP, PkgState))
continue;
// we want to ignore provides from uninteresting versions
@@ -1939,10 +1941,37 @@ void pkgDepCache::MarkPackage(const pkgCache::PkgIterator &Pkg,
if (unlikely(PV.end()) || PV != Prv.OwnerVer() || D.IsSatisfied(Prv) == false)
continue;
- if (debug_autoremove)
- std::clog << "Following dep: " << APT::PrettyDep(this, D)
- << ", provided by " << PP.FullName() << " " << PV.VerStr() << std::endl;
- MarkPackage(PP, PV, follow_recommends, follow_suggests);
+ providers.emplace_back(PV);
+ }
+ if (providers.empty() == false)
+ {
+ // sort providers by source version so that only the latest versioned
+ // binary package of a source package is marked instead of all
+ std::sort(providers.begin(), providers.end(),
+ [](pkgCache::VerIterator const &A, pkgCache::VerIterator const &B) {
+ auto const nameret = strcmp(A.SourcePkgName(), B.SourcePkgName());
+ if (nameret != 0)
+ return nameret < 0;
+ auto const verret = A.Cache()->VS->CmpVersion(A.SourceVerStr(), B.SourceVerStr());
+ if (verret != 0)
+ return verret > 0;
+ return strcmp(A.ParentPkg().Name(), B.ParentPkg().Name()) < 0;
+ });
+ auto const prvsize = providers.size();
+ providers.erase(std::unique(providers.begin(), providers.end(),
+ [](pkgCache::VerIterator const &A, pkgCache::VerIterator const &B) {
+ return strcmp(A.SourcePkgName(), B.SourcePkgName()) == 0 &&
+ strcmp(A.SourceVerStr(), B.SourceVerStr()) != 0;
+ }), providers.end());
+ for (auto && PV: providers)
+ {
+ auto const PP = PV.ParentPkg();
+ if (debug_autoremove)
+ std::clog << "Following dep: " << APT::PrettyDep(this, D)
+ << ", provided by " << PP.FullName() << " " << PV.VerStr()
+ << " (" << providers.size() << "/" << prvsize << ")"<< std::endl;
+ MarkPackage(PP, PV, follow_recommends, follow_suggests);
+ }
}
// now deal with the real part of the package
diff --git a/test/integration/test-apt-get-autoremove b/test/integration/test-apt-get-autoremove
index 7a28c51f1..17dba9aec 100755
--- a/test/integration/test-apt-get-autoremove
+++ b/test/integration/test-apt-get-autoremove
@@ -108,3 +108,39 @@ testdpkgnotinstalled 'po-debconf' 'debhelper'
testmarkedauto
testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1
testmarkedauto 'po-debconf'
+
+insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider'
+insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider
+Source: foo-multi (1)'
+insertinstalledpackage 'foo-multi1-2' 'all' '1' 'Provides: foo-provider
+Source: foo-multi (2)'
+insertinstalledpackage 'foo-multi1-3' 'all' '1' 'Provides: foo-provider
+Source: foo-multi (3)'
+insertinstalledpackage 'foo-multi2-1' 'all' '1' 'Provides: foo-provider
+Source: foo-multi (1)'
+insertinstalledpackage 'foo-multi2-2' 'all' '1' 'Provides: foo-provider
+Source: foo-multi (2)'
+insertinstalledpackage 'foo-multi2-3' 'all' '1' 'Provides: foo-provider
+Source: foo-multi (3)'
+insertinstalledpackage 'foo-plus-1' 'all' '1' 'Provides: foo-provider
+Source: foo-plus (1)'
+insertinstalledpackage 'foo-plus-2' 'all' '1' 'Provides: foo-provider
+Source: foo-plus (2)'
+insertinstalledpackage 'foo-plus-3' 'all' '1' 'Provides: foo-provider
+Source: foo-plus (3)'
+insertinstalledpackage 'foo-single-1' 'all' '1' 'Provides: foo-provider'
+insertinstalledpackage 'foo-single-2' 'all' '1' 'Provides: foo-provider'
+
+testsuccess aptmark auto 'foo-*'
+testsuccessequal 'Reading package lists...
+Building dependency tree...
+Reading state information...
+The following packages will be REMOVED:
+ foo-multi1-1 foo-multi1-2 foo-multi2-1 foo-multi2-2 foo-plus-1 foo-plus-2
+0 upgraded, 0 newly installed, 6 to remove and 0 not upgraded.
+Remv foo-multi1-1 [1]
+Remv foo-multi1-2 [1]
+Remv foo-multi2-1 [1]
+Remv foo-multi2-2 [1]
+Remv foo-plus-1 [1]
+Remv foo-plus-2 [1]' apt autoremove -s