diff --git a/man/eclean.1 b/man/eclean.1 index db2c9946..92b65a19 100644 --- a/man/eclean.1 +++ b/man/eclean.1 @@ -111,6 +111,18 @@ Only effective with \-\-deep. Without \-\-deep, no VCS cleaning is done. .TP \fB\-i, \-\-ignore\-failure\fP ignore the failure to locate PKGDIR This is only useful when scripting to ignore an otherwise fatal error. +.TP +\fB\-\-changed\-deps\fP delete packages for which ebuild dependencies have changed +.TP +\fB\-\-changed\-subslot\fP delete packages with := deps on subslots no longer in the tree +.TP +\fB\-\-changed\-iuse\fP delete packages whose IUSE no longer matches the ebuild +This is useful when, for instance, PYTHON_COMPAT changes and adds a new +python_targets_* flag: the old binary package can never be a cache hit again. +.TP +\fB\-\-unreachable\fP delete packages that can no longer be a Portage cache hit +This enables all of the \-\-changed\-* options at once, so any binary package +that Portage can no longer reuse is removed regardless of why. .SH "EXCLUSION FILES" Exclusions files are lists of packages names or categories you want to protect in particular. This may be useful to protect more binary packages for some system diff --git a/pym/gentoolkit/eclean/cli.py b/pym/gentoolkit/eclean/cli.py index 09c85f38..1b4386ad 100644 --- a/pym/gentoolkit/eclean/cli.py +++ b/pym/gentoolkit/eclean/cli.py @@ -266,6 +266,17 @@ def printUsage(_error=None, help=None, unresolved_invalids=None): + " - delete packages with := deps on subslots no longer in tree", file=out, ) + print( + yellow(" --changed-iuse") + + " - delete packages whose IUSE no longer matches the ebuild", + file=out, + ) + print( + yellow(" --unreachable") + + " - delete packages that can no longer be a cache hit" + + " (enables all --changed-* options)", + file=out, + ) print( yellow(" --no-clean-invalid") + " - Skip cleaning invalid binpkgs", @@ -425,6 +436,12 @@ def optionSwitch(option, opts, action=None): options["changed-deps"] = True elif o in ("--changed-subslot"): options["changed-subslot"] = True + elif o in ("--changed-iuse"): + options["changed-iuse"] = True + elif o in ("--unreachable"): + options["changed-deps"] = True + options["changed-subslot"] = True + options["changed-iuse"] = True elif o in ("-i", "--ignore-failure"): options["ignore-failure"] = True elif o in ("-u", "--unique-use"): @@ -480,6 +497,8 @@ def optionSwitch(option, opts, action=None): "ignore-failure", "changed-deps", "changed-subslot", + "changed-iuse", + "unreachable", "unique-use", "no-clean-invalid", ] @@ -497,6 +516,7 @@ def optionSwitch(option, opts, action=None): options["verbose"] = False options["changed-deps"] = False options["changed-subslot"] = False + options["changed-iuse"] = False options["ignore-failure"] = False options["no-clean-invalid"] = False options["unique-use"] = False diff --git a/pym/gentoolkit/eclean/search.py b/pym/gentoolkit/eclean/search.py index 2fd31dea..341e3a42 100644 --- a/pym/gentoolkit/eclean/search.py +++ b/pym/gentoolkit/eclean/search.py @@ -657,6 +657,23 @@ def _check_subslot_deps(deps, eapi, port_dbapi, uselist=None, cpv=None, verbose= return False +def _iuse_changed(iuse_binpkg, iuse_ebuild): + """Check if the set of IUSE flags differs between binpkg and ebuild. + + USE flag defaults (a leading + or -) are ignored; only the presence of a + flag matters. Portage cannot use a binpkg whose IUSE no longer matches the + ebuild's (e.g. after PYTHON_COMPAT adds a new python_targets_* flag), so + such a binpkg is orphaned and can be removed. + + Returns True if the IUSE flags differ (binpkg should be removed). + """ + + def _flags(iuse): + return frozenset(flag.lstrip("+-") for flag in iuse.split()) + + return _flags(iuse_binpkg) != _flags(iuse_ebuild) + + def _find_debuginfo_tarball(cpv: portage.versions._pkg_str, cp: str): """ From a CPV, identify and check for a matching debuginfo tarball. @@ -806,11 +823,15 @@ def mk_binpkg_key(cpv): # Exclude if binpkg exists in the porttree and not --deep if not destructive and port_dbapi.cpv_exists(cpv): - if not options["changed-deps"] and not options["changed-subslot"]: + if ( + not options["changed-deps"] + and not options["changed-subslot"] + and not options["changed-iuse"] + ): continue dep_keys = ("RDEPEND", "PDEPEND") - keys = ("EAPI", "USE") + dep_keys + keys = ("EAPI", "USE", "IUSE") + dep_keys binpkg_metadata = dict(zip(keys, bin_dbapi.aux_get(cpv, keys))) deps_binpkg = " ".join(binpkg_metadata[key] for key in dep_keys) uselist = frozenset(binpkg_metadata["USE"].split()) @@ -842,6 +863,11 @@ def mk_binpkg_key(cpv): ): should_remove = True + if not should_remove and options["changed-iuse"]: + ebuild_iuse = port_dbapi.aux_get(cpv, ["IUSE"])[0] + if _iuse_changed(binpkg_metadata["IUSE"], ebuild_iuse): + should_remove = True + if not should_remove: continue