From 886c02c8672b1da9087fee88daee813907182a6b Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sat, 14 May 2011 01:45:06 -0700 Subject: [PATCH] preserve-libs: preserve during uninstall This will fix bug #286714. The emerge --depclean-lib-check option will now be ignored when FEATURES=preserve-libs is enabled, since any libraries that have consumers will simply be preserved. --- pym/_emerge/actions.py | 3 +- pym/_emerge/help.py | 5 +- pym/portage/dbapi/vartree.py | 91 ++++++++++++++++++++++++++++-------- 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py index bab39b9e3..6e238b069 100644 --- a/pym/_emerge/actions.py +++ b/pym/_emerge/actions.py @@ -907,7 +907,8 @@ def calc_depclean(settings, trees, ldpath_mtimes, if cleanlist and \ real_vardb._linkmap is not None and \ - myopts.get('--depclean-lib-check') != 'n': + myopts.get("--depclean-lib-check") != "n" and \ + "preserve-libs" not in settings.features: # Check if any of these packages are the sole providers of libraries # with consumers that have not been selected for removal. If so, these diff --git a/pym/_emerge/help.py b/pym/_emerge/help.py index 9ba7892e5..6ff26ebd6 100644 --- a/pym/_emerge/help.py +++ b/pym/_emerge/help.py @@ -416,7 +416,10 @@ def help(myopts, havecolor=1): desc = "Account for library link-level dependencies during " + \ "--depclean and --prune actions. This " + \ "option is enabled by default. In some cases this can " + \ - "be somewhat time-consuming." + "be somewhat time-consuming. This option is ignored " + \ + "when FEATURES=\"preserve-libs\" is enabled in " + \ + "make.conf(5), since any libraries that have " + \ + "consumers will simply be preserved." for line in wrap(desc, desc_width): print(desc_indent + line) print() diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 1edda244c..01c8b0ef9 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -1480,9 +1480,13 @@ class dblink(object): try: plib_registry.load() - if unmerge: - # self.mycpv is about to be unmerged, so we shouldn't pay - # attention to its needs in the linkmap. + unmerge_with_replacement = \ + unmerge and preserve_paths is not None + if unmerge_with_replacement: + # If self.mycpv is about to be unmerged and we + # have a replacement package, we want to exclude + # the irrelevant NEEDED data that belongs to + # files which are being unmerged now. exclude_pkgs = (self.mycpv,) else: exclude_pkgs = None @@ -1490,6 +1494,11 @@ class dblink(object): self._linkmap_rebuild(exclude_pkgs=exclude_pkgs, include_file=needed, preserve_paths=preserve_paths) + unmerge_preserve = None + if unmerge and not unmerge_with_replacement: + unmerge_preserve = \ + self._find_libs_to_preserve(unmerge=True) + cpv_lib_map = self._find_unused_preserved_libs() if cpv_lib_map: self._remove_preserved_libs(cpv_lib_map) @@ -1499,8 +1508,16 @@ class dblink(object): self.vartree.dbapi.removeFromContents(cpv, removed) if unmerge: - plib_registry.unregister(self.mycpv, self.settings["SLOT"], - self.vartree.dbapi.cpv_counter(self.mycpv)) + counter = self.vartree.dbapi.cpv_counter(self.mycpv) + plib_registry.unregister(self.mycpv, + self.settings["SLOT"], counter) + if unmerge_preserve: + plib_registry.register(self.mycpv, + self.settings["SLOT"], counter, unmerge_preserve) + # Remove the preserved files from our contents + # so that they won't be unmerged. + self.vartree.dbapi.removeFromContents(self, + unmerge_preserve) plib_registry.store() finally: @@ -1605,6 +1622,9 @@ class dblink(object): level=logging.ERROR, noiselevel=-1) myebuildpath = None + self._prune_plib_registry(unmerge=True, needed=needed, + preserve_paths=preserve_paths) + builddir_lock = None scheduler = self._scheduler retval = os.EX_OK @@ -1647,8 +1667,6 @@ class dblink(object): showMessage(_("!!! FAILED postrm: %s\n") % retval, level=logging.ERROR, noiselevel=-1) - self._prune_plib_registry(unmerge=True, needed=needed, - preserve_paths=preserve_paths) finally: self.vartree.dbapi._bump_mtime(self.mycpv) if builddir_lock: @@ -2253,21 +2271,26 @@ class dblink(object): "due to error: Command Not Found: %s\n") % (e,), level=logging.ERROR, noiselevel=-1) - def _find_libs_to_preserve(self): + def _find_libs_to_preserve(self, unmerge=False): """ - Get set of relative paths for libraries to be preserved. The file - paths are selected from self._installed_instance.getcontents(). + Get set of relative paths for libraries to be preserved. When + unmerge is False, file paths to preserver are selected from + self._installed_instance. Otherwise, paths are selected from + self. """ if self._linkmap_broken or \ self.vartree.dbapi._linkmap is None or \ self.vartree.dbapi._plib_registry is None or \ - self._installed_instance is None or \ + (not unmerge and self._installed_instance is None) or \ "preserve-libs" not in self.settings.features: return None os = _os_merge linkmap = self.vartree.dbapi._linkmap - installed_instance = self._installed_instance + if unmerge: + installed_instance = self + else: + installed_instance = self._installed_instance old_contents = installed_instance.getcontents() root = self.settings['ROOT'] root_len = len(root) - 1 @@ -2307,7 +2330,9 @@ class dblink(object): os = portage.os f = f_abs[root_len:] - if self.isowner(f): + if not unmerge and self.isowner(f): + # We have an indentically named replacement file, + # so we don't try to preserve the old copy. continue try: consumers = linkmap.findConsumers(f) @@ -2324,8 +2349,6 @@ class dblink(object): # Note that consumers can also be providers. for provider_node, consumers in consumer_map.items(): for c in consumers: - if self.isowner(c): - continue consumer_node = path_to_node(c) if installed_instance.isowner(c) and \ consumer_node not in provider_nodes: @@ -3293,8 +3316,13 @@ class dblink(object): linkmap = self.vartree.dbapi._linkmap plib_registry = self.vartree.dbapi._plib_registry - include_file = None - preserve_paths = None + # We initialize preserve_paths to an empty set rather + # than None here because it plays an important role + # in prune_plib_registry logic by serving to indicate + # that we have a replacement for a package that's + # being unmerged. + + preserve_paths = set() needed = None if not (linkmap is None or plib_registry is None): plib_registry.lock() @@ -3304,6 +3332,10 @@ class dblink(object): self._linkmap_rebuild(include_file=needed) # Preserve old libs if they are still in use + # TODO: Handle cases where the previous instance + # has already been uninstalled but it still has some + # preserved libraries in the registry that we may + # want to preserve here. preserve_paths = self._find_libs_to_preserve() finally: plib_registry.unlock() @@ -3396,14 +3428,35 @@ class dblink(object): continue if cpv == self.mycpv: continue + has_vdb_entry = True try: slot, counter = self.vartree.dbapi.aux_get( cpv, ["SLOT", "COUNTER"]) except KeyError: - continue + has_vdb_entry = False + # It's possible for previously unmerged packages + # to have preserved libs in the registry, so try + # to retrieve the slot and counter from there. + has_registry_entry = False + for plib_cps, (plib_cpv, plib_counter, plib_paths) in \ + plib_registry._data.items(): + if plib_cpv != cpv: + continue + try: + cp, slot = plib_cps.split(":", 1) + except ValueError: + continue + counter = plib_counter + has_registry_entry = True + break + + if not has_registry_entry: + continue + remaining = [f for f in plib_dict[cpv] if f not in paths] plib_registry.register(cpv, slot, counter, remaining) - self.vartree.dbapi.removeFromContents(cpv, paths) + if has_vdb_entry: + self.vartree.dbapi.removeFromContents(cpv, paths) plib_registry.store() finally: -- 2.26.2