1 |
Fix check_reverse_dependencies to ignore dependencies of parent packages |
2 |
that could be uninstalled in order to solve a blocker conflict. This case |
3 |
is similar to the one from bug 584626, except that the relevant parent |
4 |
package is in an older slot which is blocked by a newer slot. In this |
5 |
case, the _upgrade_available method returns False, because the package |
6 |
in the older slot is the highest version version available for its |
7 |
slot. Therefore, a new _in_blocker_conflict method is needed to detect |
8 |
parent packages that cold be uninstalled. The included unit test fails |
9 |
without this fix. |
10 |
|
11 |
Since the _in_blocker_conflict method requires information that is |
12 |
collected by the _validate_blockers method, the _validate_blockers |
13 |
method now has to be called before the _process_slot_conflict and |
14 |
_slot_operator_trigger_reinstalls methods. |
15 |
|
16 |
X-Gentoo-bug: 612772 |
17 |
X-Gentoo-bug-url: https://bugs.gentoo.org/show_bug.cgi?id=612772 |
18 |
--- |
19 |
pym/_emerge/depgraph.py | 59 ++++++++--- |
20 |
.../resolver/test_slot_operator_exclusive_slots.py | 109 +++++++++++++++++++++ |
21 |
2 files changed, 155 insertions(+), 13 deletions(-) |
22 |
create mode 100644 pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py |
23 |
|
24 |
diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py |
25 |
index 1379b05..96b6d5f 100644 |
26 |
--- a/pym/_emerge/depgraph.py |
27 |
+++ b/pym/_emerge/depgraph.py |
28 |
@@ -387,7 +387,10 @@ class _dynamic_depgraph_config(object): |
29 |
# Contains only unsolvable Package -> Blocker edges |
30 |
self._unsolvable_blockers = digraph() |
31 |
# Contains all Blocker -> Blocked Package edges |
32 |
- self._blocked_pkgs = digraph() |
33 |
+ # Do not initialized this until the depgraph _validate_blockers |
34 |
+ # method is called, so that the _in_blocker_conflict method can |
35 |
+ # assert that _validate_blockers has been called first. |
36 |
+ self._blocked_pkgs = None |
37 |
# Contains world packages that have been protected from |
38 |
# uninstallation but may not have been added to the graph |
39 |
# if the graph is not complete yet. |
40 |
@@ -1466,9 +1469,22 @@ class depgraph(object): |
41 |
|
42 |
self._solve_non_slot_operator_slot_conflicts() |
43 |
|
44 |
+ if not self._validate_blockers(): |
45 |
+ # Blockers don't trigger the _skip_restart flag, since |
46 |
+ # backtracking may solve blockers when it solves slot |
47 |
+ # conflicts (or by blind luck). |
48 |
+ raise self._unknown_internal_error() |
49 |
+ |
50 |
+ # Both _process_slot_conflict and _slot_operator_trigger_reinstalls |
51 |
+ # can call _slot_operator_update_probe, which requires that |
52 |
+ # self._dynamic_config._blocked_pkgs has been initialized by a |
53 |
+ # call to the _validate_blockers method. |
54 |
for conflict in self._dynamic_config._package_tracker.slot_conflicts(): |
55 |
self._process_slot_conflict(conflict) |
56 |
|
57 |
+ if self._dynamic_config._allow_backtracking: |
58 |
+ self._slot_operator_trigger_reinstalls() |
59 |
+ |
60 |
def _process_slot_conflict(self, conflict): |
61 |
""" |
62 |
Process slot conflict data to identify specific atoms which |
63 |
@@ -1829,9 +1845,12 @@ class depgraph(object): |
64 |
not self._frozen_config.excluded_pkgs. |
65 |
findAtomForPackage(parent, |
66 |
modified_use=self._pkg_use_enabled(parent)) and |
67 |
- self._upgrade_available(parent)): |
68 |
+ (self._upgrade_available(parent) or |
69 |
+ (parent.installed and self._in_blocker_conflict(parent)))): |
70 |
# This parent may be irrelevant, since an |
71 |
- # update is available (see bug 584626). |
72 |
+ # update is available (see bug 584626), or |
73 |
+ # it could be uninstalled in order to solve |
74 |
+ # a blocker conflict (bug 612772). |
75 |
continue |
76 |
|
77 |
atom_set = InternalPackageSet(initial_atoms=(atom,), |
78 |
@@ -2125,6 +2144,24 @@ class depgraph(object): |
79 |
|
80 |
self._dynamic_config._need_restart = True |
81 |
|
82 |
+ def _in_blocker_conflict(self, pkg): |
83 |
+ """ |
84 |
+ Check if pkg is involved in a blocker conflict. This method |
85 |
+ only works after the _validate_blockers method has been called. |
86 |
+ """ |
87 |
+ |
88 |
+ if self._dynamic_config._blocked_pkgs is None: |
89 |
+ raise AssertionError( |
90 |
+ '_in_blocker_conflict called before _validate_blockers') |
91 |
+ |
92 |
+ if pkg in self._dynamic_config._blocked_pkgs: |
93 |
+ return True |
94 |
+ |
95 |
+ if pkg in self._dynamic_config._blocker_parents: |
96 |
+ return True |
97 |
+ |
98 |
+ return False |
99 |
+ |
100 |
def _upgrade_available(self, pkg): |
101 |
""" |
102 |
Detect cases where an upgrade of the given package is available |
103 |
@@ -2925,7 +2962,8 @@ class depgraph(object): |
104 |
self._dynamic_config._blocker_parents.discard(pkg) |
105 |
self._dynamic_config._irrelevant_blockers.discard(pkg) |
106 |
self._dynamic_config._unsolvable_blockers.discard(pkg) |
107 |
- self._dynamic_config._blocked_pkgs.discard(pkg) |
108 |
+ if self._dynamic_config._blocked_pkgs is not None: |
109 |
+ self._dynamic_config._blocked_pkgs.discard(pkg) |
110 |
self._dynamic_config._blocked_world_pkgs.pop(pkg, None) |
111 |
|
112 |
for child in children: |
113 |
@@ -6619,6 +6657,10 @@ class depgraph(object): |
114 |
installed simultaneously. Also add runtime blockers from all installed |
115 |
packages if any of them haven't been added already (bug 128809).""" |
116 |
|
117 |
+ # The _in_blocker_conflict method needs to assert that this method |
118 |
+ # has been called before it, by checking that it is not None. |
119 |
+ self._dynamic_config._blocked_pkgs = digraph() |
120 |
+ |
121 |
if "--buildpkgonly" in self._frozen_config.myopts or \ |
122 |
"--nodeps" in self._frozen_config.myopts: |
123 |
return True |
124 |
@@ -7106,15 +7148,6 @@ class depgraph(object): |
125 |
|
126 |
self._process_slot_conflicts() |
127 |
|
128 |
- if self._dynamic_config._allow_backtracking: |
129 |
- self._slot_operator_trigger_reinstalls() |
130 |
- |
131 |
- if not self._validate_blockers(): |
132 |
- # Blockers don't trigger the _skip_restart flag, since |
133 |
- # backtracking may solve blockers when it solves slot |
134 |
- # conflicts (or by blind luck). |
135 |
- raise self._unknown_internal_error() |
136 |
- |
137 |
def _serialize_tasks(self): |
138 |
|
139 |
debug = "--debug" in self._frozen_config.myopts |
140 |
diff --git a/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py b/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py |
141 |
new file mode 100644 |
142 |
index 0000000..2ab379c |
143 |
--- /dev/null |
144 |
+++ b/pym/portage/tests/resolver/test_slot_operator_exclusive_slots.py |
145 |
@@ -0,0 +1,109 @@ |
146 |
+# Copyright 2017 Gentoo Foundation |
147 |
+# Distributed under the terms of the GNU General Public License v2 |
148 |
+ |
149 |
+from portage.tests import TestCase |
150 |
+from portage.tests.resolver.ResolverPlayground import ( |
151 |
+ ResolverPlayground, |
152 |
+ ResolverPlaygroundTestCase, |
153 |
+) |
154 |
+ |
155 |
+class SlotOperatorExclusiveSlotsTestCase(TestCase): |
156 |
+ |
157 |
+ def testSlotOperatorExclusiveSlots(self): |
158 |
+ |
159 |
+ ebuilds = { |
160 |
+ |
161 |
+ "media-libs/mesa-17.0.1" : { |
162 |
+ "EAPI": "6", |
163 |
+ "SLOT": "0", |
164 |
+ "RDEPEND": "<sys-devel/llvm-5:=" |
165 |
+ }, |
166 |
+ |
167 |
+ "sys-devel/clang-4.0.0" : { |
168 |
+ "EAPI": "6", |
169 |
+ "SLOT": "4", |
170 |
+ "RDEPEND": ("~sys-devel/llvm-4.0.0:4= " |
171 |
+ "!sys-devel/llvm:0 !sys-devel/clang:0"), |
172 |
+ }, |
173 |
+ |
174 |
+ "sys-devel/clang-3.9.1-r100" : { |
175 |
+ "EAPI": "6", |
176 |
+ "SLOT": "0/3.9.1", |
177 |
+ "RDEPEND": "~sys-devel/llvm-3.9.1", |
178 |
+ }, |
179 |
+ |
180 |
+ "sys-devel/llvm-4.0.0" : { |
181 |
+ "EAPI": "6", |
182 |
+ "SLOT": "4", |
183 |
+ "RDEPEND": "!sys-devel/llvm:0", |
184 |
+ }, |
185 |
+ |
186 |
+ "sys-devel/llvm-3.9.1" : { |
187 |
+ "EAPI": "6", |
188 |
+ "SLOT": "0/3.91", |
189 |
+ "RDEPEND": "!sys-devel/llvm:0", |
190 |
+ "PDEPEND": "=sys-devel/clang-3.9.1-r100", |
191 |
+ }, |
192 |
+ |
193 |
+ } |
194 |
+ |
195 |
+ installed = { |
196 |
+ |
197 |
+ "media-libs/mesa-17.0.1" : { |
198 |
+ "EAPI": "6", |
199 |
+ "SLOT": "0", |
200 |
+ "RDEPEND": "<sys-devel/llvm-5:0/3.9.1=" |
201 |
+ }, |
202 |
+ |
203 |
+ "sys-devel/clang-3.9.1-r100" : { |
204 |
+ "EAPI": "6", |
205 |
+ "SLOT": "0/3.9.1", |
206 |
+ "RDEPEND": "~sys-devel/llvm-3.9.1", |
207 |
+ }, |
208 |
+ |
209 |
+ "sys-devel/llvm-3.9.1" : { |
210 |
+ "EAPI": "6", |
211 |
+ "SLOT": "0/3.9.1", |
212 |
+ "RDEPEND": "!sys-devel/llvm:0", |
213 |
+ "PDEPEND": "=sys-devel/clang-3.9.1-r100", |
214 |
+ }, |
215 |
+ |
216 |
+ } |
217 |
+ |
218 |
+ world = ["sys-devel/clang", "media-libs/mesa"] |
219 |
+ |
220 |
+ test_cases = ( |
221 |
+ |
222 |
+ # Test bug #612772, where slot operator rebuilds are not |
223 |
+ # properly triggered (for things like mesa) during a |
224 |
+ # llvm:0 to llvm:4 upgrade with clang, resulting in |
225 |
+ # unsolved blockers. |
226 |
+ ResolverPlaygroundTestCase( |
227 |
+ ["@world"], |
228 |
+ options = {"--update": True, "--deep": True}, |
229 |
+ success = True, |
230 |
+ ambiguous_merge_order = True, |
231 |
+ mergelist = [ |
232 |
+ 'sys-devel/llvm-4.0.0', |
233 |
+ 'media-libs/mesa-17.0.1', |
234 |
+ ( |
235 |
+ 'sys-devel/clang-4.0.0', |
236 |
+ '[uninstall]sys-devel/llvm-3.9.1', |
237 |
+ '!sys-devel/llvm:0', |
238 |
+ '[uninstall]sys-devel/clang-3.9.1-r100', |
239 |
+ '!sys-devel/clang:0', |
240 |
+ ) |
241 |
+ ], |
242 |
+ ), |
243 |
+ |
244 |
+ ) |
245 |
+ |
246 |
+ playground = ResolverPlayground(ebuilds=ebuilds, |
247 |
+ installed=installed, world=world) |
248 |
+ try: |
249 |
+ for test_case in test_cases: |
250 |
+ playground.run_TestCase(test_case) |
251 |
+ self.assertEqual(test_case.test_success, True, |
252 |
+ test_case.fail_msg) |
253 |
+ finally: |
254 |
+ playground.cleanup() |
255 |
-- |
256 |
2.10.2 |