From 296fc63fee262600811520fccf4692f47a39ffba Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sun, 17 Jul 2011 22:30:54 -0700 Subject: [PATCH] preserve-libs: search for alt providers of soname This will fix bug #289180 by making LinkageMapELF.findconsumers() exclude consumers from the results in cases when they are satisfied by an alternative provider of the required soname. --- pym/portage/dbapi/vartree.py | 3 +- pym/portage/util/_dyn_libs/LinkageMapELF.py | 58 ++++++++++++++++++--- 2 files changed, 52 insertions(+), 9 deletions(-) diff --git a/pym/portage/dbapi/vartree.py b/pym/portage/dbapi/vartree.py index 9efc47ff9..d61f7390e 100644 --- a/pym/portage/dbapi/vartree.py +++ b/pym/portage/dbapi/vartree.py @@ -2422,7 +2422,8 @@ class dblink(object): # so we don't try to preserve the old copy. continue try: - consumers = linkmap.findConsumers(f) + consumers = linkmap.findConsumers(f, + exclude_providers=(installed_instance.isowner,)) except KeyError: continue if not consumers: diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py b/pym/portage/util/_dyn_libs/LinkageMapELF.py index c55d6d623..c8e9acfe3 100644 --- a/pym/portage/util/_dyn_libs/LinkageMapELF.py +++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py @@ -603,7 +603,7 @@ class LinkageMapELF(object): rValue[soname].add(provider) return rValue - def findConsumers(self, obj): + def findConsumers(self, obj, exclude_providers=None): """ Find consumers of an object or object key. @@ -626,8 +626,20 @@ class LinkageMapELF(object): corresponding libtool archive (*.la) files to detect such consumers (revdep-rebuild is able to detect them). + The exclude_providers argument is useful for determining whether + removal of one or more packages will create unsatisfied consumers. When + this option is given, consumers are excluded from the results if there + is an alternative provider (which is not excluded) of the required + soname such that the consumers will remain satisfied if the files + owned by exclude_providers are removed. + @param obj: absolute path to an object or a key from _obj_properties @type obj: string (example: '/usr/bin/bar') or _ObjectKey + @param exclude_providers: A collection of callables that each take a + single argument referring to the path of a library (example: + '/usr/lib/libssl.so.0.9.8'), and return True if the library is + owned by a provider which is planned for removal. + @type exclude_providers: collection @rtype: set of strings (example: set(['/bin/foo', '/usr/bin/bar'])) @return: The return value is a soname -> set-of-library-paths, where set-of-library-paths satisfy soname. @@ -636,8 +648,6 @@ class LinkageMapELF(object): os = _os_merge - rValue = set() - if not self._libs: self.rebuild() @@ -672,15 +682,47 @@ class LinkageMapELF(object): (soname_st.st_dev, soname_st.st_ino): return set() - # Determine the directory(ies) from the set of objects. - objs_dir_keys = set(self._path_key(os.path.dirname(x)) for x in objs) + arch, _needed, _path, soname, _objs = self._obj_properties[obj_key] + + soname_node = None + arch_map = self._libs.get(arch) + if arch_map is not None: + soname_node = arch_map.get(soname) + defpath_keys = set(self._path_key(x) for x in self._defpath) + satisfied_consumer_keys = set() + if soname_node is not None: + if exclude_providers is not None: + relevant_dir_keys = set() + for provider_key in soname_node.providers: + provider_objs = self._obj_properties[provider_key][4] + for p in provider_objs: + for excluded in exclude_providers: + if not excluded(p): + # This provider is not excluded. It will + # satisfy a consumer of this soname if it + # is in the default ld.so path or the + # consumer's runpath. + relevant_dir_keys.add( + self._path_key(os.path.dirname(p))) + + for consumer_key in soname_node.consumers: + _arch, _needed, path, _soname, _consumer_objs = \ + self._obj_properties[consumer_key] + path_keys = defpath_keys.copy() + path_keys.update(self._path_key(x) for x in path) + if relevant_dir_keys.intersection(path_keys): + satisfied_consumer_keys.add(consumer_key) - arch, _needed, _path, soname, _objs = self._obj_properties[obj_key] - if arch in self._libs and soname in self._libs[arch]: + rValue = set() + if soname_node is not None: # For each potential consumer, add it to rValue if an object from the # arguments resides in the consumer's runpath. - for consumer_key in self._libs[arch][soname].consumers: + objs_dir_keys = set(self._path_key(os.path.dirname(x)) + for x in objs) + for consumer_key in soname_node.consumers: + if consumer_key in satisfied_consumer_keys: + continue _arch, _needed, path, _soname, consumer_objs = \ self._obj_properties[consumer_key] path_keys = defpath_keys.union(self._path_key(x) for x in path) -- 2.26.2