Make LinkageMapELF safely discard NEEDED.ELF.2 data when possible,
in a manner consistent with PROVIDES_EXCLUDE and REQUIRES_EXCLUDE.
Achieve this by intersection with PROVIDES and REQUIRES, which is
equivalent to evaluating PROVIDES_EXCLUDE and REQUIRES_EXCLUDE,
but simpler to do. Do not apply PROVIDES_EXCLUDE for entries that
have non-empty needed (after REQUIRES_EXCLUDE has been applied),
since that will require a significant number of additional changes
to LinkageMapELF.

X-Gentoo-Bug: 565792
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=565792
---
 pym/portage/util/_dyn_libs/LinkageMapELF.py | 45 +++++++++++++++++++++++++----
 pym/portage/util/_dyn_libs/NeededEntry.py   | 24 +++++++++++++++
 2 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py 
b/pym/portage/util/_dyn_libs/LinkageMapELF.py
index 63e2213..24d03a5 100644
--- a/pym/portage/util/_dyn_libs/LinkageMapELF.py
+++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py
@@ -11,6 +11,8 @@ from portage import _os_merge
 from portage import _unicode_decode
 from portage import _unicode_encode
 from portage.cache.mappings import slot_dict_class
+from portage.dep.soname.parse import parse_soname_deps
+from portage.dep.soname.SonameAtom import SonameAtom
 from portage.exception import CommandNotFound, InvalidData
 from portage.localization import _
 from portage.util import getlibpaths
@@ -225,7 +227,7 @@ class LinkageMapELF(object):
                        for line in grabfile(include_file):
                                lines.append((None, include_file, line))
 
-               aux_keys = [self._needed_aux_key]
+               aux_keys = [self._needed_aux_key, 'PROVIDES', 'REQUIRES']
                can_lock = os.access(os.path.dirname(self._dbapi._dbroot), 
os.W_OK)
                if can_lock:
                        self._dbapi.lock()
@@ -233,10 +235,26 @@ class LinkageMapELF(object):
                        for cpv in self._dbapi.cpv_all():
                                if exclude_pkgs is not None and cpv in 
exclude_pkgs:
                                        continue
+                               metadata = dict(zip(aux_keys, 
self._dbapi.aux_get(cpv, aux_keys)))
+                               soname_deps = {}
+
+                               for k in ('PROVIDES', 'REQUIRES'):
+                                       try:
+                                               soname_deps[k] = 
frozenset(parse_soname_deps(metadata[k]))
+                                       except InvalidData as e:
+                                               soname_deps[k] = None
+                                               location = 
self._dbapi.getpath(cpv, filename=k)
+                                               writemsg_level(_("\nInvalid 
data in %s: %s\n\n") %
+                                                       (location, e), 
level=logging.ERROR, noiselevel=-1)
+
+                               provides = soname_deps['PROVIDES']
+                               requires = soname_deps['REQUIRES']
+
                                needed_file = self._dbapi.getpath(cpv,
                                        filename=self._needed_aux_key)
+
                                for line in self._dbapi.aux_get(cpv, 
aux_keys)[0].splitlines():
-                                       lines.append((cpv, needed_file, line))
+                                       lines.append((cpv, provides, requires, 
needed_file, line))
                finally:
                        if can_lock:
                                self._dbapi.unlock()
@@ -290,7 +308,8 @@ class LinkageMapELF(object):
                                                continue
                                        fields[1] = fields[1][root_len:]
                                        owner = plibs.pop(fields[1], None)
-                                       lines.append((owner, "scanelf", 
";".join(fields)))
+                                       lines.append((owner, None, None,
+                                               "scanelf", ";".join(fields)))
                                proc.wait()
                                proc.stdout.close()
 
@@ -302,13 +321,14 @@ class LinkageMapELF(object):
                        # is important in order to prevent findConsumers from 
raising
                        # an unwanted KeyError.
                        for x, cpv in plibs.items():
-                               lines.append((cpv, "plibs", ";".join(['', x, 
'', '', ''])))
+                               lines.append((cpv, None, None,
+                                       "plibs", ";".join(['', x, '', '', ''])))
 
                # Share identical frozenset instances when available,
                # in order to conserve memory.
                frozensets = {}
 
-               for owner, location, l in lines:
+               for owner, provides, requires, location, l in lines:
                        l = l.rstrip("\n")
                        if not l:
                                continue
@@ -335,6 +355,21 @@ class LinkageMapELF(object):
                        if arch is None:
                                arch = _approx_multilib_categories.get(
                                        entry.arch, entry.arch)
+                               # initialize multilib_category for 
intersect_requires
+                               entry.multilib_category = arch
+
+                       if requires is not None:
+                               # Apply REQUIRES_EXCLUDE.
+                               entry = entry.intersect_requires(requires)
+
+                       if provides is not None:
+                               # Apply PROVIDES_EXCLUDE. Don't exclude entries 
with
+                               # non-empty entry.needed, since we want to 
ensure that
+                               # entry.needed is satisfied. TODO: Apply 
PROVIDES_EXCLUDE
+                               # to entries with non-empty entry.needed.
+                               if not entry.needed and SonameAtom(
+                                       entry.multilib_category, entry.soname) 
not in provides:
+                                       continue
 
                        obj = entry.filename
                        soname = entry.soname
diff --git a/pym/portage/util/_dyn_libs/NeededEntry.py 
b/pym/portage/util/_dyn_libs/NeededEntry.py
index c52cfce..824f94c 100644
--- a/pym/portage/util/_dyn_libs/NeededEntry.py
+++ b/pym/portage/util/_dyn_libs/NeededEntry.py
@@ -6,6 +6,7 @@ from __future__ import unicode_literals
 import sys
 
 from portage import _encodings, _unicode_encode
+from portage.dep.soname.SonameAtom import SonameAtom
 from portage.exception import InvalidData
 from portage.localization import _
 
@@ -57,6 +58,29 @@ class NeededEntry(object):
 
                return obj
 
+       def intersect_requires(self, requires):
+               """
+               Return a new instance, filtering out data that does not 
intersect
+               with the given requires data. This is a convenient way to 
account
+               for REQUIRES_EXCLUDE, since the intersection operation will 
filter
+               out anything that REQUIRES_EXCLUDE would exclude.
+
+               @param requires: required soname atoms
+               @type requires: frozenset
+               """
+               obj = self.__class__()
+               obj.arch = self.arch
+               obj.filename = self.filename
+               obj.multilib_category = self.multilib_category
+               obj.soname = self.soname
+               obj.runpaths = self.runpaths
+               filtered_needed = []
+               for soname in self.needed:
+                       if SonameAtom(self.multilib_category, soname) in 
requires:
+                               filtered_needed.append(soname)
+               obj.needed = tuple(filtered_needed)
+               return obj
+
        def __str__(self):
                """
                Format this entry for writing to a NEEDED.ELF.2 file.
-- 
2.4.9


Reply via email to