Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>
Subject: [gentoo-portage-dev] [PATCH v2] LinkageMapELF: account for {PROVIDES,REQUIRES}_EXCLUDE (bug 565792)
Date: Sun, 15 Nov 2015 09:22:34
Message-Id: 1447579303-14532-1-git-send-email-zmedico@gentoo.org
In Reply to: [gentoo-portage-dev] [PATCH] LinkageMapELF: account for {PROVIDES,REQUIRES}_EXCLUDE (bug 565792) by Zac Medico
1 Make LinkageMapELF safely discard NEEDED.ELF.2 data when possible,
2 in a manner consistent with PROVIDES_EXCLUDE and REQUIRES_EXCLUDE.
3 Achieve this by intersection with PROVIDES and REQUIRES, which is
4 equivalent to evaluating PROVIDES_EXCLUDE and REQUIRES_EXCLUDE,
5 but simpler to do. Do not apply PROVIDES_EXCLUDE for entries that
6 have non-empty needed (after REQUIRES_EXCLUDE has been applied),
7 since that will require a significant number of additional changes
8 to LinkageMapELF.
9
10 X-Gentoo-Bug: 565792
11 X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=565792
12 ---
13 [PATCH v2] fixes the PROVIDES_EXCLUDE code to check if the entry
14 has the necessary data to construct an SonameAtom
15
16 pym/portage/util/_dyn_libs/LinkageMapELF.py | 46 +++++++++++++++++++++++++----
17 pym/portage/util/_dyn_libs/NeededEntry.py | 24 +++++++++++++++
18 2 files changed, 65 insertions(+), 5 deletions(-)
19
20 diff --git a/pym/portage/util/_dyn_libs/LinkageMapELF.py b/pym/portage/util/_dyn_libs/LinkageMapELF.py
21 index 63e2213..466fb94 100644
22 --- a/pym/portage/util/_dyn_libs/LinkageMapELF.py
23 +++ b/pym/portage/util/_dyn_libs/LinkageMapELF.py
24 @@ -11,6 +11,8 @@ from portage import _os_merge
25 from portage import _unicode_decode
26 from portage import _unicode_encode
27 from portage.cache.mappings import slot_dict_class
28 +from portage.dep.soname.parse import parse_soname_deps
29 +from portage.dep.soname.SonameAtom import SonameAtom
30 from portage.exception import CommandNotFound, InvalidData
31 from portage.localization import _
32 from portage.util import getlibpaths
33 @@ -225,7 +227,7 @@ class LinkageMapELF(object):
34 for line in grabfile(include_file):
35 lines.append((None, include_file, line))
36
37 - aux_keys = [self._needed_aux_key]
38 + aux_keys = [self._needed_aux_key, 'PROVIDES', 'REQUIRES']
39 can_lock = os.access(os.path.dirname(self._dbapi._dbroot), os.W_OK)
40 if can_lock:
41 self._dbapi.lock()
42 @@ -233,10 +235,26 @@ class LinkageMapELF(object):
43 for cpv in self._dbapi.cpv_all():
44 if exclude_pkgs is not None and cpv in exclude_pkgs:
45 continue
46 + metadata = dict(zip(aux_keys, self._dbapi.aux_get(cpv, aux_keys)))
47 + soname_deps = {}
48 +
49 + for k in ('PROVIDES', 'REQUIRES'):
50 + try:
51 + soname_deps[k] = frozenset(parse_soname_deps(metadata[k]))
52 + except InvalidData as e:
53 + soname_deps[k] = None
54 + location = self._dbapi.getpath(cpv, filename=k)
55 + writemsg_level(_("\nInvalid data in %s: %s\n\n") %
56 + (location, e), level=logging.ERROR, noiselevel=-1)
57 +
58 + provides = soname_deps['PROVIDES']
59 + requires = soname_deps['REQUIRES']
60 +
61 needed_file = self._dbapi.getpath(cpv,
62 filename=self._needed_aux_key)
63 +
64 for line in self._dbapi.aux_get(cpv, aux_keys)[0].splitlines():
65 - lines.append((cpv, needed_file, line))
66 + lines.append((cpv, provides, requires, needed_file, line))
67 finally:
68 if can_lock:
69 self._dbapi.unlock()
70 @@ -290,7 +308,8 @@ class LinkageMapELF(object):
71 continue
72 fields[1] = fields[1][root_len:]
73 owner = plibs.pop(fields[1], None)
74 - lines.append((owner, "scanelf", ";".join(fields)))
75 + lines.append((owner, None, None,
76 + "scanelf", ";".join(fields)))
77 proc.wait()
78 proc.stdout.close()
79
80 @@ -302,13 +321,14 @@ class LinkageMapELF(object):
81 # is important in order to prevent findConsumers from raising
82 # an unwanted KeyError.
83 for x, cpv in plibs.items():
84 - lines.append((cpv, "plibs", ";".join(['', x, '', '', ''])))
85 + lines.append((cpv, None, None,
86 + "plibs", ";".join(['', x, '', '', ''])))
87
88 # Share identical frozenset instances when available,
89 # in order to conserve memory.
90 frozensets = {}
91
92 - for owner, location, l in lines:
93 + for owner, provides, requires, location, l in lines:
94 l = l.rstrip("\n")
95 if not l:
96 continue
97 @@ -335,6 +355,22 @@ class LinkageMapELF(object):
98 if arch is None:
99 arch = _approx_multilib_categories.get(
100 entry.arch, entry.arch)
101 + # initialize multilib_category for intersect_requires
102 + entry.multilib_category = arch
103 +
104 + if requires is not None:
105 + # Apply REQUIRES_EXCLUDE.
106 + entry = entry.intersect_requires(requires)
107 +
108 + if provides is not None:
109 + # Apply PROVIDES_EXCLUDE. Don't exclude entries with
110 + # non-empty entry.needed, since we want to ensure that
111 + # entry.needed is satisfied. TODO: Apply PROVIDES_EXCLUDE
112 + # to entries with non-empty entry.needed.
113 + if (not entry.needed and entry.soname and
114 + entry.multilib_category and SonameAtom(
115 + entry.multilib_category, entry.soname) not in provides):
116 + continue
117
118 obj = entry.filename
119 soname = entry.soname
120 diff --git a/pym/portage/util/_dyn_libs/NeededEntry.py b/pym/portage/util/_dyn_libs/NeededEntry.py
121 index c52cfce..824f94c 100644
122 --- a/pym/portage/util/_dyn_libs/NeededEntry.py
123 +++ b/pym/portage/util/_dyn_libs/NeededEntry.py
124 @@ -6,6 +6,7 @@ from __future__ import unicode_literals
125 import sys
126
127 from portage import _encodings, _unicode_encode
128 +from portage.dep.soname.SonameAtom import SonameAtom
129 from portage.exception import InvalidData
130 from portage.localization import _
131
132 @@ -57,6 +58,29 @@ class NeededEntry(object):
133
134 return obj
135
136 + def intersect_requires(self, requires):
137 + """
138 + Return a new instance, filtering out data that does not intersect
139 + with the given requires data. This is a convenient way to account
140 + for REQUIRES_EXCLUDE, since the intersection operation will filter
141 + out anything that REQUIRES_EXCLUDE would exclude.
142 +
143 + @param requires: required soname atoms
144 + @type requires: frozenset
145 + """
146 + obj = self.__class__()
147 + obj.arch = self.arch
148 + obj.filename = self.filename
149 + obj.multilib_category = self.multilib_category
150 + obj.soname = self.soname
151 + obj.runpaths = self.runpaths
152 + filtered_needed = []
153 + for soname in self.needed:
154 + if SonameAtom(self.multilib_category, soname) in requires:
155 + filtered_needed.append(soname)
156 + obj.needed = tuple(filtered_needed)
157 + return obj
158 +
159 def __str__(self):
160 """
161 Format this entry for writing to a NEEDED.ELF.2 file.
162 --
163 2.4.9