1 |
Add some heuristics to handle slot conflicts triggered by interaction |
2 |
of slot-operator dependencies with dependencies like those of labgl: |
3 |
|
4 |
ocaml:= || ( labltk <ocaml-4.02 ) |
5 |
|
6 |
The new heuristics involve some behavior modifications in the depgraph |
7 |
_solve_non_slot_operator_slot_conflicts method and in dep_zapdeps. The |
8 |
dep_zapdeps changes affect the behavior of _select_atoms_probe calls |
9 |
in the depgraph _slot_operator_update_probe method. |
10 |
|
11 |
X-Gentoo-Bug: 531656 |
12 |
X-Gentoo-Bug-URL: https://bugs.gentoo.org/show_bug.cgi?id=531656 |
13 |
--- |
14 |
pym/_emerge/depgraph.py | 30 ++++++++++- |
15 |
pym/portage/dep/dep_check.py | 14 +++++ |
16 |
pym/portage/tests/resolver/test_or_choices.py | 78 +++++++++++++++++++++++++++ |
17 |
3 files changed, 121 insertions(+), 1 deletion(-) |
18 |
|
19 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
20 |
index 28abea4..b067a6d 100644 |
21 |
--- a/pym/_emerge/depgraph.py |
22 |
+++ b/pym/_emerge/depgraph.py |
23 |
@@ -451,6 +451,7 @@ class _dynamic_depgraph_config(object): |
24 |
self._graph_trees[myroot]["graph_db"] = graph_tree.dbapi |
25 |
self._graph_trees[myroot]["graph"] = self.digraph |
26 |
self._graph_trees[myroot]["want_update_pkg"] = depgraph._want_update_pkg |
27 |
+ self._graph_trees[myroot]["downgrade_probe"] = depgraph._downgrade_probe |
28 |
def filtered_tree(): |
29 |
pass |
30 |
filtered_tree.dbapi = _dep_check_composite_db(depgraph, myroot) |
31 |
@@ -478,6 +479,7 @@ class _dynamic_depgraph_config(object): |
32 |
self._filtered_trees[myroot]["vartree"] = \ |
33 |
depgraph._frozen_config.trees[myroot]["vartree"] |
34 |
self._filtered_trees[myroot]["want_update_pkg"] = depgraph._want_update_pkg |
35 |
+ self._filtered_trees[myroot]["downgrade_probe"] = depgraph._downgrade_probe |
36 |
|
37 |
dbs = [] |
38 |
# (db, pkg_type, built, installed, db_keys) |
39 |
@@ -1144,7 +1146,13 @@ class depgraph(object): |
40 |
writemsg_level(" pkg: %s\n" % pkg, level=logging.DEBUG, noiselevel=-1) |
41 |
|
42 |
all_parent_atoms = set() |
43 |
+ highest_pkg = None |
44 |
+ inst_pkg = None |
45 |
for pkg in conflict: |
46 |
+ if pkg.installed: |
47 |
+ inst_pkg = pkg |
48 |
+ if highest_pkg is None or highest_pkg < pkg: |
49 |
+ highest_pkg = pkg |
50 |
all_parent_atoms.update( |
51 |
self._dynamic_config._parent_atoms.get(pkg, [])) |
52 |
|
53 |
@@ -1167,6 +1175,15 @@ class depgraph(object): |
54 |
|
55 |
matched = [] |
56 |
for pkg in conflict: |
57 |
+ if (pkg is highest_pkg and |
58 |
+ not highest_pkg.installed and |
59 |
+ inst_pkg is not None and |
60 |
+ inst_pkg.sub_slot != highest_pkg.sub_slot and |
61 |
+ not self._downgrade_probe(highest_pkg)): |
62 |
+ # If an upgrade is desired, force the highest |
63 |
+ # version into the graph (bug #531656). |
64 |
+ non_matching_forced.add(highest_pkg) |
65 |
+ |
66 |
if atom_set.findAtomForPackage(pkg, \ |
67 |
modified_use=self._pkg_use_enabled(pkg)) and \ |
68 |
not (is_arg_parent and pkg.installed): |
69 |
@@ -1220,14 +1237,20 @@ class depgraph(object): |
70 |
# the packages in the tuple. This way we don't have |
71 |
# to choose one. |
72 |
unexplored_tuples = set() |
73 |
+ explored_nodes = set() |
74 |
|
75 |
while unexplored: |
76 |
# Handle all unexplored packages. |
77 |
while unexplored: |
78 |
node = unexplored.pop() |
79 |
for child in conflict_graph.child_nodes(node): |
80 |
- if child in forced: |
81 |
+ # Don't explore a node more than once, in order |
82 |
+ # to avoid infinite recursion. The forced set |
83 |
+ # cannot be used for this purpose, since it can |
84 |
+ # contain unexplored nodes from non_matching_forced. |
85 |
+ if child in explored_nodes: |
86 |
continue |
87 |
+ explored_nodes.add(child) |
88 |
forced.add(child) |
89 |
if isinstance(child, Package): |
90 |
unexplored.add(child) |
91 |
@@ -8817,6 +8840,11 @@ def _backtrack_depgraph(settings, trees, myopts, myparams, myaction, myfiles, sp |
92 |
mydepgraph.display_problems() |
93 |
|
94 |
backtrack_parameters = backtracker.get() |
95 |
+ if debug and backtrack_parameters.runtime_pkg_mask: |
96 |
+ writemsg_level( |
97 |
+ "\n\nruntime_pkg_mask: %s \n\n" % |
98 |
+ backtrack_parameters.runtime_pkg_mask, |
99 |
+ noiselevel=-1, level=logging.DEBUG) |
100 |
|
101 |
mydepgraph = depgraph(settings, trees, myopts, myparams, spinner, |
102 |
frozen_config=frozen_config, |
103 |
diff --git a/pym/portage/dep/dep_check.py b/pym/portage/dep/dep_check.py |
104 |
index ccdda59..c40382d 100644 |
105 |
--- a/pym/portage/dep/dep_check.py |
106 |
+++ b/pym/portage/dep/dep_check.py |
107 |
@@ -319,6 +319,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): |
108 |
graph = trees[myroot].get("graph") |
109 |
pkg_use_enabled = trees[myroot].get("pkg_use_enabled") |
110 |
want_update_pkg = trees[myroot].get("want_update_pkg") |
111 |
+ downgrade_probe = trees[myroot].get("downgrade_probe") |
112 |
vardb = None |
113 |
if "vartree" in trees[myroot]: |
114 |
vardb = trees[myroot]["vartree"].dbapi |
115 |
@@ -351,6 +352,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): |
116 |
all_available = True |
117 |
all_use_satisfied = True |
118 |
all_use_unmasked = True |
119 |
+ conflict_downgrade = False |
120 |
slot_map = {} |
121 |
cp_map = {} |
122 |
for atom in atoms: |
123 |
@@ -367,6 +369,16 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): |
124 |
all_use_satisfied = False |
125 |
break |
126 |
|
127 |
+ if graph_db is not None and downgrade_probe is not None: |
128 |
+ slot_matches = graph_db.match_pkgs(avail_slot) |
129 |
+ if (len(slot_matches) > 1 and |
130 |
+ avail_pkg < slot_matches[-1] and |
131 |
+ not downgrade_probe(avail_pkg)): |
132 |
+ # If a downgrade is not desirable, then avoid a |
133 |
+ # choice that pulls in a lower version involved |
134 |
+ # in a slot conflict (bug #531656). |
135 |
+ conflict_downgrade = True |
136 |
+ |
137 |
if atom.use: |
138 |
avail_pkg_use = mydbapi_match_pkgs(atom) |
139 |
if not avail_pkg_use: |
140 |
@@ -450,6 +462,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None): |
141 |
unsat_use_installed.append(this_choice) |
142 |
else: |
143 |
unsat_use_non_installed.append(this_choice) |
144 |
+ elif conflict_downgrade: |
145 |
+ other.append(this_choice) |
146 |
else: |
147 |
all_in_graph = True |
148 |
for atom in atoms: |
149 |
diff --git a/pym/portage/tests/resolver/test_or_choices.py b/pym/portage/tests/resolver/test_or_choices.py |
150 |
index 4aae0b2..3c02dfd 100644 |
151 |
--- a/pym/portage/tests/resolver/test_or_choices.py |
152 |
+++ b/pym/portage/tests/resolver/test_or_choices.py |
153 |
@@ -262,3 +262,81 @@ class OrChoicesTestCase(TestCase): |
154 |
test_case.fail_msg) |
155 |
finally: |
156 |
playground.cleanup() |
157 |
+ |
158 |
+ def testConflictMissedUpdate(self): |
159 |
+ |
160 |
+ ebuilds = { |
161 |
+ "dev-lang/ocaml-4.02.1" : { |
162 |
+ "EAPI": "5", |
163 |
+ "SLOT": "0/4.02.1", |
164 |
+ }, |
165 |
+ |
166 |
+ "dev-lang/ocaml-4.01.0" : { |
167 |
+ "EAPI": "5", |
168 |
+ "SLOT": "0/4.01.0", |
169 |
+ }, |
170 |
+ |
171 |
+ "dev-ml/lablgl-1.05" : { |
172 |
+ "EAPI": "5", |
173 |
+ "DEPEND": (">=dev-lang/ocaml-3.10.2:= " |
174 |
+ "|| ( dev-ml/labltk:= <dev-lang/ocaml-4.02 )"), |
175 |
+ "RDEPEND": (">=dev-lang/ocaml-3.10.2:= " |
176 |
+ "|| ( dev-ml/labltk:= <dev-lang/ocaml-4.02 )"), |
177 |
+ }, |
178 |
+ |
179 |
+ "dev-ml/labltk-8.06.0" : { |
180 |
+ "EAPI": "5", |
181 |
+ "SLOT": "0/8.06.0", |
182 |
+ "DEPEND": ">=dev-lang/ocaml-4.02:=", |
183 |
+ "RDEPEND": ">=dev-lang/ocaml-4.02:=", |
184 |
+ }, |
185 |
+ } |
186 |
+ |
187 |
+ installed = { |
188 |
+ "dev-lang/ocaml-4.01.0" : { |
189 |
+ "EAPI": "5", |
190 |
+ "SLOT": "0/4.01.0", |
191 |
+ }, |
192 |
+ |
193 |
+ "dev-ml/lablgl-1.05" : { |
194 |
+ "EAPI": "5", |
195 |
+ "DEPEND": (">=dev-lang/ocaml-3.10.2:0/4.01.0= " |
196 |
+ "|| ( dev-ml/labltk:= <dev-lang/ocaml-4.02 )"), |
197 |
+ "RDEPEND": (">=dev-lang/ocaml-3.10.2:0/4.01.0= " |
198 |
+ "|| ( dev-ml/labltk:= <dev-lang/ocaml-4.02 )"), |
199 |
+ }, |
200 |
+ } |
201 |
+ |
202 |
+ world = ( |
203 |
+ "dev-lang/ocaml", |
204 |
+ "dev-ml/lablgl", |
205 |
+ ) |
206 |
+ |
207 |
+ test_cases = ( |
208 |
+ |
209 |
+ # bug #531656: If an ocaml update is desirable, |
210 |
+ # then we need to pull in dev-ml/labltk. |
211 |
+ ResolverPlaygroundTestCase( |
212 |
+ ["@world"], |
213 |
+ options = {"--update": True, "--deep": True}, |
214 |
+ success = True, |
215 |
+ mergelist = [ |
216 |
+ "dev-lang/ocaml-4.02.1", |
217 |
+ "dev-ml/labltk-8.06.0", |
218 |
+ "dev-ml/lablgl-1.05", |
219 |
+ ] |
220 |
+ ), |
221 |
+ |
222 |
+ ) |
223 |
+ |
224 |
+ playground = ResolverPlayground(debug=False, |
225 |
+ ebuilds=ebuilds, installed=installed, world=world) |
226 |
+ try: |
227 |
+ for test_case in test_cases: |
228 |
+ playground.run_TestCase(test_case) |
229 |
+ self.assertEqual(test_case.test_success, True, |
230 |
+ test_case.fail_msg) |
231 |
+ finally: |
232 |
+ # Disable debug so that cleanup works. |
233 |
+ playground.debug = False |
234 |
+ playground.cleanup() |
235 |
-- |
236 |
2.0.5 |