1 |
Soname dependency resolution is disabled by default, since it will not |
2 |
work correctly unless all available installed and binary packages have |
3 |
been built by a version of portage which generates REQUIRES and PROVIDES |
4 |
metadata. |
5 |
|
6 |
Soname dependency resolution is enabled when --ignore-soname-deps=n is |
7 |
specified, and one of the following is true: |
8 |
|
9 |
* --usepkgonly option is enabled |
10 |
|
11 |
* removal actions (--depclean and --prune) |
12 |
|
13 |
Soname dependencies are automatically ignored for dependency |
14 |
calculations that can pull unbuilt ebuilds into the dependency graph, |
15 |
since unbuilt ebuilds do not have any soname dependency metadata, |
16 |
making it impossible to determine whether an unresolved soname |
17 |
dependency can be satisfied. Therefore, --usepkgonly must be used |
18 |
in order to enable soname depedency resolution when installing packages. |
19 |
|
20 |
A new soname.provided file is supported for profiles, making it possible |
21 |
to selectively ignore soname dependencies (see the portage(5) man page). |
22 |
|
23 |
When soname dependency resolution is enabled, the soname dependencies |
24 |
are represented as SonameAtom instances which expose an interface that |
25 |
is minimally compatible with Atom instances. This allows both types of |
26 |
atoms to be satisfied using mostly the same mechanisms, with minimal |
27 |
use of conditional logic to handle the differences. Both atom classes |
28 |
have "soname" and "package" attributes that make it convenient for |
29 |
conditional code to distinguish package atoms and soname atoms. Both |
30 |
classes also implement a match method, so that it is possible to match |
31 |
a Package instance using identical syntax for both types of atoms. |
32 |
|
33 |
Since soname dependencies and slot-operator := dependencies share many |
34 |
properties, the slot-operator rebuild code has been generalized to |
35 |
handle both types of dependencies. Many of the existing unit tests |
36 |
involving slot-operator dependencies have been copied and adapted to |
37 |
test soname dependencies (the new tests are located in the |
38 |
pym/portage/tests/resolver/soname/ directory). |
39 |
|
40 |
X-Gentoo-Bug: 282639 |
41 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=282639 |
42 |
--- |
43 |
man/emerge.1 | 13 + |
44 |
man/portage.5 | 23 ++ |
45 |
pym/_emerge/FakeVartree.py | 16 +- |
46 |
pym/_emerge/Package.py | 55 +++- |
47 |
pym/_emerge/actions.py | 9 +- |
48 |
pym/_emerge/create_depgraph_params.py | 6 + |
49 |
pym/_emerge/create_world_atom.py | 6 +- |
50 |
pym/_emerge/depgraph.py | 346 +++++++++++++++------ |
51 |
pym/_emerge/main.py | 10 + |
52 |
pym/_emerge/resolver/DbapiProvidesIndex.py | 101 ++++++ |
53 |
pym/_emerge/resolver/output.py | 10 +- |
54 |
pym/_emerge/resolver/package_tracker.py | 42 ++- |
55 |
pym/_emerge/resolver/slot_collision.py | 35 ++- |
56 |
pym/portage/dbapi/DummyTree.py | 16 + |
57 |
pym/portage/dep/__init__.py | 25 ++ |
58 |
pym/portage/dep/soname/SonameAtom.py | 72 +++++ |
59 |
pym/portage/dep/soname/parse.py | 47 +++ |
60 |
pym/portage/package/ebuild/config.py | 13 + |
61 |
pym/portage/tests/resolver/ResolverPlayground.py | 3 + |
62 |
pym/portage/tests/resolver/soname/__init__.py | 2 + |
63 |
pym/portage/tests/resolver/soname/__test__.py | 2 + |
64 |
.../tests/resolver/soname/test_autounmask.py | 103 ++++++ |
65 |
pym/portage/tests/resolver/soname/test_depclean.py | 61 ++++ |
66 |
.../tests/resolver/soname/test_downgrade.py | 240 ++++++++++++++ |
67 |
.../tests/resolver/soname/test_or_choices.py | 92 ++++++ |
68 |
.../tests/resolver/soname/test_reinstall.py | 87 ++++++ |
69 |
.../tests/resolver/soname/test_skip_update.py | 86 +++++ |
70 |
.../soname/test_slot_conflict_reinstall.py | 342 ++++++++++++++++++++ |
71 |
.../resolver/soname/test_slot_conflict_update.py | 117 +++++++ |
72 |
.../tests/resolver/soname/test_soname_provided.py | 78 +++++ |
73 |
.../tests/resolver/soname/test_unsatisfiable.py | 71 +++++ |
74 |
.../tests/resolver/soname/test_unsatisfied.py | 87 ++++++ |
75 |
pym/portage/tests/resolver/test_package_tracker.py | 4 +- |
76 |
33 files changed, 2098 insertions(+), 122 deletions(-) |
77 |
create mode 100644 pym/_emerge/resolver/DbapiProvidesIndex.py |
78 |
create mode 100644 pym/portage/dbapi/DummyTree.py |
79 |
create mode 100644 pym/portage/dep/soname/SonameAtom.py |
80 |
create mode 100644 pym/portage/dep/soname/parse.py |
81 |
create mode 100644 pym/portage/tests/resolver/soname/__init__.py |
82 |
create mode 100644 pym/portage/tests/resolver/soname/__test__.py |
83 |
create mode 100644 pym/portage/tests/resolver/soname/test_autounmask.py |
84 |
create mode 100644 pym/portage/tests/resolver/soname/test_depclean.py |
85 |
create mode 100644 pym/portage/tests/resolver/soname/test_downgrade.py |
86 |
create mode 100644 pym/portage/tests/resolver/soname/test_or_choices.py |
87 |
create mode 100644 pym/portage/tests/resolver/soname/test_reinstall.py |
88 |
create mode 100644 pym/portage/tests/resolver/soname/test_skip_update.py |
89 |
create mode 100644 pym/portage/tests/resolver/soname/test_slot_conflict_reinstall.py |
90 |
create mode 100644 pym/portage/tests/resolver/soname/test_slot_conflict_update.py |
91 |
create mode 100644 pym/portage/tests/resolver/soname/test_soname_provided.py |
92 |
create mode 100644 pym/portage/tests/resolver/soname/test_unsatisfiable.py |
93 |
create mode 100644 pym/portage/tests/resolver/soname/test_unsatisfied.py |
94 |
|
95 |
diff --git a/man/emerge.1 b/man/emerge.1 |
96 |
index fd9140f..7d8d003 100644 |
97 |
--- a/man/emerge.1 |
98 |
+++ b/man/emerge.1 |
99 |
@@ -568,6 +568,19 @@ only for debugging purposes, and it only affects built packages |
100 |
that specify slot/sub\-slot := operator dependencies which are |
101 |
supported beginning with \fBEAPI 5\fR. |
102 |
.TP |
103 |
+.BR "\-\-ignore\-soname\-deps < y | n >" |
104 |
+Ignore the soname dependencies of binary and installed packages. This |
105 |
+option is enabled by default, since soname dependencies are relatively |
106 |
+new, and the required metadata is not guaranteed to exist for binary and |
107 |
+installed packages built with older versions of portage. Also, soname |
108 |
+dependencies will be automatically ignored for dependency calculations |
109 |
+that can pull unbuilt ebuilds into the dependency graph, since unbuilt |
110 |
+ebuilds do not have any soname dependency metadata, making it impossible |
111 |
+to determine whether an unresolved soname dependency can be satisfied. |
112 |
+Therefore, \fB\-\-usepkgonly\fR (or \fB\-\-getbinpkgonly\fR) must be |
113 |
+used in order to enable soname depedency resolution when installing |
114 |
+packages. |
115 |
+.TP |
116 |
.BR "-j [JOBS], \-\-jobs[=JOBS]" |
117 |
Specifies the number of packages to build simultaneously. If this option is |
118 |
given without an argument, emerge will not limit the number of jobs that can |
119 |
diff --git a/man/portage.5 b/man/portage.5 |
120 |
index cec4e2f..ed5140d 100644 |
121 |
--- a/man/portage.5 |
122 |
+++ b/man/portage.5 |
123 |
@@ -36,6 +36,7 @@ package.use.stable.force |
124 |
package.use.stable.mask |
125 |
parent |
126 |
profile.bashrc |
127 |
+soname.provided |
128 |
use.force |
129 |
use.mask |
130 |
use.stable.mask |
131 |
@@ -504,6 +505,28 @@ If needed, this file can be used to set up a special environment for ebuilds, |
132 |
different from the standard root environment. The syntax is the same as for |
133 |
any other bash script. |
134 |
.TP |
135 |
+.BR soname.provided |
136 |
+A list of sonames that portage should assume have been provided. This |
137 |
+is useful for using portage to install binary packages on top of a base |
138 |
+image which lacks /var/db/pkg for some reason (perhaps the image was |
139 |
+assembled by another package manager, or by Linux From Scratch). |
140 |
+ |
141 |
+.I Format: |
142 |
+.nf |
143 |
+\- comments begin with # (no inline comments) |
144 |
+\- line begins with a multilib category |
145 |
+\- multilib category is followed by one or more sonames |
146 |
+\- only one multilib category is allowed per line |
147 |
+\- prefixing an soname with a '\-' will negate a parent profile setting |
148 |
+.fi |
149 |
+ |
150 |
+.I Example: |
151 |
+.nf |
152 |
+# provide libc and ld-linux sonames for x86_32 and x86_64 categories |
153 |
+x86_32 ld-linux.so.2 libc.so.6 |
154 |
+x86_64 ld-linux-x86-64.so.2 libc.so.6 |
155 |
+.fi |
156 |
+.TP |
157 |
\fBuse.force\fR and \fBuse.stable.force\fR |
158 |
Some USE flags don't make sense to disable under certain conditions. Here we |
159 |
list forced flags. |
160 |
diff --git a/pym/_emerge/FakeVartree.py b/pym/_emerge/FakeVartree.py |
161 |
index 254f667..ebe07bb 100644 |
162 |
--- a/pym/_emerge/FakeVartree.py |
163 |
+++ b/pym/_emerge/FakeVartree.py |
164 |
@@ -17,6 +17,7 @@ from portage.eapi import _get_eapi_attrs |
165 |
from portage.exception import InvalidData, InvalidDependString |
166 |
from portage.update import grab_updates, parse_updates, update_dbentries |
167 |
from portage.versions import _pkg_str |
168 |
+from _emerge.resolver.DbapiProvidesIndex import PackageDbapiProvidesIndex |
169 |
|
170 |
if sys.hexversion >= 0x3000000: |
171 |
long = int |
172 |
@@ -24,12 +25,15 @@ if sys.hexversion >= 0x3000000: |
173 |
else: |
174 |
_unicode = unicode |
175 |
|
176 |
-class FakeVardbapi(PackageVirtualDbapi): |
177 |
+class FakeVardbGetPath(object): |
178 |
""" |
179 |
Implements the vardbapi.getpath() method which is used in error handling |
180 |
code for the Package class and vartree.get_provide(). |
181 |
""" |
182 |
- def getpath(self, cpv, filename=None): |
183 |
+ def __init__(self, vardb): |
184 |
+ self.settings = vardb.settings |
185 |
+ |
186 |
+ def __call__(self, cpv, filename=None): |
187 |
path = os.path.join(self.settings['EROOT'], VDB_PATH, cpv) |
188 |
if filename is not None: |
189 |
path =os.path.join(path, filename) |
190 |
@@ -50,7 +54,8 @@ class FakeVartree(vartree): |
191 |
is not a matching ebuild in the tree). Instances of this class are not |
192 |
populated until the sync() method is called.""" |
193 |
def __init__(self, root_config, pkg_cache=None, pkg_root_config=None, |
194 |
- dynamic_deps=True, ignore_built_slot_operator_deps=False): |
195 |
+ dynamic_deps=True, ignore_built_slot_operator_deps=False, |
196 |
+ soname_deps=False): |
197 |
self._root_config = root_config |
198 |
self._dynamic_deps = dynamic_deps |
199 |
self._ignore_built_slot_operator_deps = ignore_built_slot_operator_deps |
200 |
@@ -68,7 +73,10 @@ class FakeVartree(vartree): |
201 |
mykeys.append("_mtime_") |
202 |
self._db_keys = mykeys |
203 |
self._pkg_cache = pkg_cache |
204 |
- self.dbapi = FakeVardbapi(real_vartree.settings) |
205 |
+ self.dbapi = PackageVirtualDbapi(real_vartree.settings) |
206 |
+ if soname_deps: |
207 |
+ self.dbapi = PackageDbapiProvidesIndex(self.dbapi) |
208 |
+ self.dbapi.getpath = FakeVardbGetPath(self.dbapi) |
209 |
self.dbapi._aux_cache_keys = set(self._db_keys) |
210 |
|
211 |
# Initialize variables needed for lazy cache pulls of the live ebuild |
212 |
diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py |
213 |
index 518dbf6..e8a13cb 100644 |
214 |
--- a/pym/_emerge/Package.py |
215 |
+++ b/pym/_emerge/Package.py |
216 |
@@ -13,9 +13,10 @@ from portage.cache.mappings import slot_dict_class |
217 |
from portage.const import EBUILD_PHASES |
218 |
from portage.dep import Atom, check_required_use, use_reduce, \ |
219 |
paren_enclose, _slot_separator, _repo_separator |
220 |
+from portage.dep.soname.parse import parse_soname_deps |
221 |
from portage.versions import _pkg_str, _unknown_repo |
222 |
from portage.eapi import _get_eapi_attrs, eapi_has_use_aliases |
223 |
-from portage.exception import InvalidDependString |
224 |
+from portage.exception import InvalidData, InvalidDependString |
225 |
from portage.localization import _ |
226 |
from _emerge.Task import Task |
227 |
|
228 |
@@ -36,7 +37,8 @@ class Package(Task): |
229 |
"inherited", "iuse", "mtime", |
230 |
"pf", "root", "slot", "sub_slot", "slot_atom", "version") + \ |
231 |
("_invalid", "_masks", "_metadata", "_provided_cps", |
232 |
- "_raw_metadata", "_use", "_validated_atoms", "_visible") |
233 |
+ "_raw_metadata", "_provides", "_requires", "_use", |
234 |
+ "_validated_atoms", "_visible") |
235 |
|
236 |
metadata_keys = [ |
237 |
"BUILD_TIME", "CHOST", "COUNTER", "DEPEND", "EAPI", |
238 |
@@ -189,6 +191,16 @@ class Package(Task): |
239 |
def stable(self): |
240 |
return self.cpv.stable |
241 |
|
242 |
+ @property |
243 |
+ def provides(self): |
244 |
+ self.invalid |
245 |
+ return self._provides |
246 |
+ |
247 |
+ @property |
248 |
+ def requires(self): |
249 |
+ self.invalid |
250 |
+ return self._requires |
251 |
+ |
252 |
@classmethod |
253 |
def _gen_hash_key(cls, cpv=None, installed=None, onlydeps=None, |
254 |
operation=None, repo_name=None, root_config=None, |
255 |
@@ -311,6 +323,21 @@ class Package(Task): |
256 |
if not self.installed: |
257 |
self._metadata_exception(k, e) |
258 |
|
259 |
+ if self.built: |
260 |
+ k = 'PROVIDES' |
261 |
+ try: |
262 |
+ self._provides = frozenset( |
263 |
+ parse_soname_deps(self._metadata[k])) |
264 |
+ except InvalidData as e: |
265 |
+ self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) |
266 |
+ |
267 |
+ k = 'REQUIRES' |
268 |
+ try: |
269 |
+ self._requires = frozenset( |
270 |
+ parse_soname_deps(self._metadata[k])) |
271 |
+ except InvalidData as e: |
272 |
+ self._invalid_metadata(k + ".syntax", "%s: %s" % (k, e)) |
273 |
+ |
274 |
def copy(self): |
275 |
return Package(built=self.built, cpv=self.cpv, depth=self.depth, |
276 |
installed=self.installed, metadata=self._raw_metadata, |
277 |
@@ -727,32 +754,48 @@ class Package(Task): |
278 |
|
279 |
def __lt__(self, other): |
280 |
if other.cp != self.cp: |
281 |
- return False |
282 |
+ return self.cp < other.cp |
283 |
if portage.vercmp(self.version, other.version) < 0: |
284 |
return True |
285 |
return False |
286 |
|
287 |
def __le__(self, other): |
288 |
if other.cp != self.cp: |
289 |
- return False |
290 |
+ return self.cp <= other.cp |
291 |
if portage.vercmp(self.version, other.version) <= 0: |
292 |
return True |
293 |
return False |
294 |
|
295 |
def __gt__(self, other): |
296 |
if other.cp != self.cp: |
297 |
- return False |
298 |
+ return self.cp > other.cp |
299 |
if portage.vercmp(self.version, other.version) > 0: |
300 |
return True |
301 |
return False |
302 |
|
303 |
def __ge__(self, other): |
304 |
if other.cp != self.cp: |
305 |
- return False |
306 |
+ return self.cp >= other.cp |
307 |
if portage.vercmp(self.version, other.version) >= 0: |
308 |
return True |
309 |
return False |
310 |
|
311 |
+ def with_use(self, use): |
312 |
+ """ |
313 |
+ Return an Package instance with the specified USE flags. The |
314 |
+ current instance may be returned if it has identical USE flags. |
315 |
+ @param use: a set of USE flags |
316 |
+ @type use: frozenset |
317 |
+ @return: A package with the specified USE flags |
318 |
+ @rtype: Package |
319 |
+ """ |
320 |
+ if use is not self.use.enabled: |
321 |
+ pkg = self.copy() |
322 |
+ pkg._metadata["USE"] = " ".join(use) |
323 |
+ else: |
324 |
+ pkg = self |
325 |
+ return pkg |
326 |
+ |
327 |
_all_metadata_keys = set(x for x in portage.auxdbkeys \ |
328 |
if not x.startswith("UNUSED_")) |
329 |
_all_metadata_keys.update(Package.metadata_keys) |
330 |
diff --git a/pym/_emerge/actions.py b/pym/_emerge/actions.py |
331 |
index d393c78..fa4fe19 100644 |
332 |
--- a/pym/_emerge/actions.py |
333 |
+++ b/pym/_emerge/actions.py |
334 |
@@ -787,7 +787,7 @@ def calc_depclean(settings, trees, ldpath_mtimes, |
335 |
for pkg in vardb: |
336 |
if spinner is not None: |
337 |
spinner.update() |
338 |
- pkgs_for_cp = vardb.match_pkgs(pkg.cp) |
339 |
+ pkgs_for_cp = vardb.match_pkgs(Atom(pkg.cp)) |
340 |
if not pkgs_for_cp or pkg not in pkgs_for_cp: |
341 |
raise AssertionError("package expected in matches: " + \ |
342 |
"cp = %s, cpv = %s matches = %s" % \ |
343 |
@@ -931,8 +931,13 @@ def calc_depclean(settings, trees, ldpath_mtimes, |
344 |
|
345 |
parent_strs = [] |
346 |
for parent, atoms in parent_atom_dict.items(): |
347 |
+ # Display package atoms and soname |
348 |
+ # atoms in separate groups. |
349 |
+ atoms = sorted(atoms, reverse=True, |
350 |
+ key=operator.attrgetter('package')) |
351 |
parent_strs.append("%s requires %s" % |
352 |
- (getattr(parent, "cpv", parent), ", ".join(atoms))) |
353 |
+ (getattr(parent, "cpv", parent), |
354 |
+ ", ".join(_unicode(atom) for atom in atoms))) |
355 |
parent_strs.sort() |
356 |
msg = [] |
357 |
msg.append(" %s pulled in by:\n" % (child_node.cpv,)) |
358 |
diff --git a/pym/_emerge/create_depgraph_params.py b/pym/_emerge/create_depgraph_params.py |
359 |
index 11e20f4..2c64928 100644 |
360 |
--- a/pym/_emerge/create_depgraph_params.py |
361 |
+++ b/pym/_emerge/create_depgraph_params.py |
362 |
@@ -21,6 +21,9 @@ def create_depgraph_params(myopts, myaction): |
363 |
# removal by the --depclean action as soon as possible |
364 |
# ignore_built_slot_operator_deps: ignore the slot/sub-slot := operator parts |
365 |
# of dependencies that have been recorded when packages where built |
366 |
+ # ignore_soname_deps: ignore the soname dependencies of built |
367 |
+ # packages, so that they do not trigger dependency resolution |
368 |
+ # failures, or cause packages to be rebuilt or replaced. |
369 |
# with_test_deps: pull in test deps for packages matched by arguments |
370 |
# changed_deps: rebuild installed packages with outdated deps |
371 |
# binpkg_changed_deps: reject binary packages with outdated deps |
372 |
@@ -34,6 +37,9 @@ def create_depgraph_params(myopts, myaction): |
373 |
if ignore_built_slot_operator_deps is not None: |
374 |
myparams["ignore_built_slot_operator_deps"] = ignore_built_slot_operator_deps |
375 |
|
376 |
+ myparams["ignore_soname_deps"] = myopts.get( |
377 |
+ "--ignore-soname-deps", "y") |
378 |
+ |
379 |
dynamic_deps = myopts.get("--dynamic-deps") |
380 |
if dynamic_deps is not None: |
381 |
myparams["dynamic_deps"] = dynamic_deps |
382 |
diff --git a/pym/_emerge/create_world_atom.py b/pym/_emerge/create_world_atom.py |
383 |
index ac994cc..74b0fa5 100644 |
384 |
--- a/pym/_emerge/create_world_atom.py |
385 |
+++ b/pym/_emerge/create_world_atom.py |
386 |
@@ -3,7 +3,7 @@ |
387 |
|
388 |
import sys |
389 |
|
390 |
-from portage.dep import _repo_separator |
391 |
+from portage.dep import Atom, _repo_separator |
392 |
from portage.exception import InvalidData |
393 |
|
394 |
if sys.hexversion >= 0x3000000: |
395 |
@@ -40,7 +40,7 @@ def create_world_atom(pkg, args_set, root_config): |
396 |
repos.append(portdb.repositories.get_name_for_location(tree)) |
397 |
|
398 |
available_slots = set() |
399 |
- for cpv in portdb.match(cp): |
400 |
+ for cpv in portdb.match(Atom(cp)): |
401 |
for repo in repos: |
402 |
try: |
403 |
available_slots.add(portdb._pkg_str(_unicode(cpv), repo).slot) |
404 |
@@ -52,7 +52,7 @@ def create_world_atom(pkg, args_set, root_config): |
405 |
if not slotted: |
406 |
# check the vdb in case this is multislot |
407 |
available_slots = set(vardb._pkg_str(cpv, None).slot \ |
408 |
- for cpv in vardb.match(cp)) |
409 |
+ for cpv in vardb.match(Atom(cp))) |
410 |
slotted = len(available_slots) > 1 or \ |
411 |
(len(available_slots) == 1 and "0" not in available_slots) |
412 |
if slotted and arg_atom.without_repo != cp: |
413 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
414 |
index 1184dd6..f68985b 100644 |
415 |
--- a/pym/_emerge/depgraph.py |
416 |
+++ b/pym/_emerge/depgraph.py |
417 |
@@ -20,6 +20,7 @@ from portage import _unicode_decode, _unicode_encode, _encodings |
418 |
from portage.const import PORTAGE_PACKAGE_ATOM, USER_CONFIG_PATH, VCS_DIRS |
419 |
from portage.dbapi import dbapi |
420 |
from portage.dbapi.dep_expand import dep_expand |
421 |
+from portage.dbapi.DummyTree import DummyTree |
422 |
from portage.dbapi._similar_name_search import similar_name_search |
423 |
from portage.dep import Atom, best_match_to_list, extract_affecting_use, \ |
424 |
check_required_use, human_readable_required_use, match_from_list, \ |
425 |
@@ -77,6 +78,7 @@ from _emerge.UseFlagDisplay import pkg_use_display |
426 |
from _emerge.UserQuery import UserQuery |
427 |
|
428 |
from _emerge.resolver.backtracking import Backtracker, BacktrackParameter |
429 |
+from _emerge.resolver.DbapiProvidesIndex import DbapiProvidesIndex |
430 |
from _emerge.resolver.package_tracker import PackageTracker, PackageTrackerDbapiWrapper |
431 |
from _emerge.resolver.slot_collision import slot_conflict_handler |
432 |
from _emerge.resolver.circular_dependency import circular_dependency_handler |
433 |
@@ -125,6 +127,13 @@ class _frozen_depgraph_config(object): |
434 |
# All Package instances |
435 |
self._pkg_cache = {} |
436 |
self._highest_license_masked = {} |
437 |
+ # We can't know that an soname dep is unsatisfied if there are |
438 |
+ # any unbuilt ebuilds in the graph, since unbuilt ebuilds have |
439 |
+ # no soname data. Therefore, only enable soname dependency |
440 |
+ # resolution if --usepkgonly is enabled, or for removal actions. |
441 |
+ self.soname_deps_enabled = ( |
442 |
+ ("--usepkgonly" in myopts or "remove" in params) and |
443 |
+ params.get("ignore_soname_deps") != "y") |
444 |
dynamic_deps = myopts.get("--dynamic-deps", "y") != "n" |
445 |
ignore_built_slot_operator_deps = myopts.get( |
446 |
"--ignore-built-slot-operator-deps", "n") == "y" |
447 |
@@ -143,9 +152,13 @@ class _frozen_depgraph_config(object): |
448 |
pkg_cache=self._pkg_cache, |
449 |
pkg_root_config=self.roots[myroot], |
450 |
dynamic_deps=dynamic_deps, |
451 |
- ignore_built_slot_operator_deps=ignore_built_slot_operator_deps) |
452 |
+ ignore_built_slot_operator_deps=ignore_built_slot_operator_deps, |
453 |
+ soname_deps=self.soname_deps_enabled) |
454 |
self.pkgsettings[myroot] = portage.config( |
455 |
clone=self.trees[myroot]["vartree"].settings) |
456 |
+ if self.soname_deps_enabled and "remove" not in params: |
457 |
+ self.trees[myroot]["bintree"] = DummyTree( |
458 |
+ DbapiProvidesIndex(trees[myroot]["bintree"].dbapi)) |
459 |
|
460 |
self._required_set_names = set(["world"]) |
461 |
|
462 |
@@ -428,7 +441,9 @@ class _dynamic_depgraph_config(object): |
463 |
self._traverse_ignored_deps = False |
464 |
self._complete_mode = False |
465 |
self._slot_operator_deps = {} |
466 |
- self._package_tracker = PackageTracker() |
467 |
+ self._installed_sonames = collections.defaultdict(list) |
468 |
+ self._package_tracker = PackageTracker( |
469 |
+ soname_deps=depgraph._frozen_config.soname_deps_enabled) |
470 |
# Track missed updates caused by solved conflicts. |
471 |
self._conflict_missed_update = collections.defaultdict(dict) |
472 |
|
473 |
@@ -536,6 +551,18 @@ class depgraph(object): |
474 |
|
475 |
self.query = UserQuery(myopts).query |
476 |
|
477 |
+ def _index_binpkgs(self): |
478 |
+ for root in self._frozen_config.trees: |
479 |
+ bindb = self._frozen_config.trees[root]["bintree"].dbapi |
480 |
+ if bindb._provides_index: |
481 |
+ # don't repeat this when backtracking |
482 |
+ continue |
483 |
+ root_config = self._frozen_config.roots[root] |
484 |
+ for cpv in self._frozen_config._trees_orig[ |
485 |
+ root]["bintree"].dbapi.cpv_all(): |
486 |
+ bindb._provides_inject( |
487 |
+ self._pkg(cpv, "binary", root_config)) |
488 |
+ |
489 |
def _load_vdb(self): |
490 |
""" |
491 |
Load installed package metadata if appropriate. This used to be called |
492 |
@@ -571,6 +598,7 @@ class depgraph(object): |
493 |
if not dynamic_deps: |
494 |
for pkg in vardb: |
495 |
self._dynamic_config._package_tracker.add_installed_pkg(pkg) |
496 |
+ self._add_installed_sonames(pkg) |
497 |
else: |
498 |
max_jobs = self._frozen_config.myopts.get("--jobs") |
499 |
max_load = self._frozen_config.myopts.get("--load-average") |
500 |
@@ -589,6 +617,7 @@ class depgraph(object): |
501 |
for pkg in fake_vartree.dbapi: |
502 |
self._spinner_update() |
503 |
self._dynamic_config._package_tracker.add_installed_pkg(pkg) |
504 |
+ self._add_installed_sonames(pkg) |
505 |
ebuild_path, repo_path = \ |
506 |
portdb.findname2(pkg.cpv, myrepo=pkg.repo) |
507 |
if ebuild_path is None: |
508 |
@@ -631,6 +660,8 @@ class depgraph(object): |
509 |
""" |
510 |
|
511 |
debug = "--debug" in self._frozen_config.myopts |
512 |
+ installed_sonames = self._dynamic_config._installed_sonames |
513 |
+ package_tracker = self._dynamic_config._package_tracker |
514 |
|
515 |
# Get all atoms that might have caused a forced rebuild. |
516 |
atoms = {} |
517 |
@@ -666,6 +697,32 @@ class depgraph(object): |
518 |
if inst_pkg is reinst_pkg or reinst_pkg is None: |
519 |
continue |
520 |
|
521 |
+ if (inst_pkg is not None and |
522 |
+ inst_pkg.requires is not None): |
523 |
+ for atom in inst_pkg.requires: |
524 |
+ initial_providers = installed_sonames.get( |
525 |
+ (root, atom)) |
526 |
+ if initial_providers is None: |
527 |
+ continue |
528 |
+ final_provider = next( |
529 |
+ package_tracker.match(root, atom), |
530 |
+ None) |
531 |
+ if final_provider: |
532 |
+ continue |
533 |
+ for provider in initial_providers: |
534 |
+ # Find the replacement child. |
535 |
+ child = next((pkg for pkg in |
536 |
+ package_tracker.match( |
537 |
+ root, provider.slot_atom) |
538 |
+ if not pkg.installed), None) |
539 |
+ |
540 |
+ if child is None: |
541 |
+ continue |
542 |
+ |
543 |
+ forced_rebuilds.setdefault( |
544 |
+ root, {}).setdefault( |
545 |
+ child, set()).add(inst_pkg) |
546 |
+ |
547 |
# Generate pseudo-deps for any slot-operator deps of |
548 |
# inst_pkg. Its deps aren't in _slot_operator_deps |
549 |
# because it hasn't been added to the graph, but we |
550 |
@@ -1216,9 +1273,6 @@ class depgraph(object): |
551 |
if is_non_conflict_parent: |
552 |
parent = non_conflict_node |
553 |
|
554 |
- atom_set = InternalPackageSet( |
555 |
- initial_atoms=(atom,), allow_repo=True) |
556 |
- |
557 |
matched = [] |
558 |
for pkg in conflict: |
559 |
if (pkg is highest_pkg and |
560 |
@@ -1230,8 +1284,8 @@ class depgraph(object): |
561 |
# version into the graph (bug #531656). |
562 |
non_matching_forced.add(highest_pkg) |
563 |
|
564 |
- if atom_set.findAtomForPackage(pkg, \ |
565 |
- modified_use=self._pkg_use_enabled(pkg)) and \ |
566 |
+ if atom.match(pkg.with_use( |
567 |
+ self._pkg_use_enabled(pkg))) and \ |
568 |
not (is_arg_parent and pkg.installed): |
569 |
matched.append(pkg) |
570 |
|
571 |
@@ -1448,13 +1502,8 @@ class depgraph(object): |
572 |
for parent_atom in slot_parent_atoms: |
573 |
if parent_atom in parent_atoms: |
574 |
continue |
575 |
- # Use package set for matching since it will match via |
576 |
- # PROVIDE when necessary, while match_from_list does not. |
577 |
parent, atom = parent_atom |
578 |
- atom_set = InternalPackageSet( |
579 |
- initial_atoms=(atom,), allow_repo=True) |
580 |
- if atom_set.findAtomForPackage(pkg, |
581 |
- modified_use=self._pkg_use_enabled(pkg)): |
582 |
+ if atom.match(pkg.with_use(self._pkg_use_enabled(pkg))): |
583 |
parent_atoms.add(parent_atom) |
584 |
else: |
585 |
all_match = False |
586 |
@@ -1533,7 +1582,11 @@ class depgraph(object): |
587 |
if not isinstance(parent, Package): |
588 |
continue |
589 |
|
590 |
- if atom.slot_operator != "=" or not parent.built: |
591 |
+ if not parent.built: |
592 |
+ continue |
593 |
+ |
594 |
+ if not atom.soname and not ( |
595 |
+ atom.package and atom.slot_operator_built): |
596 |
continue |
597 |
|
598 |
if pkg not in conflict_pkgs: |
599 |
@@ -1740,7 +1793,7 @@ class depgraph(object): |
600 |
""" |
601 |
built_slot_operator_parents = set() |
602 |
for parent, atom in self._dynamic_config._parent_atoms.get(existing_pkg, []): |
603 |
- if atom.slot_operator_built: |
604 |
+ if atom.soname or atom.slot_operator_built: |
605 |
built_slot_operator_parents.add(parent) |
606 |
|
607 |
for parent, atom in self._dynamic_config._parent_atoms.get(existing_pkg, []): |
608 |
@@ -1778,6 +1831,9 @@ class depgraph(object): |
609 |
for replacement_parent in self._iter_similar_available(dep.parent, |
610 |
dep.parent.slot_atom, autounmask_level=autounmask_level): |
611 |
|
612 |
+ if replacement_parent is dep.parent: |
613 |
+ continue |
614 |
+ |
615 |
if replacement_parent < dep.parent: |
616 |
if want_downgrade_parent is None: |
617 |
want_downgrade_parent = self._downgrade_probe( |
618 |
@@ -1796,35 +1852,51 @@ class depgraph(object): |
619 |
except InvalidDependString: |
620 |
continue |
621 |
|
622 |
+ if replacement_parent.requires is not None: |
623 |
+ atoms = list(atoms) |
624 |
+ atoms.extend(replacement_parent.requires) |
625 |
+ |
626 |
# List of list of child,atom pairs for each atom. |
627 |
replacement_candidates = [] |
628 |
# Set of all packages all atoms can agree on. |
629 |
all_candidate_pkgs = None |
630 |
+ atom_not_selected = False |
631 |
|
632 |
for atom in atoms: |
633 |
- if atom.blocker or \ |
634 |
- atom.cp != dep.atom.cp: |
635 |
- continue |
636 |
|
637 |
- # Discard USE deps, we're only searching for an approximate |
638 |
- # pattern, and dealing with USE states is too complex for |
639 |
- # this purpose. |
640 |
- unevaluated_atom = atom.unevaluated_atom |
641 |
- atom = atom.without_use |
642 |
+ if not atom.package: |
643 |
+ unevaluated_atom = None |
644 |
+ if atom.match(dep.child): |
645 |
+ # We are searching for a replacement_parent |
646 |
+ # atom that will pull in a different child, |
647 |
+ # so continue checking the rest of the atoms. |
648 |
+ continue |
649 |
+ else: |
650 |
|
651 |
- if replacement_parent.built and \ |
652 |
- portage.dep._match_slot(atom, dep.child): |
653 |
- # Our selected replacement_parent appears to be built |
654 |
- # for the existing child selection. So, discard this |
655 |
- # parent and search for another. |
656 |
- break |
657 |
+ if atom.blocker or \ |
658 |
+ atom.cp != dep.child.cp: |
659 |
+ continue |
660 |
+ |
661 |
+ # Discard USE deps, we're only searching for an |
662 |
+ # approximate pattern, and dealing with USE states |
663 |
+ # is too complex for this purpose. |
664 |
+ unevaluated_atom = atom.unevaluated_atom |
665 |
+ atom = atom.without_use |
666 |
+ |
667 |
+ if replacement_parent.built and \ |
668 |
+ portage.dep._match_slot(atom, dep.child): |
669 |
+ # We are searching for a replacement_parent |
670 |
+ # atom that will pull in a different child, |
671 |
+ # so continue checking the rest of the atoms. |
672 |
+ continue |
673 |
|
674 |
candidate_pkg_atoms = [] |
675 |
candidate_pkgs = [] |
676 |
for pkg in self._iter_similar_available( |
677 |
dep.child, atom): |
678 |
- if pkg.slot == dep.child.slot and \ |
679 |
- pkg.sub_slot == dep.child.sub_slot: |
680 |
+ if (dep.atom.package and |
681 |
+ pkg.slot == dep.child.slot and |
682 |
+ pkg.sub_slot == dep.child.sub_slot): |
683 |
# If slot/sub-slot is identical, then there's |
684 |
# no point in updating. |
685 |
continue |
686 |
@@ -1863,7 +1935,8 @@ class depgraph(object): |
687 |
# slot conflict). |
688 |
insignificant = True |
689 |
|
690 |
- if not insignificant: |
691 |
+ if (not insignificant and |
692 |
+ unevaluated_atom is not None): |
693 |
# Evaluate USE conditionals and || deps, in order |
694 |
# to see if this atom is really desirable, since |
695 |
# otherwise we may trigger an undesirable rebuild |
696 |
@@ -1872,14 +1945,19 @@ class depgraph(object): |
697 |
selected_atoms = self._select_atoms_probe( |
698 |
dep.child.root, replacement_parent) |
699 |
if unevaluated_atom not in selected_atoms: |
700 |
- continue |
701 |
+ atom_not_selected = True |
702 |
+ break |
703 |
|
704 |
if not insignificant and \ |
705 |
check_reverse_dependencies(dep.child, pkg, |
706 |
replacement_parent=replacement_parent): |
707 |
|
708 |
- candidate_pkg_atoms.append((pkg, unevaluated_atom)) |
709 |
+ candidate_pkg_atoms.append( |
710 |
+ (pkg, unevaluated_atom or atom)) |
711 |
candidate_pkgs.append(pkg) |
712 |
+ |
713 |
+ if atom_not_selected: |
714 |
+ continue |
715 |
replacement_candidates.append(candidate_pkg_atoms) |
716 |
if all_candidate_pkgs is None: |
717 |
all_candidate_pkgs = set(candidate_pkgs) |
718 |
@@ -2183,10 +2261,8 @@ class depgraph(object): |
719 |
for dep in slot_info: |
720 |
|
721 |
atom = dep.atom |
722 |
- if atom.slot_operator is None: |
723 |
- continue |
724 |
|
725 |
- if not atom.slot_operator_built: |
726 |
+ if not (atom.soname or atom.slot_operator_built): |
727 |
new_child_slot = self._slot_change_probe(dep) |
728 |
if new_child_slot is not None: |
729 |
self._slot_change_backtrack(dep, new_child_slot) |
730 |
@@ -2471,7 +2547,7 @@ class depgraph(object): |
731 |
(dep.parent, |
732 |
self._dynamic_config._runtime_pkg_mask[ |
733 |
dep.parent]), noiselevel=-1) |
734 |
- elif dep.atom.slot_operator_built and \ |
735 |
+ elif dep.atom.package and dep.atom.slot_operator_built and \ |
736 |
self._slot_operator_unsatisfied_probe(dep): |
737 |
self._slot_operator_unsatisfied_backtrack(dep) |
738 |
return 1 |
739 |
@@ -2483,8 +2559,9 @@ class depgraph(object): |
740 |
# return None, and eventually we come through here |
741 |
# and skip the "missing dependency" backtracking path. |
742 |
dep_pkg, existing_node = \ |
743 |
- self._select_package(dep.root, dep.atom.without_use, |
744 |
- onlydeps=dep.onlydeps) |
745 |
+ self._select_package(dep.root, |
746 |
+ dep.atom.without_use if dep.atom.package |
747 |
+ else dep.atom, onlydeps=dep.onlydeps) |
748 |
if dep_pkg is None: |
749 |
self._dynamic_config._backtrack_infos["missing dependency"] = dep |
750 |
self._dynamic_config._need_restart = True |
751 |
@@ -2520,11 +2597,8 @@ class depgraph(object): |
752 |
matches = pkg.cpv == existing_node.cpv |
753 |
if pkg != existing_node and \ |
754 |
atom is not None: |
755 |
- # Use package set for matching since it will match via |
756 |
- # PROVIDE when necessary, while match_from_list does not. |
757 |
- matches = bool(InternalPackageSet(initial_atoms=(atom,), |
758 |
- allow_repo=True).findAtomForPackage(existing_node, |
759 |
- modified_use=self._pkg_use_enabled(existing_node))) |
760 |
+ matches = atom.match(existing_node.with_use( |
761 |
+ self._pkg_use_enabled(existing_node))) |
762 |
|
763 |
return (existing_node, matches) |
764 |
|
765 |
@@ -2563,8 +2637,8 @@ class depgraph(object): |
766 |
# Display the specific atom from SetArg or |
767 |
# Package types. |
768 |
uneval = "" |
769 |
- if dep.atom and dep.atom.unevaluated_atom and \ |
770 |
- dep.atom is not dep.atom.unevaluated_atom: |
771 |
+ if (dep.atom and dep.atom.package and |
772 |
+ dep.atom is not dep.atom.unevaluated_atom): |
773 |
uneval = " (%s)" % (dep.atom.unevaluated_atom,) |
774 |
writemsg_level( |
775 |
"%s%s%s required by %s\n" % |
776 |
@@ -2728,8 +2802,9 @@ class depgraph(object): |
777 |
not (deep is not True and depth > deep)) |
778 |
|
779 |
dep.child = pkg |
780 |
- if (not pkg.onlydeps and |
781 |
- dep.atom and dep.atom.slot_operator is not None): |
782 |
+ if not pkg.onlydeps and dep.atom and ( |
783 |
+ dep.atom.soname or |
784 |
+ dep.atom.slot_operator == "="): |
785 |
self._add_slot_operator_dep(dep) |
786 |
|
787 |
recurse = deep is True or depth + 1 <= deep |
788 |
@@ -2745,6 +2820,32 @@ class depgraph(object): |
789 |
dep_stack.append(pkg) |
790 |
return 1 |
791 |
|
792 |
+ def _add_installed_sonames(self, pkg): |
793 |
+ if (self._frozen_config.soname_deps_enabled and |
794 |
+ pkg.provides is not None): |
795 |
+ for atom in pkg.provides: |
796 |
+ self._dynamic_config._installed_sonames[ |
797 |
+ (pkg.root, atom)].append(pkg) |
798 |
+ |
799 |
+ def _add_pkg_soname_deps(self, pkg, allow_unsatisfied=False): |
800 |
+ if (self._frozen_config.soname_deps_enabled and |
801 |
+ pkg.requires is not None): |
802 |
+ if isinstance(pkg.depth, int): |
803 |
+ depth = pkg.depth + 1 |
804 |
+ else: |
805 |
+ depth = pkg.depth |
806 |
+ soname_provided = self._frozen_config.roots[ |
807 |
+ pkg.root].settings.soname_provided |
808 |
+ for atom in pkg.requires: |
809 |
+ if atom in soname_provided: |
810 |
+ continue |
811 |
+ dep = Dependency(atom=atom, blocker=False, depth=depth, |
812 |
+ parent=pkg, priority=self._priority(runtime=True), |
813 |
+ root=pkg.root) |
814 |
+ if not self._add_dep(dep, |
815 |
+ allow_unsatisfied=allow_unsatisfied): |
816 |
+ return False |
817 |
+ return True |
818 |
|
819 |
def _remove_pkg(self, pkg): |
820 |
""" |
821 |
@@ -2832,6 +2933,10 @@ class depgraph(object): |
822 |
|
823 |
def _add_pkg_deps(self, pkg, allow_unsatisfied=False): |
824 |
|
825 |
+ if not self._add_pkg_soname_deps(pkg, |
826 |
+ allow_unsatisfied=allow_unsatisfied): |
827 |
+ return False |
828 |
+ |
829 |
myroot = pkg.root |
830 |
metadata = pkg._metadata |
831 |
removal_action = "remove" in self._dynamic_config.myparams |
832 |
@@ -3482,6 +3587,9 @@ class depgraph(object): |
833 |
self._dynamic_config._initial_arg_list and call self._resolve to create the |
834 |
appropriate depgraph and return a favorite list.""" |
835 |
self._load_vdb() |
836 |
+ if (self._frozen_config.soname_deps_enabled and |
837 |
+ "remove" not in self._dynamic_config.myparams): |
838 |
+ self._index_binpkgs() |
839 |
debug = "--debug" in self._frozen_config.myopts |
840 |
root_config = self._frozen_config.roots[self._frozen_config.target_root] |
841 |
sets = root_config.sets |
842 |
@@ -4451,7 +4559,7 @@ class depgraph(object): |
843 |
# (aka unsatisfied_dependency is not None) we |
844 |
# need that the start_node doesn't match the atom. |
845 |
if not unsatisfied_dependency or \ |
846 |
- not InternalPackageSet(initial_atoms=(patom,)).findAtomForPackage(start_node): |
847 |
+ not patom.match(start_node): |
848 |
start_node_parent_atoms.setdefault(patom, []).append(ppkg) |
849 |
|
850 |
if start_node_parent_atoms: |
851 |
@@ -4459,7 +4567,16 @@ class depgraph(object): |
852 |
# If not, then this package got pulled in by an Arg and |
853 |
# will be correctly handled by the code that handles later |
854 |
# packages in the dep chain. |
855 |
- best_match = best_match_to_list(node.cpv, start_node_parent_atoms) |
856 |
+ if (any(not x.package for x in start_node_parent_atoms) and |
857 |
+ any(x.package for x in start_node_parent_atoms)): |
858 |
+ for x in list(start_node_parent_atoms): |
859 |
+ if not x.package: |
860 |
+ del start_node_parent_atoms[x] |
861 |
+ if next(iter(start_node_parent_atoms)).package: |
862 |
+ best_match = best_match_to_list(node.cpv, |
863 |
+ start_node_parent_atoms) |
864 |
+ else: |
865 |
+ best_match = next(iter(start_node_parent_atoms)) |
866 |
|
867 |
child = node |
868 |
for ppkg in start_node_parent_atoms[best_match]: |
869 |
@@ -4486,10 +4603,11 @@ class depgraph(object): |
870 |
for ppkg, patom in all_parents[child]: |
871 |
if ppkg == node: |
872 |
if child is start_node and unsatisfied_dependency and \ |
873 |
- InternalPackageSet(initial_atoms=(patom,)).findAtomForPackage(child): |
874 |
+ patom.match(child): |
875 |
# This atom is satisfied by child, there must be another atom. |
876 |
continue |
877 |
- atom = patom.unevaluated_atom |
878 |
+ atom = (patom.unevaluated_atom |
879 |
+ if patom.package else patom) |
880 |
break |
881 |
|
882 |
dep_strings = set() |
883 |
@@ -4558,8 +4676,7 @@ class depgraph(object): |
884 |
# flag changes. |
885 |
for ppkg, atom in all_parents[start_node]: |
886 |
if parent is ppkg: |
887 |
- atom_set = InternalPackageSet(initial_atoms=(atom,)) |
888 |
- if not atom_set.findAtomForPackage(start_node): |
889 |
+ if not atom.match(start_node): |
890 |
parent_unsatisfied = parent |
891 |
break |
892 |
else: |
893 |
@@ -4602,11 +4719,13 @@ class depgraph(object): |
894 |
""" |
895 |
backtrack_mask = False |
896 |
autounmask_broke_use_dep = False |
897 |
- atom_set = InternalPackageSet(initial_atoms=(atom.without_use,), |
898 |
- allow_repo=True) |
899 |
- atom_set_with_use = InternalPackageSet(initial_atoms=(atom,), |
900 |
- allow_repo=True) |
901 |
- xinfo = '"%s"' % atom.unevaluated_atom |
902 |
+ if atom.package: |
903 |
+ xinfo = '"%s"' % atom.unevaluated_atom |
904 |
+ atom_without_use = atom.without_use |
905 |
+ else: |
906 |
+ xinfo = '"%s"' % atom |
907 |
+ atom_without_use = None |
908 |
+ |
909 |
if arg: |
910 |
xinfo='"%s"' % arg |
911 |
if isinstance(myparent, AtomArg): |
912 |
@@ -4630,12 +4749,18 @@ class depgraph(object): |
913 |
for db, pkg_type, built, installed, db_keys in dbs: |
914 |
if installed: |
915 |
continue |
916 |
- if hasattr(db, "xmatch"): |
917 |
+ if atom.soname: |
918 |
+ if not isinstance(db, DbapiProvidesIndex): |
919 |
+ continue |
920 |
+ cpv_list = db.match(atom) |
921 |
+ elif hasattr(db, "xmatch"): |
922 |
cpv_list = db.xmatch("match-all-cpv-only", atom.without_use) |
923 |
else: |
924 |
cpv_list = db.match(atom.without_use) |
925 |
|
926 |
- if atom.repo is None and hasattr(db, "getRepositories"): |
927 |
+ if atom.soname: |
928 |
+ repo_list = [None] |
929 |
+ elif atom.repo is None and hasattr(db, "getRepositories"): |
930 |
repo_list = db.getRepositories() |
931 |
else: |
932 |
repo_list = [atom.repo] |
933 |
@@ -4666,8 +4791,10 @@ class depgraph(object): |
934 |
masked_packages.append( |
935 |
(root_config, pkgsettings, cpv, repo, metadata, mreasons)) |
936 |
continue |
937 |
- if not atom_set.findAtomForPackage(pkg, |
938 |
- modified_use=self._pkg_use_enabled(pkg)): |
939 |
+ if atom.soname and not atom.match(pkg): |
940 |
+ continue |
941 |
+ if (atom_without_use is not None and |
942 |
+ not atom_without_use.match(pkg)): |
943 |
continue |
944 |
if pkg in self._dynamic_config._runtime_pkg_mask: |
945 |
backtrack_reasons = \ |
946 |
@@ -4680,12 +4807,12 @@ class depgraph(object): |
947 |
mreasons = ["exclude option"] |
948 |
if mreasons: |
949 |
masked_pkg_instances.add(pkg) |
950 |
- if atom.unevaluated_atom.use: |
951 |
+ if atom.package and atom.unevaluated_atom.use: |
952 |
try: |
953 |
if not pkg.iuse.is_valid_flag(atom.unevaluated_atom.use.required) \ |
954 |
or atom.violated_conditionals(self._pkg_use_enabled(pkg), pkg.iuse.is_valid_flag).use: |
955 |
missing_use.append(pkg) |
956 |
- if atom_set_with_use.findAtomForPackage(pkg): |
957 |
+ if atom.match(pkg): |
958 |
autounmask_broke_use_dep = True |
959 |
if not mreasons: |
960 |
continue |
961 |
@@ -4952,7 +5079,7 @@ class depgraph(object): |
962 |
mask_docs = True |
963 |
else: |
964 |
cp_exists = False |
965 |
- if not atom.cp.startswith("null/"): |
966 |
+ if atom.package and not atom.cp.startswith("null/"): |
967 |
for pkg in self._iter_match_pkgs_any( |
968 |
root_config, Atom(atom.cp)): |
969 |
cp_exists = True |
970 |
@@ -5011,7 +5138,27 @@ class depgraph(object): |
971 |
pkg_type, atom, onlydeps=onlydeps): |
972 |
yield pkg |
973 |
|
974 |
- def _iter_match_pkgs(self, root_config, pkg_type, atom, onlydeps=False): |
975 |
+ def _iter_match_pkgs(self, root_config, pkg_type, atom, |
976 |
+ onlydeps=False): |
977 |
+ if atom.package: |
978 |
+ return self._iter_match_pkgs_atom(root_config, pkg_type, |
979 |
+ atom, onlydeps=onlydeps) |
980 |
+ else: |
981 |
+ return self._iter_match_pkgs_soname(root_config, pkg_type, |
982 |
+ atom, onlydeps=onlydeps) |
983 |
+ |
984 |
+ def _iter_match_pkgs_soname(self, root_config, pkg_type, atom, |
985 |
+ onlydeps=False): |
986 |
+ db = root_config.trees[self.pkg_tree_map[pkg_type]].dbapi |
987 |
+ installed = pkg_type == 'installed' |
988 |
+ |
989 |
+ if isinstance(db, DbapiProvidesIndex): |
990 |
+ for cpv in db.match(atom): |
991 |
+ yield self._pkg(cpv, pkg_type, root_config, |
992 |
+ installed=installed, onlydeps=onlydeps) |
993 |
+ |
994 |
+ def _iter_match_pkgs_atom(self, root_config, pkg_type, atom, |
995 |
+ onlydeps=False): |
996 |
""" |
997 |
Iterate over Package instances of pkg_type matching the given atom. |
998 |
This does not check visibility and it also does not match USE for |
999 |
@@ -5114,14 +5261,21 @@ class depgraph(object): |
1000 |
return |
1001 |
|
1002 |
def _select_pkg_highest_available(self, root, atom, onlydeps=False, parent=None): |
1003 |
- cache_key = (root, atom, atom.unevaluated_atom, onlydeps, self._dynamic_config._autounmask) |
1004 |
+ if atom.package: |
1005 |
+ cache_key = (root, atom, atom.unevaluated_atom, onlydeps, |
1006 |
+ self._dynamic_config._autounmask) |
1007 |
+ self._dynamic_config._highest_pkg_cache_cp_map.\ |
1008 |
+ setdefault((root, atom.cp), []).append(cache_key) |
1009 |
+ else: |
1010 |
+ cache_key = (root, atom, onlydeps, |
1011 |
+ self._dynamic_config._autounmask) |
1012 |
+ self._dynamic_config._highest_pkg_cache_cp_map.\ |
1013 |
+ setdefault((root, atom), []).append(cache_key) |
1014 |
ret = self._dynamic_config._highest_pkg_cache.get(cache_key) |
1015 |
if ret is not None: |
1016 |
return ret |
1017 |
ret = self._select_pkg_highest_available_imp(root, atom, onlydeps=onlydeps, parent=parent) |
1018 |
self._dynamic_config._highest_pkg_cache[cache_key] = ret |
1019 |
- self._dynamic_config._highest_pkg_cache_cp_map. \ |
1020 |
- setdefault(atom.cp, []).append(cache_key) |
1021 |
pkg, existing = ret |
1022 |
if pkg is not None: |
1023 |
if self._pkg_visibility_check(pkg) and \ |
1024 |
@@ -5136,11 +5290,15 @@ class depgraph(object): |
1025 |
return False |
1026 |
|
1027 |
def _prune_highest_pkg_cache(self, pkg): |
1028 |
+ cache = self._dynamic_config._highest_pkg_cache |
1029 |
+ key_map = self._dynamic_config._highest_pkg_cache_cp_map |
1030 |
for cp in pkg.provided_cps: |
1031 |
- for cache_key in self._dynamic_config. \ |
1032 |
- _highest_pkg_cache_cp_map.pop(cp, []): |
1033 |
- self._dynamic_config._highest_pkg_cache.pop( |
1034 |
- cache_key, None) |
1035 |
+ for cache_key in key_map.pop((pkg.root, cp), []): |
1036 |
+ cache.pop(cache_key, None) |
1037 |
+ if pkg.provides is not None: |
1038 |
+ for atom in pkg.provides: |
1039 |
+ for cache_key in key_map.pop((pkg.root, atom), []): |
1040 |
+ cache.pop(cache_key, None) |
1041 |
|
1042 |
def _want_installed_pkg(self, pkg): |
1043 |
""" |
1044 |
@@ -5540,12 +5698,13 @@ class depgraph(object): |
1045 |
# List of acceptable packages, ordered by type preference. |
1046 |
matched_packages = [] |
1047 |
highest_version = None |
1048 |
- if not isinstance(atom, portage.dep.Atom): |
1049 |
- atom = portage.dep.Atom(atom) |
1050 |
- atom_cp = atom.cp |
1051 |
- have_new_virt = atom_cp.startswith("virtual/") and \ |
1052 |
- self._have_new_virt(root, atom_cp) |
1053 |
- atom_set = InternalPackageSet(initial_atoms=(atom,), allow_repo=True) |
1054 |
+ atom_cp = None |
1055 |
+ have_new_virt = None |
1056 |
+ if atom.package: |
1057 |
+ atom_cp = atom.cp |
1058 |
+ have_new_virt = (atom_cp.startswith("virtual/") and |
1059 |
+ self._have_new_virt(root, atom_cp)) |
1060 |
+ |
1061 |
existing_node = None |
1062 |
myeb = None |
1063 |
rebuilt_binaries = 'rebuilt_binaries' in self._dynamic_config.myparams |
1064 |
@@ -5590,9 +5749,10 @@ class depgraph(object): |
1065 |
# Ignore USE deps for the initial match since we want to |
1066 |
# ensure that updates aren't missed solely due to the user's |
1067 |
# USE configuration. |
1068 |
- for pkg in self._iter_match_pkgs(root_config, pkg_type, atom.without_use, |
1069 |
+ for pkg in self._iter_match_pkgs(root_config, pkg_type, |
1070 |
+ atom.without_use if atom.package else atom, |
1071 |
onlydeps=onlydeps): |
1072 |
- if pkg.cp != atom_cp and have_new_virt: |
1073 |
+ if have_new_virt is True and pkg.cp != atom_cp: |
1074 |
# pull in a new-style virtual instead |
1075 |
continue |
1076 |
if pkg in self._dynamic_config._runtime_pkg_mask: |
1077 |
@@ -5711,14 +5871,14 @@ class depgraph(object): |
1078 |
if not installed and myarg: |
1079 |
found_available_arg = True |
1080 |
|
1081 |
- if atom.unevaluated_atom.use: |
1082 |
+ if atom.package and atom.unevaluated_atom.use: |
1083 |
#Make sure we don't miss a 'missing IUSE'. |
1084 |
if pkg.iuse.get_missing_iuse(atom.unevaluated_atom.use.required): |
1085 |
# Don't add this to packages_with_invalid_use_config |
1086 |
# since IUSE cannot be adjusted by the user. |
1087 |
continue |
1088 |
|
1089 |
- if atom.use: |
1090 |
+ if atom.package and atom.use is not None: |
1091 |
|
1092 |
if autounmask_level and autounmask_level.allow_use_changes and not pkg.built: |
1093 |
target_use = {} |
1094 |
@@ -5778,7 +5938,7 @@ class depgraph(object): |
1095 |
packages_with_invalid_use_config.append(pkg) |
1096 |
continue |
1097 |
|
1098 |
- if pkg.cp == atom_cp: |
1099 |
+ if atom_cp is None or pkg.cp == atom_cp: |
1100 |
if highest_version is None: |
1101 |
highest_version = pkg |
1102 |
elif pkg > highest_version: |
1103 |
@@ -5797,9 +5957,11 @@ class depgraph(object): |
1104 |
|
1105 |
# Use PackageSet.findAtomForPackage() |
1106 |
# for PROVIDE support. |
1107 |
- if atom_set.findAtomForPackage(e_pkg, modified_use=self._pkg_use_enabled(e_pkg)): |
1108 |
+ if atom.match(e_pkg.with_use( |
1109 |
+ self._pkg_use_enabled(e_pkg))): |
1110 |
if highest_version and \ |
1111 |
- e_pkg.cp == atom_cp and \ |
1112 |
+ (atom_cp is None or |
1113 |
+ e_pkg.cp == atom_cp) and \ |
1114 |
e_pkg < highest_version and \ |
1115 |
e_pkg.slot_atom != highest_version.slot_atom: |
1116 |
# There is a higher version available in a |
1117 |
@@ -5865,7 +6027,8 @@ class depgraph(object): |
1118 |
# Compare current config to installed package |
1119 |
# and do not reinstall if possible. |
1120 |
if not installed and not useoldpkg and cpv in vardb.match(atom): |
1121 |
- inst_pkg = vardb.match_pkgs('=' + pkg.cpv)[0] |
1122 |
+ inst_pkg = vardb.match_pkgs( |
1123 |
+ Atom('=' + pkg.cpv))[0] |
1124 |
if "--newrepo" in self._frozen_config.myopts and pkg.repo != inst_pkg.repo: |
1125 |
reinstall = True |
1126 |
elif reinstall_use: |
1127 |
@@ -5906,8 +6069,9 @@ class depgraph(object): |
1128 |
|
1129 |
# Filter out any old-style virtual matches if they are |
1130 |
# mixed with new-style virtual matches. |
1131 |
- cp = atom.cp |
1132 |
+ cp = atom_cp |
1133 |
if len(matched_packages) > 1 and \ |
1134 |
+ cp is not None and \ |
1135 |
"virtual" == portage.catsplit(cp)[0]: |
1136 |
for pkg in matched_packages: |
1137 |
if pkg.cp != cp: |
1138 |
@@ -6893,7 +7057,7 @@ class depgraph(object): |
1139 |
runtime_deps = InternalPackageSet( |
1140 |
initial_atoms=[PORTAGE_PACKAGE_ATOM]) |
1141 |
running_portage = self._frozen_config.trees[running_root]["vartree"].dbapi.match_pkgs( |
1142 |
- PORTAGE_PACKAGE_ATOM) |
1143 |
+ Atom(PORTAGE_PACKAGE_ATOM)) |
1144 |
replacement_portage = list(self._dynamic_config._package_tracker.match( |
1145 |
running_root, Atom(PORTAGE_PACKAGE_ATOM))) |
1146 |
|
1147 |
diff --git a/pym/_emerge/main.py b/pym/_emerge/main.py |
1148 |
index 5d5e936..b56277c 100644 |
1149 |
--- a/pym/_emerge/main.py |
1150 |
+++ b/pym/_emerge/main.py |
1151 |
@@ -455,6 +455,16 @@ def parse_opts(tmpcmdline, silent=False): |
1152 |
"choices": y_or_n |
1153 |
}, |
1154 |
|
1155 |
+ "--ignore-soname-deps": { |
1156 |
+ "help": "Ignore the soname dependencies of binary and " |
1157 |
+ "installed packages. This option is enabled by " |
1158 |
+ "default, since soname dependencies are relatively " |
1159 |
+ "new, and the required metadata is not guaranteed to " |
1160 |
+ "exist for binary and installed packages built with " |
1161 |
+ "older versions of portage.", |
1162 |
+ "choices": y_or_n |
1163 |
+ }, |
1164 |
+ |
1165 |
"--jobs": { |
1166 |
|
1167 |
"shortopt" : "-j", |
1168 |
diff --git a/pym/_emerge/resolver/DbapiProvidesIndex.py b/pym/_emerge/resolver/DbapiProvidesIndex.py |
1169 |
new file mode 100644 |
1170 |
index 0000000..59ae719 |
1171 |
--- /dev/null |
1172 |
+++ b/pym/_emerge/resolver/DbapiProvidesIndex.py |
1173 |
@@ -0,0 +1,101 @@ |
1174 |
+# Copyright 2015 Gentoo Foundation |
1175 |
+# Distributed under the terms of the GNU General Public License v2 |
1176 |
+ |
1177 |
+import bisect |
1178 |
+import collections |
1179 |
+import sys |
1180 |
+ |
1181 |
+class DbapiProvidesIndex(object): |
1182 |
+ """ |
1183 |
+ The DbapiProvidesIndex class is used to wrap existing dbapi |
1184 |
+ interfaces, index packages by the sonames that they provide, and |
1185 |
+ implement the dbapi.match method for SonameAtom instances. Since |
1186 |
+ this class acts as a wrapper, it can be used conditionally, so that |
1187 |
+ soname indexing overhead is avoided when soname dependency |
1188 |
+ resolution is disabled. |
1189 |
+ |
1190 |
+ Since it's possible for soname atom match results to consist of |
1191 |
+ packages with multiple categories or names, it is essential that |
1192 |
+ Package.__lt__ behave meaningfully when Package.cp is dissimilar, |
1193 |
+ so that match results will be correctly ordered by version for each |
1194 |
+ value of Package.cp. |
1195 |
+ """ |
1196 |
+ |
1197 |
+ _copy_attrs = ('aux_get', 'aux_update', 'categories', 'cpv_all', |
1198 |
+ 'cpv_exists', 'cp_all', 'cp_list', 'getfetchsizes', |
1199 |
+ 'settings', '_aux_cache_keys', '_clear_cache', |
1200 |
+ '_cpv_sort_ascending', '_pkg_str', '_pkg_str_aux_keys') |
1201 |
+ |
1202 |
+ def __init__(self, db): |
1203 |
+ self._db = db |
1204 |
+ for k in self._copy_attrs: |
1205 |
+ try: |
1206 |
+ setattr(self, k, getattr(db, k)) |
1207 |
+ except AttributeError: |
1208 |
+ pass |
1209 |
+ self._provides_index = collections.defaultdict(list) |
1210 |
+ |
1211 |
+ def match(self, atom, use_cache=DeprecationWarning): |
1212 |
+ if atom.soname: |
1213 |
+ result = self._match_soname(atom) |
1214 |
+ else: |
1215 |
+ result = self._db.match(atom) |
1216 |
+ return result |
1217 |
+ |
1218 |
+ def _match_soname(self, atom): |
1219 |
+ result = self._provides_index.get(atom) |
1220 |
+ if result is None: |
1221 |
+ result = [] |
1222 |
+ else: |
1223 |
+ result = [pkg.cpv for pkg in result] |
1224 |
+ return result |
1225 |
+ |
1226 |
+ def _provides_inject(self, pkg): |
1227 |
+ index = self._provides_index |
1228 |
+ for atom in pkg.provides: |
1229 |
+ # Use bisect.insort for ordered match results. |
1230 |
+ bisect.insort(index[atom], pkg) |
1231 |
+ |
1232 |
+class PackageDbapiProvidesIndex(DbapiProvidesIndex): |
1233 |
+ """ |
1234 |
+ This class extends DbapiProvidesIndex in order to make it suitable |
1235 |
+ for wrapping a PackageVirtualDbapi instance. |
1236 |
+ """ |
1237 |
+ |
1238 |
+ _copy_attrs = DbapiProvidesIndex._copy_attrs + ( |
1239 |
+ "clear", "get", "_cpv_map") |
1240 |
+ |
1241 |
+ def clear(self): |
1242 |
+ self._db.clear() |
1243 |
+ self._provides_index.clear() |
1244 |
+ |
1245 |
+ def __bool__(self): |
1246 |
+ return bool(self._db) |
1247 |
+ |
1248 |
+ if sys.hexversion < 0x3000000: |
1249 |
+ __nonzero__ = __bool__ |
1250 |
+ |
1251 |
+ def __iter__(self): |
1252 |
+ return iter(self._db) |
1253 |
+ |
1254 |
+ def __contains__(self, item): |
1255 |
+ return item in self._db |
1256 |
+ |
1257 |
+ def match_pkgs(self, atom): |
1258 |
+ return [self._db._cpv_map[cpv] for cpv in self.match(atom)] |
1259 |
+ |
1260 |
+ def cpv_inject(self, pkg): |
1261 |
+ self._db.cpv_inject(pkg) |
1262 |
+ self._provides_inject(pkg) |
1263 |
+ |
1264 |
+ def cpv_remove(self, pkg): |
1265 |
+ self._db.cpv_remove(pkg) |
1266 |
+ index = self._provides_index |
1267 |
+ for atom in pkg.provides: |
1268 |
+ items = index[atom] |
1269 |
+ try: |
1270 |
+ items.remove(pkg) |
1271 |
+ except ValueError: |
1272 |
+ pass |
1273 |
+ if not items: |
1274 |
+ del index[atom] |
1275 |
diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py |
1276 |
index 14d1b28..7df0302 100644 |
1277 |
--- a/pym/_emerge/resolver/output.py |
1278 |
+++ b/pym/_emerge/resolver/output.py |
1279 |
@@ -15,7 +15,7 @@ import sys |
1280 |
import portage |
1281 |
from portage import os |
1282 |
from portage.dbapi.dep_expand import dep_expand |
1283 |
-from portage.dep import cpvequal, _repo_separator, _slot_separator |
1284 |
+from portage.dep import Atom, cpvequal, _repo_separator, _slot_separator |
1285 |
from portage.eapi import _get_eapi_attrs |
1286 |
from portage.exception import InvalidDependString, SignatureException |
1287 |
from portage.localization import localized_size |
1288 |
@@ -659,7 +659,8 @@ class Display(object): |
1289 |
|
1290 |
if self.vardb.cpv_exists(pkg.cpv): |
1291 |
# Do a cpv match first, in case the SLOT has changed. |
1292 |
- pkg_info.previous_pkg = self.vardb.match_pkgs('=' + pkg.cpv)[0] |
1293 |
+ pkg_info.previous_pkg = self.vardb.match_pkgs( |
1294 |
+ Atom('=' + pkg.cpv))[0] |
1295 |
else: |
1296 |
slot_matches = self.vardb.match_pkgs(pkg.slot_atom) |
1297 |
if slot_matches: |
1298 |
@@ -742,7 +743,7 @@ class Display(object): |
1299 |
""" |
1300 |
myoldbest = [] |
1301 |
myinslotlist = None |
1302 |
- installed_versions = self.vardb.match_pkgs(pkg.cp) |
1303 |
+ installed_versions = self.vardb.match_pkgs(Atom(pkg.cp)) |
1304 |
if self.vardb.cpv_exists(pkg.cpv): |
1305 |
pkg_info.attr_display.replace = True |
1306 |
installed_version = pkg_info.previous_pkg |
1307 |
@@ -931,6 +932,9 @@ def format_unmatched_atom(pkg, atom, pkg_use_enabled): |
1308 |
# 4. repository |
1309 |
# 5. USE |
1310 |
|
1311 |
+ if atom.soname: |
1312 |
+ return "%s" % (atom,), "" |
1313 |
+ |
1314 |
highlight = set() |
1315 |
|
1316 |
def perform_coloring(): |
1317 |
diff --git a/pym/_emerge/resolver/package_tracker.py b/pym/_emerge/resolver/package_tracker.py |
1318 |
index 406d5ce..398d4cf 100644 |
1319 |
--- a/pym/_emerge/resolver/package_tracker.py |
1320 |
+++ b/pym/_emerge/resolver/package_tracker.py |
1321 |
@@ -3,6 +3,7 @@ |
1322 |
|
1323 |
from __future__ import print_function |
1324 |
|
1325 |
+import bisect |
1326 |
import collections |
1327 |
|
1328 |
import portage |
1329 |
@@ -43,7 +44,11 @@ class PackageTracker(object): |
1330 |
3) Packages that block each other. |
1331 |
""" |
1332 |
|
1333 |
- def __init__(self): |
1334 |
+ def __init__(self, soname_deps=False): |
1335 |
+ """ |
1336 |
+ @param soname_deps: enable soname match support |
1337 |
+ @type soname_deps: bool |
1338 |
+ """ |
1339 |
# Mapping from package keys to set of packages. |
1340 |
self._cp_pkg_map = collections.defaultdict(list) |
1341 |
self._cp_vdb_pkg_map = collections.defaultdict(list) |
1342 |
@@ -61,6 +66,10 @@ class PackageTracker(object): |
1343 |
self._replaced_by = collections.defaultdict(list) |
1344 |
|
1345 |
self._match_cache = collections.defaultdict(dict) |
1346 |
+ if soname_deps: |
1347 |
+ self._provides_index = collections.defaultdict(list) |
1348 |
+ else: |
1349 |
+ self._provides_index = None |
1350 |
|
1351 |
def add_pkg(self, pkg): |
1352 |
""" |
1353 |
@@ -85,8 +94,19 @@ class PackageTracker(object): |
1354 |
self._replacing[pkg].append(installed) |
1355 |
self._replaced_by[installed].append(pkg) |
1356 |
|
1357 |
+ self._add_provides(pkg) |
1358 |
+ |
1359 |
self._match_cache.pop(cp_key, None) |
1360 |
|
1361 |
+ def _add_provides(self, pkg): |
1362 |
+ if (self._provides_index is not None and |
1363 |
+ pkg.provides is not None): |
1364 |
+ index = self._provides_index |
1365 |
+ root = pkg.root |
1366 |
+ for atom in pkg.provides: |
1367 |
+ # Use bisect.insort for ordered match results. |
1368 |
+ bisect.insort(index[(root, atom)], pkg) |
1369 |
+ |
1370 |
def add_installed_pkg(self, installed): |
1371 |
""" |
1372 |
Add an installed package during vdb load. These packages |
1373 |
@@ -133,6 +153,19 @@ class PackageTracker(object): |
1374 |
del self._replaced_by[installed] |
1375 |
del self._replacing[pkg] |
1376 |
|
1377 |
+ if self._provides_index is not None: |
1378 |
+ index = self._provides_index |
1379 |
+ root = pkg.root |
1380 |
+ for atom in pkg.provides: |
1381 |
+ key = (root, atom) |
1382 |
+ items = index[key] |
1383 |
+ try: |
1384 |
+ items.remove(pkg) |
1385 |
+ except ValueError: |
1386 |
+ pass |
1387 |
+ if not items: |
1388 |
+ del index[key] |
1389 |
+ |
1390 |
self._match_cache.pop(cp_key, None) |
1391 |
|
1392 |
def discard_pkg(self, pkg): |
1393 |
@@ -151,6 +184,9 @@ class PackageTracker(object): |
1394 |
If 'installed' is True, installed non-replaced |
1395 |
packages may also be returned. |
1396 |
""" |
1397 |
+ if atom.soname: |
1398 |
+ return iter(self._provides_index.get((root, atom), [])) |
1399 |
+ |
1400 |
cp_key = root, atom.cp |
1401 |
cache_key = root, atom, atom.unevaluated_atom, installed |
1402 |
try: |
1403 |
@@ -285,8 +321,6 @@ class PackageTrackerDbapiWrapper(object): |
1404 |
self._package_tracker.add_pkg(pkg) |
1405 |
|
1406 |
def match_pkgs(self, atom): |
1407 |
- if not isinstance(atom, Atom): |
1408 |
- atom = Atom(atom) |
1409 |
ret = sorted(self._package_tracker.match(self._root, atom), |
1410 |
key=cmp_sort_key(lambda x, y: vercmp(x.version, y.version))) |
1411 |
return ret |
1412 |
@@ -298,4 +332,4 @@ class PackageTrackerDbapiWrapper(object): |
1413 |
return self.match_pkgs(atom) |
1414 |
|
1415 |
def cp_list(self, cp): |
1416 |
- return self.match_pkgs(cp) |
1417 |
+ return self.match_pkgs(Atom(cp)) |
1418 |
diff --git a/pym/_emerge/resolver/slot_collision.py b/pym/_emerge/resolver/slot_collision.py |
1419 |
index baeab08..5473d72 100644 |
1420 |
--- a/pym/_emerge/resolver/slot_collision.py |
1421 |
+++ b/pym/_emerge/resolver/slot_collision.py |
1422 |
@@ -271,16 +271,27 @@ class slot_conflict_handler(object): |
1423 |
num_all_specific_atoms = 0 |
1424 |
|
1425 |
for ppkg, atom in parent_atoms: |
1426 |
- atom_set = InternalPackageSet(initial_atoms=(atom,)) |
1427 |
- atom_without_use_set = InternalPackageSet(initial_atoms=(atom.without_use,)) |
1428 |
- atom_without_use_and_slot_set = InternalPackageSet(initial_atoms=( |
1429 |
- atom.without_use.without_slot,)) |
1430 |
+ if not atom.soname: |
1431 |
+ atom_set = InternalPackageSet( |
1432 |
+ initial_atoms=(atom,)) |
1433 |
+ atom_without_use_set = InternalPackageSet( |
1434 |
+ initial_atoms=(atom.without_use,)) |
1435 |
+ atom_without_use_and_slot_set = \ |
1436 |
+ InternalPackageSet(initial_atoms=( |
1437 |
+ atom.without_use.without_slot,)) |
1438 |
|
1439 |
for other_pkg in pkgs: |
1440 |
if other_pkg == pkg: |
1441 |
continue |
1442 |
|
1443 |
- if not atom_without_use_and_slot_set.findAtomForPackage(other_pkg, \ |
1444 |
+ if atom.soname: |
1445 |
+ # The soname does not match. |
1446 |
+ key = ("soname", atom) |
1447 |
+ atoms = collision_reasons.get(key, set()) |
1448 |
+ atoms.add((ppkg, atom, other_pkg)) |
1449 |
+ num_all_specific_atoms += 1 |
1450 |
+ collision_reasons[key] = atoms |
1451 |
+ elif not atom_without_use_and_slot_set.findAtomForPackage(other_pkg, |
1452 |
modified_use=_pkg_use_enabled(other_pkg)): |
1453 |
if atom.operator is not None: |
1454 |
# The version range does not match. |
1455 |
@@ -381,7 +392,7 @@ class slot_conflict_handler(object): |
1456 |
if not verboseconflicts: |
1457 |
selected_for_display.update( |
1458 |
best_matches.values()) |
1459 |
- elif type == "slot": |
1460 |
+ elif type in ("soname", "slot"): |
1461 |
for ppkg, atom, other_pkg in parents: |
1462 |
selected_for_display.add((ppkg, atom)) |
1463 |
if not verboseconflicts: |
1464 |
@@ -532,7 +543,10 @@ class slot_conflict_handler(object): |
1465 |
ordered_list.append(parent_atom) |
1466 |
for parent_atom in ordered_list: |
1467 |
parent, atom = parent_atom |
1468 |
- if isinstance(parent, PackageArg): |
1469 |
+ if atom.soname: |
1470 |
+ msg.append("%s required by %s\n" % |
1471 |
+ (atom, parent)) |
1472 |
+ elif isinstance(parent, PackageArg): |
1473 |
# For PackageArg it's |
1474 |
# redundant to display the atom attribute. |
1475 |
msg.append("%s\n" % (parent,)) |
1476 |
@@ -677,6 +691,9 @@ class slot_conflict_handler(object): |
1477 |
for id, pkg in enumerate(config): |
1478 |
involved_flags = {} |
1479 |
for ppkg, atom in all_conflict_atoms_by_slotatom[id]: |
1480 |
+ if not atom.package: |
1481 |
+ continue |
1482 |
+ |
1483 |
if ppkg in conflict_nodes and not ppkg in config: |
1484 |
#The parent is part of a slot conflict itself and is |
1485 |
#not part of the current config. |
1486 |
@@ -882,6 +899,8 @@ class slot_conflict_handler(object): |
1487 |
|
1488 |
#Go through all (parent, atom) pairs for the current slot conflict. |
1489 |
for ppkg, atom in all_conflict_atoms_by_slotatom[id]: |
1490 |
+ if not atom.package: |
1491 |
+ continue |
1492 |
use = atom.unevaluated_atom.use |
1493 |
if not use: |
1494 |
#No need to force something for an atom without USE conditionals. |
1495 |
@@ -952,6 +971,8 @@ class slot_conflict_handler(object): |
1496 |
new_use = old_use |
1497 |
|
1498 |
for ppkg, atom in all_conflict_atoms_by_slotatom[id]: |
1499 |
+ if not atom.package: |
1500 |
+ continue |
1501 |
if not hasattr(ppkg, "use"): |
1502 |
#It's a SetArg or something like that. |
1503 |
continue |
1504 |
diff --git a/pym/portage/dbapi/DummyTree.py b/pym/portage/dbapi/DummyTree.py |
1505 |
new file mode 100644 |
1506 |
index 0000000..6579e88 |
1507 |
--- /dev/null |
1508 |
+++ b/pym/portage/dbapi/DummyTree.py |
1509 |
@@ -0,0 +1,16 @@ |
1510 |
+# Copyright 2015 Gentoo Foundation |
1511 |
+# Distributed under the terms of the GNU General Public License v2 |
1512 |
+ |
1513 |
+class DummyTree(object): |
1514 |
+ """ |
1515 |
+ Most internal code only accesses the "dbapi" attribute of the |
1516 |
+ binarytree, portagetree, and vartree classes. DummyTree is useful |
1517 |
+ in cases where alternative dbapi implementations (or wrappers that |
1518 |
+ modify or extend behavior of existing dbapi implementations) are |
1519 |
+ needed, since it allows these implementations to be exposed through |
1520 |
+ an interface which is minimally compatible with the *tree classes. |
1521 |
+ """ |
1522 |
+ __slots__ = ("dbapi",) |
1523 |
+ |
1524 |
+ def __init__(self, dbapi): |
1525 |
+ self.dbapi = dbapi |
1526 |
diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py |
1527 |
index c457df0..e2e416c 100644 |
1528 |
--- a/pym/portage/dep/__init__.py |
1529 |
+++ b/pym/portage/dep/__init__.py |
1530 |
@@ -1172,6 +1172,12 @@ class Atom(_unicode): |
1531 |
class emulates most of the str methods that are useful with atoms. |
1532 |
""" |
1533 |
|
1534 |
+ # Distiguishes package atoms from other atom types |
1535 |
+ package = True |
1536 |
+ |
1537 |
+ # Distiguishes soname atoms from other atom types |
1538 |
+ soname = False |
1539 |
+ |
1540 |
class _blocker(object): |
1541 |
__slots__ = ("overlap",) |
1542 |
|
1543 |
@@ -1561,6 +1567,25 @@ class Atom(_unicode): |
1544 |
memo[id(self)] = self |
1545 |
return self |
1546 |
|
1547 |
+ def match(self, pkg): |
1548 |
+ """ |
1549 |
+ Check if the given package instance matches this atom. This |
1550 |
+ includes support for virtual matches via PROVIDE metadata. |
1551 |
+ |
1552 |
+ @param pkg: a Package instance |
1553 |
+ @type pkg: Package |
1554 |
+ @return: True if this atom matches pkg, otherwise False |
1555 |
+ @rtype: bool |
1556 |
+ """ |
1557 |
+ if pkg.cp == self.cp: |
1558 |
+ return bool(match_from_list(self, [pkg])) |
1559 |
+ else: |
1560 |
+ for provided_cp in pkg.provided_cps: |
1561 |
+ if provided_cp == self.cp: |
1562 |
+ return bool(match_from_list( |
1563 |
+ self.replace(self.cp, provided_cp, 1), [pkg])) |
1564 |
+ return False |
1565 |
+ |
1566 |
_extended_cp_re_cache = {} |
1567 |
|
1568 |
def extended_cp_match(extended_cp, other_cp): |
1569 |
diff --git a/pym/portage/dep/soname/SonameAtom.py b/pym/portage/dep/soname/SonameAtom.py |
1570 |
new file mode 100644 |
1571 |
index 0000000..a7dad97 |
1572 |
--- /dev/null |
1573 |
+++ b/pym/portage/dep/soname/SonameAtom.py |
1574 |
@@ -0,0 +1,72 @@ |
1575 |
+# Copyright 2015 Gentoo Foundation |
1576 |
+# Distributed under the terms of the GNU General Public License v2 |
1577 |
+ |
1578 |
+from __future__ import unicode_literals |
1579 |
+ |
1580 |
+import sys |
1581 |
+ |
1582 |
+from portage import _encodings, _unicode_encode |
1583 |
+ |
1584 |
+class SonameAtom(object): |
1585 |
+ |
1586 |
+ __slots__ = ("multilib_category", "soname", "_hash_key", |
1587 |
+ "_hash_value") |
1588 |
+ |
1589 |
+ # Distiguishes package atoms from other atom types |
1590 |
+ package = False |
1591 |
+ |
1592 |
+ def __init__(self, multilib_category, soname): |
1593 |
+ object.__setattr__(self, "multilib_category", multilib_category) |
1594 |
+ object.__setattr__(self, "soname", soname) |
1595 |
+ object.__setattr__(self, "_hash_key", |
1596 |
+ (multilib_category, soname)) |
1597 |
+ object.__setattr__(self, "_hash_value", hash(self._hash_key)) |
1598 |
+ |
1599 |
+ def __setattr__(self, name, value): |
1600 |
+ raise AttributeError("SonameAtom instances are immutable", |
1601 |
+ self.__class__, name, value) |
1602 |
+ |
1603 |
+ def __hash__(self): |
1604 |
+ return self._hash_value |
1605 |
+ |
1606 |
+ def __eq__(self, other): |
1607 |
+ try: |
1608 |
+ return self._hash_key == other._hash_key |
1609 |
+ except AttributeError: |
1610 |
+ return False |
1611 |
+ |
1612 |
+ def __ne__(self, other): |
1613 |
+ try: |
1614 |
+ return self._hash_key != other._hash_key |
1615 |
+ except AttributeError: |
1616 |
+ return True |
1617 |
+ |
1618 |
+ def __repr__(self): |
1619 |
+ return "%s('%s', '%s')" % ( |
1620 |
+ self.__class__.__name__, |
1621 |
+ self.multilib_category, |
1622 |
+ self.soname |
1623 |
+ ) |
1624 |
+ |
1625 |
+ def __str__(self): |
1626 |
+ return "%s: %s" % (self.multilib_category, self.soname) |
1627 |
+ |
1628 |
+ if sys.hexversion < 0x3000000: |
1629 |
+ |
1630 |
+ __unicode__ = __str__ |
1631 |
+ |
1632 |
+ def __str__(self): |
1633 |
+ return _unicode_encode(self.__unicode__(), |
1634 |
+ encoding=_encodings['content']) |
1635 |
+ |
1636 |
+ def match(self, pkg): |
1637 |
+ """ |
1638 |
+ Check if the given package instance matches this atom. Unbuilt |
1639 |
+ ebuilds, which do not have soname metadata, will never match. |
1640 |
+ |
1641 |
+ @param pkg: a Package instance |
1642 |
+ @type pkg: Package |
1643 |
+ @return: True if this atom matches pkg, otherwise False |
1644 |
+ @rtype: bool |
1645 |
+ """ |
1646 |
+ return pkg.provides is not None and self in pkg.provides |
1647 |
diff --git a/pym/portage/dep/soname/parse.py b/pym/portage/dep/soname/parse.py |
1648 |
new file mode 100644 |
1649 |
index 0000000..3f37572 |
1650 |
--- /dev/null |
1651 |
+++ b/pym/portage/dep/soname/parse.py |
1652 |
@@ -0,0 +1,47 @@ |
1653 |
+# Copyright 2015 Gentoo Foundation |
1654 |
+# Distributed under the terms of the GNU General Public License v2 |
1655 |
+ |
1656 |
+from __future__ import unicode_literals |
1657 |
+ |
1658 |
+from portage.exception import InvalidData |
1659 |
+from portage.localization import _ |
1660 |
+from portage.dep.soname.SonameAtom import SonameAtom |
1661 |
+ |
1662 |
+_error_empty_category = _("Multilib category empty: %s") |
1663 |
+_error_missing_category = _("Multilib category missing: %s") |
1664 |
+_error_duplicate_category = _("Multilib category occurs" |
1665 |
+ " more than once: %s") |
1666 |
+ |
1667 |
+def parse_soname_deps(s): |
1668 |
+ """ |
1669 |
+ Parse a REQUIRES or PROVIDES dependency string, and raise |
1670 |
+ InvalidData if necessary. |
1671 |
+ |
1672 |
+ @param s: REQUIRES or PROVIDES string |
1673 |
+ @type s: str |
1674 |
+ @rtype: iter |
1675 |
+ @return: An iterator of SonameAtom instances |
1676 |
+ """ |
1677 |
+ |
1678 |
+ categories = set() |
1679 |
+ category = None |
1680 |
+ previous_soname = None |
1681 |
+ for soname in s.split(): |
1682 |
+ if soname.endswith(":"): |
1683 |
+ if category is not None and previous_soname is None: |
1684 |
+ raise InvalidData(_error_empty_category % category) |
1685 |
+ |
1686 |
+ category = soname[:-1] |
1687 |
+ previous_soname = None |
1688 |
+ if category in categories: |
1689 |
+ raise InvalidData(_error_duplicate_category % category) |
1690 |
+ categories.add(category) |
1691 |
+ |
1692 |
+ elif category is None: |
1693 |
+ raise InvalidData(_error_missing_category % soname) |
1694 |
+ else: |
1695 |
+ previous_soname = soname |
1696 |
+ yield SonameAtom(category, soname) |
1697 |
+ |
1698 |
+ if category is not None and previous_soname is None: |
1699 |
+ raise InvalidData(_error_empty_category % category) |
1700 |
diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py |
1701 |
index e119498..71fe4df 100644 |
1702 |
--- a/pym/portage/package/ebuild/config.py |
1703 |
+++ b/pym/portage/package/ebuild/config.py |
1704 |
@@ -22,6 +22,7 @@ from _emerge.Package import Package |
1705 |
import portage |
1706 |
portage.proxy.lazyimport.lazyimport(globals(), |
1707 |
'portage.data:portage_gid', |
1708 |
+ 'portage.dep.soname.SonameAtom:SonameAtom', |
1709 |
'portage.dbapi.vartree:vartree', |
1710 |
'portage.package.ebuild.doebuild:_phase_func_map', |
1711 |
) |
1712 |
@@ -230,6 +231,7 @@ class config(object): |
1713 |
self._features_overrides = [] |
1714 |
self._make_defaults = None |
1715 |
self._parent_stable = None |
1716 |
+ self._soname_provided = None |
1717 |
|
1718 |
# _unknown_features records unknown features that |
1719 |
# have triggered warning messages, and ensures that |
1720 |
@@ -266,6 +268,7 @@ class config(object): |
1721 |
self.make_defaults_use = clone.make_defaults_use |
1722 |
self.mycpv = clone.mycpv |
1723 |
self._setcpv_args_hash = clone._setcpv_args_hash |
1724 |
+ self._soname_provided = clone._soname_provided |
1725 |
|
1726 |
# immutable attributes (internal policy ensures lack of mutation) |
1727 |
self._locations_manager = clone._locations_manager |
1728 |
@@ -1049,6 +1052,16 @@ class config(object): |
1729 |
def punmaskdict(self): |
1730 |
return self._mask_manager._punmaskdict.copy() |
1731 |
|
1732 |
+ @property |
1733 |
+ def soname_provided(self): |
1734 |
+ if self._soname_provided is None: |
1735 |
+ d = stack_dictlist((grabdict( |
1736 |
+ os.path.join(x, "soname.provided"), recursive=True) |
1737 |
+ for x in self.profiles), incremental=True) |
1738 |
+ self._soname_provided = frozenset(SonameAtom(cat, soname) |
1739 |
+ for cat, sonames in d.items() for soname in sonames) |
1740 |
+ return self._soname_provided |
1741 |
+ |
1742 |
def expandLicenseTokens(self, tokens): |
1743 |
""" Take a token from ACCEPT_LICENSE or package.license and expand it |
1744 |
if it's a group token (indicated by @) or just return it if it's not a |
1745 |
diff --git a/pym/portage/tests/resolver/ResolverPlayground.py b/pym/portage/tests/resolver/ResolverPlayground.py |
1746 |
index b5c0446..84ad17c 100644 |
1747 |
--- a/pym/portage/tests/resolver/ResolverPlayground.py |
1748 |
+++ b/pym/portage/tests/resolver/ResolverPlayground.py |
1749 |
@@ -40,6 +40,7 @@ class ResolverPlayground(object): |
1750 |
config_files = frozenset(("eapi", "layout.conf", "make.conf", "package.accept_keywords", |
1751 |
"package.keywords", "package.license", "package.mask", "package.properties", |
1752 |
"package.unmask", "package.use", "package.use.aliases", "package.use.stable.mask", |
1753 |
+ "soname.provided", |
1754 |
"unpack_dependencies", "use.aliases", "use.force", "use.mask", "layout.conf")) |
1755 |
|
1756 |
metadata_xml_template = """<?xml version="1.0" encoding="UTF-8"?> |
1757 |
@@ -254,6 +255,8 @@ class ResolverPlayground(object): |
1758 |
unknown_keys.discard("COUNTER") |
1759 |
unknown_keys.discard("repository") |
1760 |
unknown_keys.discard("USE") |
1761 |
+ unknown_keys.discard("PROVIDES") |
1762 |
+ unknown_keys.discard("REQUIRES") |
1763 |
if unknown_keys: |
1764 |
raise ValueError("metadata of installed '%s' contains unknown keys: %s" % |
1765 |
(cpv, sorted(unknown_keys))) |
1766 |
diff --git a/pym/portage/tests/resolver/soname/__init__.py b/pym/portage/tests/resolver/soname/__init__.py |
1767 |
new file mode 100644 |
1768 |
index 0000000..4725d33 |
1769 |
--- /dev/null |
1770 |
+++ b/pym/portage/tests/resolver/soname/__init__.py |
1771 |
@@ -0,0 +1,2 @@ |
1772 |
+# Copyright 2015 Gentoo Foundation |
1773 |
+# Distributed under the terms of the GNU General Public License v2 |
1774 |
diff --git a/pym/portage/tests/resolver/soname/__test__.py b/pym/portage/tests/resolver/soname/__test__.py |
1775 |
new file mode 100644 |
1776 |
index 0000000..4725d33 |
1777 |
--- /dev/null |
1778 |
+++ b/pym/portage/tests/resolver/soname/__test__.py |
1779 |
@@ -0,0 +1,2 @@ |
1780 |
+# Copyright 2015 Gentoo Foundation |
1781 |
+# Distributed under the terms of the GNU General Public License v2 |
1782 |
diff --git a/pym/portage/tests/resolver/soname/test_autounmask.py b/pym/portage/tests/resolver/soname/test_autounmask.py |
1783 |
new file mode 100644 |
1784 |
index 0000000..be0f94e |
1785 |
--- /dev/null |
1786 |
+++ b/pym/portage/tests/resolver/soname/test_autounmask.py |
1787 |
@@ -0,0 +1,103 @@ |
1788 |
+# Copyright 2015 Gentoo Foundation |
1789 |
+# Distributed under the terms of the GNU General Public License v2 |
1790 |
+ |
1791 |
+from portage.tests import TestCase |
1792 |
+from portage.tests.resolver.ResolverPlayground import ( |
1793 |
+ ResolverPlayground, ResolverPlaygroundTestCase) |
1794 |
+ |
1795 |
+class SonameAutoUnmaskTestCase(TestCase): |
1796 |
+ |
1797 |
+ def testSonameAutoUnmask(self): |
1798 |
+ |
1799 |
+ binpkgs = { |
1800 |
+ "dev-libs/icu-49" : { |
1801 |
+ "KEYWORDS": "x86", |
1802 |
+ "PROVIDES": "x86_32: libicu.so.49", |
1803 |
+ }, |
1804 |
+ "dev-libs/icu-4.8" : { |
1805 |
+ "KEYWORDS": "x86", |
1806 |
+ "PROVIDES": "x86_32: libicu.so.48", |
1807 |
+ }, |
1808 |
+ "dev-libs/libxml2-2.7.8" : { |
1809 |
+ "KEYWORDS": "~x86", |
1810 |
+ "DEPEND": "dev-libs/icu", |
1811 |
+ "RDEPEND": "dev-libs/icu", |
1812 |
+ "REQUIRES": "x86_32: libicu.so.49", |
1813 |
+ }, |
1814 |
+ } |
1815 |
+ |
1816 |
+ installed = { |
1817 |
+ "dev-libs/icu-4.8" : { |
1818 |
+ "KEYWORDS": "x86", |
1819 |
+ "PROVIDES": "x86_32: libicu.so.48", |
1820 |
+ }, |
1821 |
+ "dev-libs/libxml2-2.7.8" : { |
1822 |
+ "KEYWORDS": "~x86", |
1823 |
+ "DEPEND": "dev-libs/icu", |
1824 |
+ "RDEPEND": "dev-libs/icu", |
1825 |
+ "REQUIRES": "x86_32: libicu.so.48", |
1826 |
+ }, |
1827 |
+ } |
1828 |
+ |
1829 |
+ world = ["dev-libs/libxml2"] |
1830 |
+ |
1831 |
+ test_cases = ( |
1832 |
+ |
1833 |
+ ResolverPlaygroundTestCase( |
1834 |
+ ["dev-libs/icu"], |
1835 |
+ options = { |
1836 |
+ "--autounmask": True, |
1837 |
+ "--ignore-soname-deps": "n", |
1838 |
+ "--oneshot": True, |
1839 |
+ "--usepkgonly": True, |
1840 |
+ }, |
1841 |
+ success = False, |
1842 |
+ mergelist = [ |
1843 |
+ "[binary]dev-libs/icu-49", |
1844 |
+ "[binary]dev-libs/libxml2-2.7.8" |
1845 |
+ ], |
1846 |
+ unstable_keywords = ['dev-libs/libxml2-2.7.8'], |
1847 |
+ ), |
1848 |
+ |
1849 |
+ ResolverPlaygroundTestCase( |
1850 |
+ ["dev-libs/icu"], |
1851 |
+ options = { |
1852 |
+ "--autounmask": True, |
1853 |
+ "--ignore-soname-deps": "y", |
1854 |
+ "--oneshot": True, |
1855 |
+ "--usepkgonly": True, |
1856 |
+ }, |
1857 |
+ success = True, |
1858 |
+ mergelist = [ |
1859 |
+ "[binary]dev-libs/icu-49" |
1860 |
+ ] |
1861 |
+ ), |
1862 |
+ |
1863 |
+ # Test that dev-libs/icu-49 update is skipped due to |
1864 |
+ # dev-libs/libxml2-2.7.8 being masked by KEYWORDS. Note |
1865 |
+ # that this result is questionable, since the installed |
1866 |
+ # dev-libs/libxml2-2.7.8 instance is also masked! |
1867 |
+ ResolverPlaygroundTestCase( |
1868 |
+ ["@world"], |
1869 |
+ options = { |
1870 |
+ "--autounmask": True, |
1871 |
+ "--deep": True, |
1872 |
+ "--ignore-soname-deps": "n", |
1873 |
+ "--update": True, |
1874 |
+ "--usepkgonly": True, |
1875 |
+ }, |
1876 |
+ success = True, |
1877 |
+ mergelist = [], |
1878 |
+ ), |
1879 |
+ |
1880 |
+ ) |
1881 |
+ |
1882 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
1883 |
+ installed=installed, world=world, debug=False) |
1884 |
+ try: |
1885 |
+ for test_case in test_cases: |
1886 |
+ playground.run_TestCase(test_case) |
1887 |
+ self.assertEqual(test_case.test_success, True, test_case.fail_msg) |
1888 |
+ finally: |
1889 |
+ playground.debug = False |
1890 |
+ playground.cleanup() |
1891 |
diff --git a/pym/portage/tests/resolver/soname/test_depclean.py b/pym/portage/tests/resolver/soname/test_depclean.py |
1892 |
new file mode 100644 |
1893 |
index 0000000..50cc169 |
1894 |
--- /dev/null |
1895 |
+++ b/pym/portage/tests/resolver/soname/test_depclean.py |
1896 |
@@ -0,0 +1,61 @@ |
1897 |
+# Copyright 2015 Gentoo Foundation |
1898 |
+# Distributed under the terms of the GNU General Public License v2 |
1899 |
+ |
1900 |
+from portage.tests import TestCase |
1901 |
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, |
1902 |
+ ResolverPlaygroundTestCase) |
1903 |
+ |
1904 |
+class SonameDepcleanTestCase(TestCase): |
1905 |
+ |
1906 |
+ def testSonameDepclean(self): |
1907 |
+ |
1908 |
+ installed = { |
1909 |
+ "app-misc/A-1" : { |
1910 |
+ "RDEPEND": "dev-libs/B", |
1911 |
+ "DEPEND": "dev-libs/B", |
1912 |
+ "REQUIRES": "x86_32: libB.so.1 libc.so.6", |
1913 |
+ }, |
1914 |
+ "dev-libs/B-1" : { |
1915 |
+ "PROVIDES": "x86_32: libB.so.1", |
1916 |
+ }, |
1917 |
+ "sys-libs/glibc-2.19-r1" : { |
1918 |
+ "PROVIDES": "x86_32: libc.so.6" |
1919 |
+ }, |
1920 |
+ } |
1921 |
+ |
1922 |
+ world = ("app-misc/A",) |
1923 |
+ |
1924 |
+ test_cases = ( |
1925 |
+ |
1926 |
+ ResolverPlaygroundTestCase( |
1927 |
+ [], |
1928 |
+ options={ |
1929 |
+ "--depclean": True, |
1930 |
+ "--ignore-soname-deps": "n", |
1931 |
+ }, |
1932 |
+ success=True, |
1933 |
+ cleanlist=[] |
1934 |
+ ), |
1935 |
+ |
1936 |
+ ResolverPlaygroundTestCase( |
1937 |
+ [], |
1938 |
+ options={ |
1939 |
+ "--depclean": True, |
1940 |
+ "--ignore-soname-deps": "y", |
1941 |
+ }, |
1942 |
+ success=True, |
1943 |
+ cleanlist=["sys-libs/glibc-2.19-r1"] |
1944 |
+ ), |
1945 |
+ ) |
1946 |
+ |
1947 |
+ playground = ResolverPlayground(debug=False, |
1948 |
+ installed=installed, world=world) |
1949 |
+ try: |
1950 |
+ for test_case in test_cases: |
1951 |
+ playground.run_TestCase(test_case) |
1952 |
+ self.assertEqual(test_case.test_success, True, |
1953 |
+ test_case.fail_msg) |
1954 |
+ finally: |
1955 |
+ # Disable debug so that cleanup works. |
1956 |
+ playground.debug = False |
1957 |
+ playground.cleanup() |
1958 |
diff --git a/pym/portage/tests/resolver/soname/test_downgrade.py b/pym/portage/tests/resolver/soname/test_downgrade.py |
1959 |
new file mode 100644 |
1960 |
index 0000000..a95be34 |
1961 |
--- /dev/null |
1962 |
+++ b/pym/portage/tests/resolver/soname/test_downgrade.py |
1963 |
@@ -0,0 +1,240 @@ |
1964 |
+# Copyright 2015 Gentoo Foundation |
1965 |
+# Distributed under the terms of the GNU General Public License v2 |
1966 |
+ |
1967 |
+from portage.tests import TestCase |
1968 |
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, |
1969 |
+ ResolverPlaygroundTestCase) |
1970 |
+ |
1971 |
+class SonameDowngradeTestCase(TestCase): |
1972 |
+ |
1973 |
+ def testSingleSlot(self): |
1974 |
+ |
1975 |
+ ebuilds = { |
1976 |
+ "dev-libs/icu-49" : { |
1977 |
+ }, |
1978 |
+ "dev-libs/icu-4.8" : { |
1979 |
+ }, |
1980 |
+ "dev-libs/libxml2-2.7.8" : { |
1981 |
+ "DEPEND": "dev-libs/icu", |
1982 |
+ "RDEPEND": "dev-libs/icu", |
1983 |
+ }, |
1984 |
+ } |
1985 |
+ |
1986 |
+ binpkgs = { |
1987 |
+ "dev-libs/icu-49" : { |
1988 |
+ "PROVIDES": "x86_32: libicu.so.49", |
1989 |
+ }, |
1990 |
+ "dev-libs/icu-4.8" : { |
1991 |
+ "PROVIDES": "x86_32: libicu.so.48", |
1992 |
+ }, |
1993 |
+ "dev-libs/libxml2-2.7.8" : { |
1994 |
+ "DEPEND": "dev-libs/icu", |
1995 |
+ "RDEPEND": "dev-libs/icu", |
1996 |
+ "REQUIRES": "x86_32: libicu.so.48", |
1997 |
+ }, |
1998 |
+ } |
1999 |
+ installed = { |
2000 |
+ "dev-libs/icu-49" : { |
2001 |
+ "PROVIDES": "x86_32: libicu.so.49", |
2002 |
+ }, |
2003 |
+ "dev-libs/libxml2-2.7.8" : { |
2004 |
+ "DEPEND": "dev-libs/icu", |
2005 |
+ "RDEPEND": "dev-libs/icu", |
2006 |
+ "REQUIRES": "x86_32: libicu.so.49", |
2007 |
+ }, |
2008 |
+ } |
2009 |
+ |
2010 |
+ user_config = { |
2011 |
+ "package.mask" : ( |
2012 |
+ ">=dev-libs/icu-49", |
2013 |
+ ), |
2014 |
+ } |
2015 |
+ |
2016 |
+ world = ["dev-libs/libxml2"] |
2017 |
+ |
2018 |
+ test_cases = ( |
2019 |
+ |
2020 |
+ ResolverPlaygroundTestCase( |
2021 |
+ ["dev-libs/icu"], |
2022 |
+ options = { |
2023 |
+ "--autounmask": "n", |
2024 |
+ "--ignore-soname-deps": "n", |
2025 |
+ "--oneshot": True, |
2026 |
+ "--usepkgonly": True |
2027 |
+ }, |
2028 |
+ success = True, |
2029 |
+ mergelist = [ |
2030 |
+ "[binary]dev-libs/icu-4.8", |
2031 |
+ "[binary]dev-libs/libxml2-2.7.8" |
2032 |
+ ] |
2033 |
+ ), |
2034 |
+ |
2035 |
+ ResolverPlaygroundTestCase( |
2036 |
+ ["dev-libs/icu"], |
2037 |
+ options = { |
2038 |
+ "--autounmask": "n", |
2039 |
+ "--ignore-soname-deps": "y", |
2040 |
+ "--oneshot": True, |
2041 |
+ "--usepkgonly": True |
2042 |
+ }, |
2043 |
+ success = True, |
2044 |
+ mergelist = [ |
2045 |
+ "[binary]dev-libs/icu-4.8", |
2046 |
+ ] |
2047 |
+ ), |
2048 |
+ |
2049 |
+ ResolverPlaygroundTestCase( |
2050 |
+ ["@world"], |
2051 |
+ options = { |
2052 |
+ "--autounmask": "n", |
2053 |
+ "--deep": True, |
2054 |
+ "--ignore-soname-deps": "n", |
2055 |
+ "--update": True, |
2056 |
+ "--usepkgonly": True, |
2057 |
+ }, |
2058 |
+ success = True, |
2059 |
+ mergelist = [ |
2060 |
+ "[binary]dev-libs/icu-4.8", |
2061 |
+ "[binary]dev-libs/libxml2-2.7.8" |
2062 |
+ ] |
2063 |
+ ), |
2064 |
+ |
2065 |
+ # In this case, soname dependencies are not respected, |
2066 |
+ # because --usepkgonly is not enabled. This could be |
2067 |
+ # handled differently, by respecting soname dependencies |
2068 |
+ # as long as no unbuilt ebuilds get pulled into the graph. |
2069 |
+ # However, that kind of conditional dependency accounting |
2070 |
+ # would add a significant amount of complexity. |
2071 |
+ ResolverPlaygroundTestCase( |
2072 |
+ ["@world"], |
2073 |
+ options = { |
2074 |
+ "--deep": True, |
2075 |
+ "--ignore-soname-deps": "n", |
2076 |
+ "--update": True, |
2077 |
+ "--usepkg": True, |
2078 |
+ }, |
2079 |
+ success = True, |
2080 |
+ mergelist = [ |
2081 |
+ "[binary]dev-libs/icu-4.8", |
2082 |
+ ] |
2083 |
+ ), |
2084 |
+ |
2085 |
+ ResolverPlaygroundTestCase( |
2086 |
+ ["@world"], |
2087 |
+ options = { |
2088 |
+ "--deep": True, |
2089 |
+ "--update": True, |
2090 |
+ }, |
2091 |
+ success = True, |
2092 |
+ mergelist = [ |
2093 |
+ "dev-libs/icu-4.8", |
2094 |
+ ] |
2095 |
+ ), |
2096 |
+ ) |
2097 |
+ |
2098 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
2099 |
+ ebuilds=ebuilds, installed=installed, |
2100 |
+ user_config=user_config, world=world, debug=False) |
2101 |
+ try: |
2102 |
+ for test_case in test_cases: |
2103 |
+ playground.run_TestCase(test_case) |
2104 |
+ self.assertEqual(test_case.test_success, True, test_case.fail_msg) |
2105 |
+ finally: |
2106 |
+ # Disable debug so that cleanup works. |
2107 |
+ playground.debug = False |
2108 |
+ playground.cleanup() |
2109 |
+ |
2110 |
+ def testTwoSlots(self): |
2111 |
+ |
2112 |
+ ebuilds = { |
2113 |
+ "dev-libs/glib-1.2.10" : { |
2114 |
+ "SLOT": "1" |
2115 |
+ }, |
2116 |
+ "dev-libs/glib-2.30.2" : { |
2117 |
+ "SLOT": "2" |
2118 |
+ }, |
2119 |
+ "dev-libs/dbus-glib-0.98" : { |
2120 |
+ "EAPI": "1", |
2121 |
+ "DEPEND": "dev-libs/glib:2", |
2122 |
+ "RDEPEND": "dev-libs/glib:2" |
2123 |
+ }, |
2124 |
+ } |
2125 |
+ binpkgs = { |
2126 |
+ "dev-libs/glib-1.2.10" : { |
2127 |
+ "SLOT": "1", |
2128 |
+ "PROVIDES": "x86_32: libglib-1.0.so.0", |
2129 |
+ }, |
2130 |
+ "dev-libs/glib-2.30.2" : { |
2131 |
+ "PROVIDES": "x86_32: libglib-2.0.so.30", |
2132 |
+ "SLOT": "2", |
2133 |
+ }, |
2134 |
+ "dev-libs/glib-2.32.3" : { |
2135 |
+ "PROVIDES": "x86_32: libglib-2.0.so.32", |
2136 |
+ "SLOT": "2", |
2137 |
+ }, |
2138 |
+ "dev-libs/dbus-glib-0.98" : { |
2139 |
+ "EAPI": "1", |
2140 |
+ "DEPEND": "dev-libs/glib:2", |
2141 |
+ "RDEPEND": "dev-libs/glib:2", |
2142 |
+ "REQUIRES": "x86_32: libglib-2.0.so.30", |
2143 |
+ }, |
2144 |
+ } |
2145 |
+ installed = { |
2146 |
+ "dev-libs/glib-1.2.10" : { |
2147 |
+ "PROVIDES": "x86_32: libglib-1.0.so.0", |
2148 |
+ "SLOT": "1", |
2149 |
+ }, |
2150 |
+ "dev-libs/glib-2.32.3" : { |
2151 |
+ "PROVIDES": "x86_32: libglib-2.0.so.32", |
2152 |
+ "SLOT": "2", |
2153 |
+ }, |
2154 |
+ "dev-libs/dbus-glib-0.98" : { |
2155 |
+ "EAPI": "1", |
2156 |
+ "DEPEND": "dev-libs/glib:2", |
2157 |
+ "RDEPEND": "dev-libs/glib:2", |
2158 |
+ "REQUIRES": "x86_32: libglib-2.0.so.32", |
2159 |
+ }, |
2160 |
+ } |
2161 |
+ |
2162 |
+ user_config = { |
2163 |
+ "package.mask" : ( |
2164 |
+ ">=dev-libs/glib-2.32", |
2165 |
+ ), |
2166 |
+ } |
2167 |
+ |
2168 |
+ world = [ |
2169 |
+ "dev-libs/glib:1", |
2170 |
+ "dev-libs/dbus-glib", |
2171 |
+ ] |
2172 |
+ |
2173 |
+ test_cases = ( |
2174 |
+ |
2175 |
+ ResolverPlaygroundTestCase( |
2176 |
+ ["@world"], |
2177 |
+ options = { |
2178 |
+ "--autounmask": "n", |
2179 |
+ "--deep": True, |
2180 |
+ "--ignore-soname-deps": "n", |
2181 |
+ "--update": True, |
2182 |
+ "--usepkgonly": True, |
2183 |
+ }, |
2184 |
+ success = True, |
2185 |
+ mergelist = [ |
2186 |
+ "[binary]dev-libs/glib-2.30.2", |
2187 |
+ "[binary]dev-libs/dbus-glib-0.98" |
2188 |
+ ] |
2189 |
+ ), |
2190 |
+ |
2191 |
+ ) |
2192 |
+ |
2193 |
+ playground = ResolverPlayground(ebuilds=ebuilds, binpkgs=binpkgs, |
2194 |
+ installed=installed, user_config=user_config, world=world, |
2195 |
+ debug=False) |
2196 |
+ try: |
2197 |
+ for test_case in test_cases: |
2198 |
+ playground.run_TestCase(test_case) |
2199 |
+ self.assertEqual(test_case.test_success, True, test_case.fail_msg) |
2200 |
+ finally: |
2201 |
+ # Disable debug so that cleanup works. |
2202 |
+ playground.debug = False |
2203 |
+ playground.cleanup() |
2204 |
diff --git a/pym/portage/tests/resolver/soname/test_or_choices.py b/pym/portage/tests/resolver/soname/test_or_choices.py |
2205 |
new file mode 100644 |
2206 |
index 0000000..2420cd3 |
2207 |
--- /dev/null |
2208 |
+++ b/pym/portage/tests/resolver/soname/test_or_choices.py |
2209 |
@@ -0,0 +1,92 @@ |
2210 |
+# Copyright 2015 Gentoo Foundation |
2211 |
+# Distributed under the terms of the GNU General Public License v2 |
2212 |
+ |
2213 |
+from portage.tests import TestCase |
2214 |
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, |
2215 |
+ ResolverPlaygroundTestCase) |
2216 |
+ |
2217 |
+class SonameOrChoicesTestCase(TestCase): |
2218 |
+ |
2219 |
+ def testSonameConflictMissedUpdate(self): |
2220 |
+ |
2221 |
+ binpkgs = { |
2222 |
+ "dev-lang/ocaml-4.02.1" : { |
2223 |
+ "EAPI": "5", |
2224 |
+ "PROVIDES": "x86_32: libocaml-4.02.1.so", |
2225 |
+ }, |
2226 |
+ |
2227 |
+ "dev-lang/ocaml-4.01.0" : { |
2228 |
+ "EAPI": "5", |
2229 |
+ "PROVIDES": "x86_32: libocaml-4.01.0.so", |
2230 |
+ }, |
2231 |
+ |
2232 |
+ "dev-ml/lablgl-1.05" : { |
2233 |
+ "DEPEND": (">=dev-lang/ocaml-3.10.2 " |
2234 |
+ "|| ( dev-ml/labltk <dev-lang/ocaml-4.02 )"), |
2235 |
+ "RDEPEND": (">=dev-lang/ocaml-3.10.2 " |
2236 |
+ "|| ( dev-ml/labltk <dev-lang/ocaml-4.02 )"), |
2237 |
+ "REQUIRES": "x86_32: libocaml-4.02.1.so", |
2238 |
+ }, |
2239 |
+ |
2240 |
+ "dev-ml/labltk-8.06.0" : { |
2241 |
+ "EAPI": "5", |
2242 |
+ "SLOT": "0/8.06.0", |
2243 |
+ "DEPEND": ">=dev-lang/ocaml-4.02", |
2244 |
+ "RDEPEND": ">=dev-lang/ocaml-4.02", |
2245 |
+ "REQUIRES": "x86_32: libocaml-4.02.1.so", |
2246 |
+ }, |
2247 |
+ } |
2248 |
+ |
2249 |
+ installed = { |
2250 |
+ "dev-lang/ocaml-4.01.0" : { |
2251 |
+ "EAPI": "5", |
2252 |
+ "PROVIDES": "x86_32: libocaml-4.01.0.so", |
2253 |
+ }, |
2254 |
+ |
2255 |
+ "dev-ml/lablgl-1.05" : { |
2256 |
+ "DEPEND": (">=dev-lang/ocaml-3.10.2 " |
2257 |
+ "|| ( dev-ml/labltk <dev-lang/ocaml-4.02 )"), |
2258 |
+ "RDEPEND": (">=dev-lang/ocaml-3.10.2 " |
2259 |
+ "|| ( dev-ml/labltk <dev-lang/ocaml-4.02 )"), |
2260 |
+ "REQUIRES": "x86_32: libocaml-4.01.0.so", |
2261 |
+ }, |
2262 |
+ } |
2263 |
+ |
2264 |
+ world = ( |
2265 |
+ "dev-lang/ocaml", |
2266 |
+ "dev-ml/lablgl", |
2267 |
+ ) |
2268 |
+ |
2269 |
+ test_cases = ( |
2270 |
+ |
2271 |
+ # bug #531656: If an ocaml update is desirable, |
2272 |
+ # then we need to pull in dev-ml/labltk. |
2273 |
+ ResolverPlaygroundTestCase( |
2274 |
+ ["@world"], |
2275 |
+ options = { |
2276 |
+ "--deep": True, |
2277 |
+ "--ignore-soname-deps": "n", |
2278 |
+ "--update": True, |
2279 |
+ "--usepkgonly": True |
2280 |
+ }, |
2281 |
+ success = True, |
2282 |
+ mergelist = [ |
2283 |
+ "[binary]dev-lang/ocaml-4.02.1", |
2284 |
+ "[binary]dev-ml/labltk-8.06.0", |
2285 |
+ "[binary]dev-ml/lablgl-1.05", |
2286 |
+ ] |
2287 |
+ ), |
2288 |
+ |
2289 |
+ ) |
2290 |
+ |
2291 |
+ playground = ResolverPlayground(debug=False, |
2292 |
+ binpkgs=binpkgs, installed=installed, world=world) |
2293 |
+ try: |
2294 |
+ for test_case in test_cases: |
2295 |
+ playground.run_TestCase(test_case) |
2296 |
+ self.assertEqual(test_case.test_success, True, |
2297 |
+ test_case.fail_msg) |
2298 |
+ finally: |
2299 |
+ # Disable debug so that cleanup works. |
2300 |
+ playground.debug = False |
2301 |
+ playground.cleanup() |
2302 |
diff --git a/pym/portage/tests/resolver/soname/test_reinstall.py b/pym/portage/tests/resolver/soname/test_reinstall.py |
2303 |
new file mode 100644 |
2304 |
index 0000000..b8f2d2c |
2305 |
--- /dev/null |
2306 |
+++ b/pym/portage/tests/resolver/soname/test_reinstall.py |
2307 |
@@ -0,0 +1,87 @@ |
2308 |
+# Copyright 2015 Gentoo Foundation |
2309 |
+# Distributed under the terms of the GNU General Public License v2 |
2310 |
+ |
2311 |
+from portage.tests import TestCase |
2312 |
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, |
2313 |
+ ResolverPlaygroundTestCase) |
2314 |
+ |
2315 |
+class SonameReinstallTestCase(TestCase): |
2316 |
+ |
2317 |
+ def testSonameReinstall(self): |
2318 |
+ |
2319 |
+ binpkgs = { |
2320 |
+ "app-misc/A-1" : { |
2321 |
+ "RDEPEND": "dev-libs/B", |
2322 |
+ "DEPEND": "dev-libs/B", |
2323 |
+ "REQUIRES": "x86_32: libB.so.2", |
2324 |
+ }, |
2325 |
+ "dev-libs/B-2" : { |
2326 |
+ "PROVIDES": "x86_32: libB.so.2", |
2327 |
+ }, |
2328 |
+ "dev-libs/B-1" : { |
2329 |
+ "PROVIDES": "x86_32: libB.so.1", |
2330 |
+ }, |
2331 |
+ } |
2332 |
+ |
2333 |
+ installed = { |
2334 |
+ "app-misc/A-1" : { |
2335 |
+ "RDEPEND": "dev-libs/B", |
2336 |
+ "DEPEND": "dev-libs/B", |
2337 |
+ "REQUIRES": "x86_32: libB.so.1", |
2338 |
+ }, |
2339 |
+ "dev-libs/B-1" : { |
2340 |
+ "PROVIDES": "x86_32: libB.so.1", |
2341 |
+ }, |
2342 |
+ } |
2343 |
+ |
2344 |
+ world = ("app-misc/A",) |
2345 |
+ |
2346 |
+ test_cases = ( |
2347 |
+ |
2348 |
+ # Test that --ignore-soname-deps prevents the above |
2349 |
+ # rebuild from being triggered. |
2350 |
+ ResolverPlaygroundTestCase( |
2351 |
+ ["@world"], |
2352 |
+ options = { |
2353 |
+ "--deep": True, |
2354 |
+ "--ignore-soname-deps": "n", |
2355 |
+ "--update": True, |
2356 |
+ "--usepkgonly": True |
2357 |
+ }, |
2358 |
+ success = True, |
2359 |
+ mergelist = [ |
2360 |
+ "[binary]dev-libs/B-2", |
2361 |
+ "[binary]app-misc/A-1", |
2362 |
+ ] |
2363 |
+ ), |
2364 |
+ |
2365 |
+ # Test that --ignore-soname-deps prevents the above |
2366 |
+ # reinstall from being triggered. |
2367 |
+ ResolverPlaygroundTestCase( |
2368 |
+ ["@world"], |
2369 |
+ options = { |
2370 |
+ "--deep": True, |
2371 |
+ "--ignore-soname-deps": "y", |
2372 |
+ "--update": True, |
2373 |
+ "--usepkgonly": True |
2374 |
+ }, |
2375 |
+ success = True, |
2376 |
+ mergelist = [ |
2377 |
+ "[binary]dev-libs/B-2", |
2378 |
+ ] |
2379 |
+ ), |
2380 |
+ |
2381 |
+ ) |
2382 |
+ |
2383 |
+ playground = ResolverPlayground(debug=False, |
2384 |
+ binpkgs=binpkgs, installed=installed, |
2385 |
+ world=world) |
2386 |
+ try: |
2387 |
+ for test_case in test_cases: |
2388 |
+ playground.run_TestCase(test_case) |
2389 |
+ self.assertEqual(test_case.test_success, True, |
2390 |
+ test_case.fail_msg) |
2391 |
+ finally: |
2392 |
+ # Disable debug so that cleanup works. |
2393 |
+ playground.debug = False |
2394 |
+ playground.cleanup() |
2395 |
diff --git a/pym/portage/tests/resolver/soname/test_skip_update.py b/pym/portage/tests/resolver/soname/test_skip_update.py |
2396 |
new file mode 100644 |
2397 |
index 0000000..67e1e02 |
2398 |
--- /dev/null |
2399 |
+++ b/pym/portage/tests/resolver/soname/test_skip_update.py |
2400 |
@@ -0,0 +1,86 @@ |
2401 |
+# Copyright 2015 Gentoo Foundation |
2402 |
+# Distributed under the terms of the GNU General Public License v2 |
2403 |
+ |
2404 |
+from portage.tests import TestCase |
2405 |
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, |
2406 |
+ ResolverPlaygroundTestCase) |
2407 |
+ |
2408 |
+class SonameSkipUpdateTestCase(TestCase): |
2409 |
+ |
2410 |
+ def testSonameSkipUpdate(self): |
2411 |
+ |
2412 |
+ binpkgs = { |
2413 |
+ "app-misc/A-1" : { |
2414 |
+ "RDEPEND": "dev-libs/B", |
2415 |
+ "DEPEND": "dev-libs/B", |
2416 |
+ "REQUIRES": "x86_32: libB.so.1", |
2417 |
+ }, |
2418 |
+ "dev-libs/B-2" : { |
2419 |
+ "PROVIDES": "x86_32: libB.so.2", |
2420 |
+ }, |
2421 |
+ "dev-libs/B-1" : { |
2422 |
+ "PROVIDES": "x86_32: libB.so.1", |
2423 |
+ }, |
2424 |
+ } |
2425 |
+ |
2426 |
+ installed = { |
2427 |
+ "app-misc/A-1" : { |
2428 |
+ "RDEPEND": "dev-libs/B", |
2429 |
+ "DEPEND": "dev-libs/B", |
2430 |
+ "REQUIRES": "x86_32: libB.so.1", |
2431 |
+ }, |
2432 |
+ "dev-libs/B-1" : { |
2433 |
+ "PROVIDES": "x86_32: libB.so.1", |
2434 |
+ }, |
2435 |
+ } |
2436 |
+ |
2437 |
+ world = ("app-misc/A",) |
2438 |
+ |
2439 |
+ test_cases = ( |
2440 |
+ |
2441 |
+ # Test that --ignore-soname-deps allows the upgrade, |
2442 |
+ # even though it will break an soname dependency of |
2443 |
+ # app-misc/A-1. |
2444 |
+ ResolverPlaygroundTestCase( |
2445 |
+ ["@world"], |
2446 |
+ options = { |
2447 |
+ "--deep": True, |
2448 |
+ "--ignore-soname-deps": "y", |
2449 |
+ "--update": True, |
2450 |
+ "--usepkgonly": True |
2451 |
+ }, |
2452 |
+ success = True, |
2453 |
+ mergelist = [ |
2454 |
+ "[binary]dev-libs/B-2", |
2455 |
+ ] |
2456 |
+ ), |
2457 |
+ |
2458 |
+ # Test that upgrade to B-2 is skipped with --usepkgonly |
2459 |
+ # because it will break an soname dependency that |
2460 |
+ # cannot be satisfied by the available binary packages. |
2461 |
+ ResolverPlaygroundTestCase( |
2462 |
+ ["@world"], |
2463 |
+ options = { |
2464 |
+ "--deep": True, |
2465 |
+ "--ignore-soname-deps": "n", |
2466 |
+ "--update": True, |
2467 |
+ "--usepkgonly": True |
2468 |
+ }, |
2469 |
+ success = True, |
2470 |
+ mergelist = [] |
2471 |
+ ), |
2472 |
+ |
2473 |
+ ) |
2474 |
+ |
2475 |
+ playground = ResolverPlayground(debug=False, |
2476 |
+ binpkgs=binpkgs, installed=installed, |
2477 |
+ world=world) |
2478 |
+ try: |
2479 |
+ for test_case in test_cases: |
2480 |
+ playground.run_TestCase(test_case) |
2481 |
+ self.assertEqual(test_case.test_success, True, |
2482 |
+ test_case.fail_msg) |
2483 |
+ finally: |
2484 |
+ # Disable debug so that cleanup works. |
2485 |
+ playground.debug = False |
2486 |
+ playground.cleanup() |
2487 |
diff --git a/pym/portage/tests/resolver/soname/test_slot_conflict_reinstall.py b/pym/portage/tests/resolver/soname/test_slot_conflict_reinstall.py |
2488 |
new file mode 100644 |
2489 |
index 0000000..40e6995 |
2490 |
--- /dev/null |
2491 |
+++ b/pym/portage/tests/resolver/soname/test_slot_conflict_reinstall.py |
2492 |
@@ -0,0 +1,342 @@ |
2493 |
+# Copyright 2015 Gentoo Foundation |
2494 |
+# Distributed under the terms of the GNU General Public License v2 |
2495 |
+ |
2496 |
+from portage.tests import TestCase |
2497 |
+from portage.tests.resolver.ResolverPlayground import ( |
2498 |
+ ResolverPlayground, ResolverPlaygroundTestCase) |
2499 |
+ |
2500 |
+class SonameSlotConflictReinstallTestCase(TestCase): |
2501 |
+ |
2502 |
+ def testSonameSlotConflictReinstall(self): |
2503 |
+ |
2504 |
+ binpkgs = { |
2505 |
+ |
2506 |
+ "app-misc/A-1" : { |
2507 |
+ "PROVIDES": "x86_32: libA-1.so", |
2508 |
+ }, |
2509 |
+ |
2510 |
+ "app-misc/A-2" : { |
2511 |
+ "PROVIDES": "x86_32: libA-2.so", |
2512 |
+ }, |
2513 |
+ |
2514 |
+ "app-misc/B-0" : { |
2515 |
+ "DEPEND": "app-misc/A", |
2516 |
+ "RDEPEND": "app-misc/A", |
2517 |
+ "REQUIRES": "x86_32: libA-2.so", |
2518 |
+ }, |
2519 |
+ |
2520 |
+ "app-misc/C-0" : { |
2521 |
+ "EAPI": "5", |
2522 |
+ "DEPEND": "<app-misc/A-2", |
2523 |
+ "RDEPEND": "<app-misc/A-2" |
2524 |
+ }, |
2525 |
+ |
2526 |
+ "app-misc/D-1" : { |
2527 |
+ "PROVIDES": "x86_32: libD-1.so", |
2528 |
+ }, |
2529 |
+ |
2530 |
+ "app-misc/D-2" : { |
2531 |
+ "PROVIDES": "x86_32: libD-2.so", |
2532 |
+ }, |
2533 |
+ |
2534 |
+ "app-misc/E-0" : { |
2535 |
+ "DEPEND": "app-misc/D", |
2536 |
+ "RDEPEND": "app-misc/D", |
2537 |
+ "REQUIRES": "x86_32: libD-2.so", |
2538 |
+ }, |
2539 |
+ |
2540 |
+ } |
2541 |
+ |
2542 |
+ installed = { |
2543 |
+ |
2544 |
+ "app-misc/A-1" : { |
2545 |
+ "PROVIDES": "x86_32: libA-1.so", |
2546 |
+ }, |
2547 |
+ |
2548 |
+ "app-misc/B-0" : { |
2549 |
+ "DEPEND": "app-misc/A", |
2550 |
+ "RDEPEND": "app-misc/A", |
2551 |
+ "REQUIRES": "x86_32: libA-1.so", |
2552 |
+ }, |
2553 |
+ |
2554 |
+ "app-misc/C-0" : { |
2555 |
+ "DEPEND": "<app-misc/A-2", |
2556 |
+ "RDEPEND": "<app-misc/A-2" |
2557 |
+ }, |
2558 |
+ |
2559 |
+ "app-misc/D-1" : { |
2560 |
+ "PROVIDES": "x86_32: libD-1.so", |
2561 |
+ }, |
2562 |
+ |
2563 |
+ "app-misc/E-0" : { |
2564 |
+ "DEPEND": "app-misc/D", |
2565 |
+ "RDEPEND": "app-misc/D", |
2566 |
+ "REQUIRES": "x86_32: libD-1.so", |
2567 |
+ }, |
2568 |
+ |
2569 |
+ } |
2570 |
+ |
2571 |
+ world = ["app-misc/B", "app-misc/C", "app-misc/E"] |
2572 |
+ |
2573 |
+ test_cases = ( |
2574 |
+ |
2575 |
+ # Test bug #439688, where a slot conflict prevents an |
2576 |
+ # upgrade and we don't want to trigger unnecessary rebuilds. |
2577 |
+ ResolverPlaygroundTestCase( |
2578 |
+ ["@world"], |
2579 |
+ options = { |
2580 |
+ "--deep": True, |
2581 |
+ "--ignore-soname-deps": "n", |
2582 |
+ "--update": True, |
2583 |
+ "--usepkgonly": True, |
2584 |
+ }, |
2585 |
+ success = True, |
2586 |
+ mergelist = [ |
2587 |
+ "[binary]app-misc/D-2", |
2588 |
+ "[binary]app-misc/E-0" |
2589 |
+ ] |
2590 |
+ ), |
2591 |
+ |
2592 |
+ ) |
2593 |
+ |
2594 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
2595 |
+ installed=installed, world=world, debug=False) |
2596 |
+ try: |
2597 |
+ for test_case in test_cases: |
2598 |
+ playground.run_TestCase(test_case) |
2599 |
+ self.assertEqual(test_case.test_success, |
2600 |
+ True, test_case.fail_msg) |
2601 |
+ finally: |
2602 |
+ playground.debug = False |
2603 |
+ playground.cleanup() |
2604 |
+ |
2605 |
+ def testSonameSlotConflictMassRebuild(self): |
2606 |
+ """ |
2607 |
+ Bug 486580 |
2608 |
+ Before this bug was fixed, emerge would backtrack for each |
2609 |
+ package that needs a rebuild. This could cause it to hit the |
2610 |
+ backtrack limit and not rebuild all needed packages. |
2611 |
+ """ |
2612 |
+ binpkgs = { |
2613 |
+ |
2614 |
+ "app-misc/A-1" : { |
2615 |
+ "DEPEND": "app-misc/B", |
2616 |
+ "RDEPEND": "app-misc/B", |
2617 |
+ "REQUIRES": "x86_32: libB-2.so", |
2618 |
+ }, |
2619 |
+ |
2620 |
+ "app-misc/B-1" : { |
2621 |
+ "SLOT": "1", |
2622 |
+ "PROVIDES": "x86_32: libB-1.so", |
2623 |
+ }, |
2624 |
+ |
2625 |
+ "app-misc/B-2" : { |
2626 |
+ "SLOT": "2", |
2627 |
+ "PROVIDES": "x86_32: libB-2.so", |
2628 |
+ }, |
2629 |
+ } |
2630 |
+ |
2631 |
+ installed = { |
2632 |
+ "app-misc/B-1" : { |
2633 |
+ "SLOT": "1", |
2634 |
+ "PROVIDES": "x86_32: libB-1.so", |
2635 |
+ }, |
2636 |
+ } |
2637 |
+ |
2638 |
+ expected_mergelist = [ |
2639 |
+ '[binary]app-misc/A-1', |
2640 |
+ '[binary]app-misc/B-2' |
2641 |
+ ] |
2642 |
+ |
2643 |
+ for i in range(5): |
2644 |
+ binpkgs["app-misc/C%sC-1" % i] = { |
2645 |
+ "DEPEND": "app-misc/B", |
2646 |
+ "RDEPEND": "app-misc/B", |
2647 |
+ "REQUIRES": "x86_32: libB-2.so", |
2648 |
+ } |
2649 |
+ |
2650 |
+ installed["app-misc/C%sC-1" % i] = { |
2651 |
+ "DEPEND": "app-misc/B", |
2652 |
+ "RDEPEND": "app-misc/B", |
2653 |
+ "REQUIRES": "x86_32: libB-1.so", |
2654 |
+ } |
2655 |
+ for x in ("DEPEND", "RDEPEND"): |
2656 |
+ binpkgs["app-misc/A-1"][x] += " app-misc/C%sC" % i |
2657 |
+ |
2658 |
+ expected_mergelist.append("[binary]app-misc/C%sC-1" % i) |
2659 |
+ |
2660 |
+ |
2661 |
+ test_cases = ( |
2662 |
+ ResolverPlaygroundTestCase( |
2663 |
+ ["app-misc/A"], |
2664 |
+ ignore_mergelist_order=True, |
2665 |
+ all_permutations=True, |
2666 |
+ options = { |
2667 |
+ "--backtrack": 3, |
2668 |
+ "--deep": True, |
2669 |
+ "--ignore-soname-deps": "n", |
2670 |
+ "--update": True, |
2671 |
+ "--usepkgonly": True, |
2672 |
+ }, |
2673 |
+ success = True, |
2674 |
+ mergelist = expected_mergelist), |
2675 |
+ ) |
2676 |
+ |
2677 |
+ world = [] |
2678 |
+ |
2679 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
2680 |
+ installed=installed, world=world, debug=False) |
2681 |
+ try: |
2682 |
+ for test_case in test_cases: |
2683 |
+ playground.run_TestCase(test_case) |
2684 |
+ self.assertEqual(test_case.test_success, |
2685 |
+ True, test_case.fail_msg) |
2686 |
+ finally: |
2687 |
+ playground.debug = False |
2688 |
+ playground.cleanup() |
2689 |
+ |
2690 |
+ def testSonameSlotConflictForgottenChild(self): |
2691 |
+ """ |
2692 |
+ Similar to testSonameSlotConflictMassRebuild above, but this |
2693 |
+ time the rebuilds are scheduled, but the package causing the |
2694 |
+ rebuild (the child) is not installed. |
2695 |
+ """ |
2696 |
+ binpkgs = { |
2697 |
+ |
2698 |
+ "app-misc/A-2" : { |
2699 |
+ "DEPEND": "app-misc/B app-misc/C", |
2700 |
+ "RDEPEND": "app-misc/B app-misc/C", |
2701 |
+ "REQUIRES": "x86_32: libB-2.so", |
2702 |
+ }, |
2703 |
+ |
2704 |
+ "app-misc/B-2" : { |
2705 |
+ "PROVIDES": "x86_32: libB-2.so", |
2706 |
+ "SLOT": "2", |
2707 |
+ }, |
2708 |
+ |
2709 |
+ "app-misc/C-1": { |
2710 |
+ "DEPEND": "app-misc/B", |
2711 |
+ "RDEPEND": "app-misc/B", |
2712 |
+ "REQUIRES": "x86_32: libB-2.so", |
2713 |
+ }, |
2714 |
+ } |
2715 |
+ |
2716 |
+ installed = { |
2717 |
+ "app-misc/A-1" : { |
2718 |
+ "DEPEND": "app-misc/B app-misc/C", |
2719 |
+ "RDEPEND": "app-misc/B app-misc/C", |
2720 |
+ "REQUIRES": "x86_32: libB-1.so", |
2721 |
+ }, |
2722 |
+ |
2723 |
+ "app-misc/B-1" : { |
2724 |
+ "PROVIDES": "x86_32: libB-1.so", |
2725 |
+ "SLOT": "1", |
2726 |
+ }, |
2727 |
+ |
2728 |
+ "app-misc/C-1": { |
2729 |
+ "DEPEND": "app-misc/B", |
2730 |
+ "RDEPEND": "app-misc/B", |
2731 |
+ "REQUIRES": "x86_32: libB-1.so", |
2732 |
+ }, |
2733 |
+ } |
2734 |
+ |
2735 |
+ test_cases = ( |
2736 |
+ ResolverPlaygroundTestCase( |
2737 |
+ ["app-misc/A"], |
2738 |
+ options = { |
2739 |
+ "--ignore-soname-deps": "n", |
2740 |
+ "--usepkgonly": True, |
2741 |
+ }, |
2742 |
+ success = True, |
2743 |
+ mergelist = [ |
2744 |
+ '[binary]app-misc/B-2', |
2745 |
+ '[binary]app-misc/C-1', |
2746 |
+ '[binary]app-misc/A-2', |
2747 |
+ ] |
2748 |
+ ), |
2749 |
+ ) |
2750 |
+ |
2751 |
+ world = [] |
2752 |
+ |
2753 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
2754 |
+ installed=installed, world=world, debug=False) |
2755 |
+ try: |
2756 |
+ for test_case in test_cases: |
2757 |
+ playground.run_TestCase(test_case) |
2758 |
+ self.assertEqual(test_case.test_success, True, test_case.fail_msg) |
2759 |
+ finally: |
2760 |
+ playground.debug = False |
2761 |
+ playground.cleanup() |
2762 |
+ |
2763 |
+ def testSonameSlotConflictMixedDependencies(self): |
2764 |
+ """ |
2765 |
+ Bug 487198 |
2766 |
+ For parents with mixed >= and < dependencies, we scheduled |
2767 |
+ reinstalls for the >= atom, but in the end didn't install the |
2768 |
+ child update because of the < atom. |
2769 |
+ """ |
2770 |
+ binpkgs = { |
2771 |
+ "cat/slotted-lib-1" : { |
2772 |
+ "PROVIDES": "x86_32: lib1.so", |
2773 |
+ "SLOT": "1", |
2774 |
+ }, |
2775 |
+ "cat/slotted-lib-2" : { |
2776 |
+ "PROVIDES": "x86_32: lib2.so", |
2777 |
+ "SLOT": "2", |
2778 |
+ }, |
2779 |
+ "cat/slotted-lib-3" : { |
2780 |
+ "PROVIDES": "x86_32: lib3.so", |
2781 |
+ "SLOT": "3", |
2782 |
+ }, |
2783 |
+ "cat/slotted-lib-4" : { |
2784 |
+ "PROVIDES": "x86_32: lib4.so", |
2785 |
+ "SLOT": "4", |
2786 |
+ }, |
2787 |
+ "cat/slotted-lib-5" : { |
2788 |
+ "PROVIDES": "x86_32: lib5.so", |
2789 |
+ "SLOT": "5", |
2790 |
+ }, |
2791 |
+ "cat/user-1" : { |
2792 |
+ "DEPEND": ">=cat/slotted-lib-2 <cat/slotted-lib-4", |
2793 |
+ "RDEPEND": ">=cat/slotted-lib-2 <cat/slotted-lib-4", |
2794 |
+ "REQUIRES": "x86_32: lib3.so", |
2795 |
+ }, |
2796 |
+ } |
2797 |
+ |
2798 |
+ installed = { |
2799 |
+ "cat/slotted-lib-3" : { |
2800 |
+ "PROVIDES": "x86_32: lib3.so", |
2801 |
+ "SLOT": "3", |
2802 |
+ }, |
2803 |
+ "cat/user-1" : { |
2804 |
+ "DEPEND": ">=cat/slotted-lib-2 <cat/slotted-lib-4", |
2805 |
+ "RDEPEND": ">=cat/slotted-lib-2 <cat/slotted-lib-4", |
2806 |
+ "REQUIRES": "x86_32: lib3.so", |
2807 |
+ }, |
2808 |
+ } |
2809 |
+ |
2810 |
+ test_cases = ( |
2811 |
+ ResolverPlaygroundTestCase( |
2812 |
+ ["cat/user"], |
2813 |
+ options = { |
2814 |
+ "--deep": True, |
2815 |
+ "--ignore-soname-deps": "n", |
2816 |
+ "--update": True, |
2817 |
+ "--usepkgonly": True, |
2818 |
+ }, |
2819 |
+ success = True, |
2820 |
+ mergelist = []), |
2821 |
+ ) |
2822 |
+ |
2823 |
+ world = [] |
2824 |
+ |
2825 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
2826 |
+ installed=installed, world=world, debug=False) |
2827 |
+ try: |
2828 |
+ for test_case in test_cases: |
2829 |
+ playground.run_TestCase(test_case) |
2830 |
+ self.assertEqual(test_case.test_success, |
2831 |
+ True, test_case.fail_msg) |
2832 |
+ finally: |
2833 |
+ playground.debug = False |
2834 |
+ playground.cleanup() |
2835 |
diff --git a/pym/portage/tests/resolver/soname/test_slot_conflict_update.py b/pym/portage/tests/resolver/soname/test_slot_conflict_update.py |
2836 |
new file mode 100644 |
2837 |
index 0000000..c607496 |
2838 |
--- /dev/null |
2839 |
+++ b/pym/portage/tests/resolver/soname/test_slot_conflict_update.py |
2840 |
@@ -0,0 +1,117 @@ |
2841 |
+# Copyright 2015 Gentoo Foundation |
2842 |
+# Distributed under the terms of the GNU General Public License v2 |
2843 |
+ |
2844 |
+from portage.tests import TestCase |
2845 |
+from portage.tests.resolver.ResolverPlayground import ( |
2846 |
+ ResolverPlayground, ResolverPlaygroundTestCase) |
2847 |
+ |
2848 |
+class SonameSlotConflictUpdateTestCase(TestCase): |
2849 |
+ |
2850 |
+ def testSonameSlotConflictUpdate(self): |
2851 |
+ |
2852 |
+ binpkgs = { |
2853 |
+ |
2854 |
+ "app-text/podofo-0.9.2" : { |
2855 |
+ "RDEPEND" : "dev-util/boost-build", |
2856 |
+ }, |
2857 |
+ |
2858 |
+ "dev-cpp/libcmis-0.3.1" : { |
2859 |
+ "DEPEND": "dev-libs/boost", |
2860 |
+ "RDEPEND": "dev-libs/boost", |
2861 |
+ "REQUIRES": "x86_32: libboost-1.53.so", |
2862 |
+ }, |
2863 |
+ |
2864 |
+ "dev-libs/boost-1.53.0" : { |
2865 |
+ "PROVIDES": "x86_32: libboost-1.53.so", |
2866 |
+ "RDEPEND" : "=dev-util/boost-build-1.53.0", |
2867 |
+ }, |
2868 |
+ |
2869 |
+ "dev-libs/boost-1.52.0" : { |
2870 |
+ "PROVIDES": "x86_32: libboost-1.52.so", |
2871 |
+ "RDEPEND" : "=dev-util/boost-build-1.52.0", |
2872 |
+ }, |
2873 |
+ |
2874 |
+ "dev-util/boost-build-1.53.0" : { |
2875 |
+ }, |
2876 |
+ |
2877 |
+ "dev-util/boost-build-1.52.0" : { |
2878 |
+ }, |
2879 |
+ |
2880 |
+ |
2881 |
+ } |
2882 |
+ |
2883 |
+ installed = { |
2884 |
+ |
2885 |
+ "app-text/podofo-0.9.2" : { |
2886 |
+ "RDEPEND" : "dev-util/boost-build", |
2887 |
+ }, |
2888 |
+ |
2889 |
+ "dev-cpp/libcmis-0.3.1" : { |
2890 |
+ "DEPEND": "dev-libs/boost", |
2891 |
+ "RDEPEND": "dev-libs/boost", |
2892 |
+ "REQUIRES": "x86_32: libboost-1.52.so", |
2893 |
+ }, |
2894 |
+ |
2895 |
+ "dev-util/boost-build-1.52.0" : { |
2896 |
+ }, |
2897 |
+ |
2898 |
+ "dev-libs/boost-1.52.0" : { |
2899 |
+ "PROVIDES": "x86_32: libboost-1.52.so", |
2900 |
+ "RDEPEND" : "=dev-util/boost-build-1.52.0", |
2901 |
+ }, |
2902 |
+ |
2903 |
+ } |
2904 |
+ |
2905 |
+ world = [ |
2906 |
+ "dev-cpp/libcmis", |
2907 |
+ "dev-libs/boost", |
2908 |
+ "app-text/podofo", |
2909 |
+ ] |
2910 |
+ |
2911 |
+ test_cases = ( |
2912 |
+ |
2913 |
+ ResolverPlaygroundTestCase( |
2914 |
+ world, |
2915 |
+ all_permutations = True, |
2916 |
+ options = { |
2917 |
+ "--deep": True, |
2918 |
+ "--ignore-soname-deps": "n", |
2919 |
+ "--update": True, |
2920 |
+ "--usepkgonly": True, |
2921 |
+ }, |
2922 |
+ success = True, |
2923 |
+ mergelist = [ |
2924 |
+ '[binary]dev-util/boost-build-1.53.0', |
2925 |
+ '[binary]dev-libs/boost-1.53.0', |
2926 |
+ '[binary]dev-cpp/libcmis-0.3.1' |
2927 |
+ ] |
2928 |
+ ), |
2929 |
+ |
2930 |
+ ResolverPlaygroundTestCase( |
2931 |
+ world, |
2932 |
+ all_permutations = True, |
2933 |
+ options = { |
2934 |
+ "--deep": True, |
2935 |
+ "--ignore-soname-deps": "y", |
2936 |
+ "--update": True, |
2937 |
+ "--usepkgonly": True, |
2938 |
+ }, |
2939 |
+ success = True, |
2940 |
+ mergelist = [ |
2941 |
+ '[binary]dev-util/boost-build-1.53.0', |
2942 |
+ '[binary]dev-libs/boost-1.53.0', |
2943 |
+ ] |
2944 |
+ ), |
2945 |
+ |
2946 |
+ ) |
2947 |
+ |
2948 |
+ playground = ResolverPlayground(binpkgs=binpkgs, |
2949 |
+ installed=installed, world=world, debug=False) |
2950 |
+ try: |
2951 |
+ for test_case in test_cases: |
2952 |
+ playground.run_TestCase(test_case) |
2953 |
+ self.assertEqual(test_case.test_success, |
2954 |
+ True, test_case.fail_msg) |
2955 |
+ finally: |
2956 |
+ playground.debug = False |
2957 |
+ playground.cleanup() |
2958 |
diff --git a/pym/portage/tests/resolver/soname/test_soname_provided.py b/pym/portage/tests/resolver/soname/test_soname_provided.py |
2959 |
new file mode 100644 |
2960 |
index 0000000..162da47 |
2961 |
--- /dev/null |
2962 |
+++ b/pym/portage/tests/resolver/soname/test_soname_provided.py |
2963 |
@@ -0,0 +1,78 @@ |
2964 |
+# Copyright 2015 Gentoo Foundation |
2965 |
+# Distributed under the terms of the GNU General Public License v2 |
2966 |
+ |
2967 |
+from portage.tests import TestCase |
2968 |
+from portage.tests.resolver.ResolverPlayground import ( |
2969 |
+ ResolverPlayground, ResolverPlaygroundTestCase) |
2970 |
+ |
2971 |
+class SonameProvidedTestCase(TestCase): |
2972 |
+ |
2973 |
+ def testSonameProvided(self): |
2974 |
+ |
2975 |
+ binpkgs = { |
2976 |
+ "app-misc/A-1" : { |
2977 |
+ "EAPI": "5", |
2978 |
+ "PROVIDES": "x86_32: libA.so.1", |
2979 |
+ }, |
2980 |
+ "app-misc/B-1" : { |
2981 |
+ "DEPEND": "app-misc/A", |
2982 |
+ "RDEPEND": "app-misc/A", |
2983 |
+ "REQUIRES": "x86_32: libA.so.2", |
2984 |
+ }, |
2985 |
+ "app-misc/B-0" : { |
2986 |
+ "DEPEND": "app-misc/A", |
2987 |
+ "RDEPEND": "app-misc/A", |
2988 |
+ "REQUIRES": "x86_32: libA.so.1", |
2989 |
+ }, |
2990 |
+ } |
2991 |
+ |
2992 |
+ installed = { |
2993 |
+ "app-misc/A-1" : { |
2994 |
+ "EAPI": "5", |
2995 |
+ "PROVIDES": "x86_32: libA.so.1", |
2996 |
+ }, |
2997 |
+ |
2998 |
+ "app-misc/B-0" : { |
2999 |
+ "DEPEND": "app-misc/A", |
3000 |
+ "RDEPEND": "app-misc/A", |
3001 |
+ "REQUIRES": "x86_32: libA.so.1", |
3002 |
+ }, |
3003 |
+ } |
3004 |
+ |
3005 |
+ world = ["app-misc/B"] |
3006 |
+ |
3007 |
+ profile = { |
3008 |
+ "soname.provided": ( |
3009 |
+ "x86_32 libA.so.2", |
3010 |
+ ), |
3011 |
+ } |
3012 |
+ |
3013 |
+ test_cases = ( |
3014 |
+ |
3015 |
+ # Allow update due to soname dependency satisfied by |
3016 |
+ # soname.provided. |
3017 |
+ ResolverPlaygroundTestCase( |
3018 |
+ ["@world"], |
3019 |
+ options = { |
3020 |
+ "--deep": True, |
3021 |
+ "--ignore-soname-deps": "n", |
3022 |
+ "--update": True, |
3023 |
+ "--usepkgonly": True, |
3024 |
+ }, |
3025 |
+ success = True, |
3026 |
+ mergelist = ["[binary]app-misc/B-1"], |
3027 |
+ ), |
3028 |
+ |
3029 |
+ ) |
3030 |
+ |
3031 |
+ playground = ResolverPlayground(binpkgs=binpkgs, debug=False, |
3032 |
+ profile=profile, installed=installed, world=world) |
3033 |
+ try: |
3034 |
+ for test_case in test_cases: |
3035 |
+ playground.run_TestCase(test_case) |
3036 |
+ self.assertEqual( |
3037 |
+ test_case.test_success, True, test_case.fail_msg) |
3038 |
+ finally: |
3039 |
+ # Disable debug so that cleanup works. |
3040 |
+ playground.debug = False |
3041 |
+ playground.cleanup() |
3042 |
diff --git a/pym/portage/tests/resolver/soname/test_unsatisfiable.py b/pym/portage/tests/resolver/soname/test_unsatisfiable.py |
3043 |
new file mode 100644 |
3044 |
index 0000000..039a9df |
3045 |
--- /dev/null |
3046 |
+++ b/pym/portage/tests/resolver/soname/test_unsatisfiable.py |
3047 |
@@ -0,0 +1,71 @@ |
3048 |
+# Copyright 2015 Gentoo Foundation |
3049 |
+# Distributed under the terms of the GNU General Public License v2 |
3050 |
+ |
3051 |
+from portage.tests import TestCase |
3052 |
+from portage.tests.resolver.ResolverPlayground import ( |
3053 |
+ ResolverPlayground, ResolverPlaygroundTestCase) |
3054 |
+ |
3055 |
+class SonameUnsatisfiableTestCase(TestCase): |
3056 |
+ |
3057 |
+ def testSonameUnsatisfiable(self): |
3058 |
+ |
3059 |
+ binpkgs = { |
3060 |
+ "app-misc/A-1" : { |
3061 |
+ "EAPI": "5", |
3062 |
+ "PROVIDES": "x86_32: libA.so.1", |
3063 |
+ }, |
3064 |
+ "app-misc/B-1" : { |
3065 |
+ "DEPEND": "app-misc/A", |
3066 |
+ "RDEPEND": "app-misc/A", |
3067 |
+ "REQUIRES": "x86_32: libA.so.2", |
3068 |
+ }, |
3069 |
+ "app-misc/B-0" : { |
3070 |
+ "DEPEND": "app-misc/A", |
3071 |
+ "RDEPEND": "app-misc/A", |
3072 |
+ "REQUIRES": "x86_32: libA.so.1", |
3073 |
+ }, |
3074 |
+ } |
3075 |
+ |
3076 |
+ installed = { |
3077 |
+ "app-misc/A-1" : { |
3078 |
+ "EAPI": "5", |
3079 |
+ "PROVIDES": "x86_32: libA.so.1", |
3080 |
+ }, |
3081 |
+ |
3082 |
+ "app-misc/B-0" : { |
3083 |
+ "DEPEND": "app-misc/A", |
3084 |
+ "RDEPEND": "app-misc/A", |
3085 |
+ "REQUIRES": "x86_32: libA.so.1", |
3086 |
+ }, |
3087 |
+ } |
3088 |
+ |
3089 |
+ world = ["app-misc/B"] |
3090 |
+ |
3091 |
+ test_cases = ( |
3092 |
+ |
3093 |
+ # Skip update due to unsatisfied soname dependency. |
3094 |
+ ResolverPlaygroundTestCase( |
3095 |
+ ["@world"], |
3096 |
+ options = { |
3097 |
+ "--deep": True, |
3098 |
+ "--ignore-soname-deps": "n", |
3099 |
+ "--update": True, |
3100 |
+ "--usepkgonly": True, |
3101 |
+ }, |
3102 |
+ success = True, |
3103 |
+ mergelist = [], |
3104 |
+ ), |
3105 |
+ |
3106 |
+ ) |
3107 |
+ |
3108 |
+ playground = ResolverPlayground(binpkgs=binpkgs, debug=False, |
3109 |
+ installed=installed, world=world) |
3110 |
+ try: |
3111 |
+ for test_case in test_cases: |
3112 |
+ playground.run_TestCase(test_case) |
3113 |
+ self.assertEqual( |
3114 |
+ test_case.test_success, True, test_case.fail_msg) |
3115 |
+ finally: |
3116 |
+ # Disable debug so that cleanup works. |
3117 |
+ playground.debug = False |
3118 |
+ playground.cleanup() |
3119 |
diff --git a/pym/portage/tests/resolver/soname/test_unsatisfied.py b/pym/portage/tests/resolver/soname/test_unsatisfied.py |
3120 |
new file mode 100644 |
3121 |
index 0000000..27cdcc4 |
3122 |
--- /dev/null |
3123 |
+++ b/pym/portage/tests/resolver/soname/test_unsatisfied.py |
3124 |
@@ -0,0 +1,87 @@ |
3125 |
+# Copyright 2015 Gentoo Foundation |
3126 |
+# Distributed under the terms of the GNU General Public License v2 |
3127 |
+ |
3128 |
+from portage.tests import TestCase |
3129 |
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground, |
3130 |
+ ResolverPlaygroundTestCase) |
3131 |
+ |
3132 |
+class SonameUnsatisfiedTestCase(TestCase): |
3133 |
+ |
3134 |
+ def testSonameUnsatisfied(self): |
3135 |
+ |
3136 |
+ binpkgs = { |
3137 |
+ "app-misc/A-1" : { |
3138 |
+ "EAPI": "5", |
3139 |
+ "PROVIDES": "x86_32: libA.so.1", |
3140 |
+ }, |
3141 |
+ "app-misc/A-2" : { |
3142 |
+ "EAPI": "5", |
3143 |
+ "PROVIDES": "x86_32: libA.so.2", |
3144 |
+ }, |
3145 |
+ "app-misc/B-0" : { |
3146 |
+ "DEPEND": "app-misc/A", |
3147 |
+ "RDEPEND": "app-misc/A", |
3148 |
+ "REQUIRES": "x86_32: libA.so.2", |
3149 |
+ } |
3150 |
+ } |
3151 |
+ |
3152 |
+ installed = { |
3153 |
+ "app-misc/A-2" : { |
3154 |
+ "EAPI": "5", |
3155 |
+ "PROVIDES": "x86_32: libA.so.2", |
3156 |
+ }, |
3157 |
+ |
3158 |
+ "app-misc/B-0" : { |
3159 |
+ "DEPEND": "app-misc/A", |
3160 |
+ "RDEPEND": "app-misc/A", |
3161 |
+ "REQUIRES": "x86_32: libA.so.1", |
3162 |
+ } |
3163 |
+ } |
3164 |
+ |
3165 |
+ world = ["app-misc/B"] |
3166 |
+ |
3167 |
+ test_cases = ( |
3168 |
+ |
3169 |
+ # Demonstrate bug #439694, where a broken |
3170 |
+ # soname dependency needs to trigger a reinstall. |
3171 |
+ ResolverPlaygroundTestCase( |
3172 |
+ ["@world"], |
3173 |
+ options = { |
3174 |
+ "--deep": True, |
3175 |
+ "--ignore-soname-deps": "n", |
3176 |
+ "--update": True, |
3177 |
+ "--usepkgonly": True, |
3178 |
+ }, |
3179 |
+ success = True, |
3180 |
+ mergelist = [ |
3181 |
+ "[binary]app-misc/B-0" |
3182 |
+ ] |
3183 |
+ ), |
3184 |
+ |
3185 |
+ # This doesn't trigger a reinstall, since there's no version |
3186 |
+ # change to trigger complete graph mode, and initially |
3187 |
+ # unsatisfied deps are ignored in complete graph mode anyway. |
3188 |
+ ResolverPlaygroundTestCase( |
3189 |
+ ["app-misc/A"], |
3190 |
+ options = { |
3191 |
+ "--ignore-soname-deps": "n", |
3192 |
+ "--oneshot": True, |
3193 |
+ "--usepkgonly": True, |
3194 |
+ }, |
3195 |
+ success = True, |
3196 |
+ mergelist = [ |
3197 |
+ "[binary]app-misc/A-2" |
3198 |
+ ] |
3199 |
+ ), |
3200 |
+ ) |
3201 |
+ |
3202 |
+ playground = ResolverPlayground(binpkgs=binpkgs, debug=False, |
3203 |
+ installed=installed, world=world) |
3204 |
+ try: |
3205 |
+ for test_case in test_cases: |
3206 |
+ playground.run_TestCase(test_case) |
3207 |
+ self.assertEqual(test_case.test_success, True, test_case.fail_msg) |
3208 |
+ finally: |
3209 |
+ # Disable debug so that cleanup works. |
3210 |
+ playground.debug = False |
3211 |
+ playground.cleanup() |
3212 |
diff --git a/pym/portage/tests/resolver/test_package_tracker.py b/pym/portage/tests/resolver/test_package_tracker.py |
3213 |
index 8fa3513..468c3d8 100644 |
3214 |
--- a/pym/portage/tests/resolver/test_package_tracker.py |
3215 |
+++ b/pym/portage/tests/resolver/test_package_tracker.py |
3216 |
@@ -116,11 +116,11 @@ class PackageTrackerTestCase(TestCase): |
3217 |
if pkg.root == "/" and pkg.cp == x_atom: |
3218 |
self.assertTrue(pkg in matches) |
3219 |
self.assertTrue(not dbapi.cp_list(y_atom)) |
3220 |
- matches = dbapi.match(x_atom) |
3221 |
+ matches = dbapi.match(Atom(x_atom)) |
3222 |
for pkg in pkgs: |
3223 |
if pkg.root == "/" and pkg.cp == x_atom: |
3224 |
self.assertTrue(pkg in matches) |
3225 |
- self.assertTrue(not dbapi.match(y_atom)) |
3226 |
+ self.assertTrue(not dbapi.match(Atom(y_atom))) |
3227 |
|
3228 |
check_dbapi([]) |
3229 |
|
3230 |
-- |
3231 |
2.0.5 |