1 |
commit: f7d83d75c6b05a16ef07473917082dbd0cd9955c |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Sun Jan 26 01:44:14 2020 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Mon Jan 27 03:18:02 2020 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=f7d83d75 |
7 |
|
8 |
dep_zapdeps: adjust || preference for slot upgrades (bug 706278) |
9 |
|
10 |
Prefer choices that include a slot upgrade when appropriate, like for |
11 |
the || ( llvm:10 ... llvm:7 ) case reported in bug 706278. In order |
12 |
to avoid pulling in inappropriate slot upgrades, like those which |
13 |
should only be pulled in with --update and --deep, add a want_update |
14 |
flag to each choice which is True for choices that pull in a new slot |
15 |
for which an update is desirable. |
16 |
|
17 |
Mark the test case for bug 480736 as todo, since the "undesirable" |
18 |
slot upgrade which triggers a blocker conflict in this test case is |
19 |
practically indistinguishable from a desirable slot upgrade. This |
20 |
particular blocker conflict is no longer relevant, since current |
21 |
versions of media-libs/libpostproc are no longer compatible with |
22 |
any available media-video/ffmpeg slot. In order to solve this test |
23 |
case, some fancy backtracking (like for bug 382421) will be required. |
24 |
|
25 |
Bug: https://bugs.gentoo.org/706278 |
26 |
Bug: https://bugs.gentoo.org/480736 |
27 |
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org> |
28 |
|
29 |
lib/_emerge/depgraph.py | 16 ++++- |
30 |
lib/portage/dep/dep_check.py | 79 ++++++++++++---------- |
31 |
lib/portage/tests/resolver/test_or_choices.py | 9 +++ |
32 |
.../tests/resolver/test_or_upgrade_installed.py | 3 +- |
33 |
4 files changed, 67 insertions(+), 40 deletions(-) |
34 |
|
35 |
diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py |
36 |
index bf8882774..cae1c4470 100644 |
37 |
--- a/lib/_emerge/depgraph.py |
38 |
+++ b/lib/_emerge/depgraph.py |
39 |
@@ -95,6 +95,14 @@ if sys.hexversion >= 0x3000000: |
40 |
else: |
41 |
_unicode = unicode |
42 |
|
43 |
+# Exposes a depgraph interface to dep_check. |
44 |
+_dep_check_graph_interface = collections.namedtuple('_dep_check_graph_interface',( |
45 |
+ # Indicates a removal action, like depclean or prune. |
46 |
+ 'removal_action', |
47 |
+ # Checks if update is desirable for a given package. |
48 |
+ 'want_update_pkg', |
49 |
+)) |
50 |
+ |
51 |
class _scheduler_graph_config(object): |
52 |
def __init__(self, trees, pkg_cache, graph, mergelist): |
53 |
self.trees = trees |
54 |
@@ -510,6 +518,10 @@ class _dynamic_depgraph_config(object): |
55 |
soname_deps=depgraph._frozen_config.soname_deps_enabled) |
56 |
# Track missed updates caused by solved conflicts. |
57 |
self._conflict_missed_update = collections.defaultdict(dict) |
58 |
+ dep_check_iface = _dep_check_graph_interface( |
59 |
+ removal_action="remove" in myparams, |
60 |
+ want_update_pkg=depgraph._want_update_pkg, |
61 |
+ ) |
62 |
|
63 |
for myroot in depgraph._frozen_config.trees: |
64 |
self.sets[myroot] = _depgraph_sets() |
65 |
@@ -530,7 +542,7 @@ class _dynamic_depgraph_config(object): |
66 |
self._graph_trees[myroot]["vartree"] = graph_tree |
67 |
self._graph_trees[myroot]["graph_db"] = graph_tree.dbapi |
68 |
self._graph_trees[myroot]["graph"] = self.digraph |
69 |
- self._graph_trees[myroot]["want_update_pkg"] = depgraph._want_update_pkg |
70 |
+ self._graph_trees[myroot]["graph_interface"] = dep_check_iface |
71 |
self._graph_trees[myroot]["downgrade_probe"] = depgraph._downgrade_probe |
72 |
def filtered_tree(): |
73 |
pass |
74 |
@@ -558,7 +570,7 @@ class _dynamic_depgraph_config(object): |
75 |
self._filtered_trees[myroot]["graph"] = self.digraph |
76 |
self._filtered_trees[myroot]["vartree"] = \ |
77 |
depgraph._frozen_config.trees[myroot]["vartree"] |
78 |
- self._filtered_trees[myroot]["want_update_pkg"] = depgraph._want_update_pkg |
79 |
+ self._filtered_trees[myroot]["graph_interface"] = dep_check_iface |
80 |
self._filtered_trees[myroot]["downgrade_probe"] = depgraph._downgrade_probe |
81 |
|
82 |
dbs = [] |
83 |
|
84 |
diff --git a/lib/portage/dep/dep_check.py b/lib/portage/dep/dep_check.py |
85 |
index 321d961dd..a7ae2cfa4 100644 |
86 |
--- a/lib/portage/dep/dep_check.py |
87 |
+++ b/lib/portage/dep/dep_check.py |
88 |
@@ -296,7 +296,7 @@ def dep_eval(deplist): |
89 |
|
90 |
class _dep_choice(SlotObject): |
91 |
__slots__ = ('atoms', 'slot_map', 'cp_map', 'all_available', |
92 |
- 'all_installed_slots', 'new_slot_count') |
93 |
+ 'all_installed_slots', 'new_slot_count', 'want_update', 'all_in_graph') |
94 |
|
95 |
def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
96 |
minimize_slots=False): |
97 |
@@ -331,9 +331,9 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
98 |
# c) contains masked installed packages |
99 |
# d) is the first item |
100 |
|
101 |
- preferred_installed = [] |
102 |
preferred_in_graph = [] |
103 |
- preferred_any_slot = [] |
104 |
+ preferred_installed = preferred_in_graph |
105 |
+ preferred_any_slot = preferred_in_graph |
106 |
preferred_non_installed = [] |
107 |
unsat_use_in_graph = [] |
108 |
unsat_use_installed = [] |
109 |
@@ -347,8 +347,6 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
110 |
# for correct ordering in cases like || ( foo[a] foo[b] ). |
111 |
choice_bins = ( |
112 |
preferred_in_graph, |
113 |
- preferred_installed, |
114 |
- preferred_any_slot, |
115 |
preferred_non_installed, |
116 |
unsat_use_in_graph, |
117 |
unsat_use_installed, |
118 |
@@ -365,7 +363,7 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
119 |
graph_db = trees[myroot].get("graph_db") |
120 |
graph = trees[myroot].get("graph") |
121 |
pkg_use_enabled = trees[myroot].get("pkg_use_enabled") |
122 |
- want_update_pkg = trees[myroot].get("want_update_pkg") |
123 |
+ graph_interface = trees[myroot].get("graph_interface") |
124 |
downgrade_probe = trees[myroot].get("downgrade_probe") |
125 |
circular_dependency = trees[myroot].get("circular_dependency") |
126 |
vardb = None |
127 |
@@ -506,14 +504,24 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
128 |
if current_higher or (all_match_current and not all_match_previous): |
129 |
cp_map[avail_pkg.cp] = avail_pkg |
130 |
|
131 |
- new_slot_count = (len(slot_map) if graph_db is None else |
132 |
- sum(not graph_db.match_pkgs(slot_atom) for slot_atom in slot_map |
133 |
- if not slot_atom.cp.startswith("virtual/"))) |
134 |
+ want_update = False |
135 |
+ if graph_interface is None or graph_interface.removal_action: |
136 |
+ new_slot_count = len(slot_map) |
137 |
+ else: |
138 |
+ new_slot_count = 0 |
139 |
+ for slot_atom, avail_pkg in slot_map.items(): |
140 |
+ if graph_interface.want_update_pkg(parent, avail_pkg): |
141 |
+ want_update = True |
142 |
+ if (not slot_atom.cp.startswith("virtual/") |
143 |
+ and not graph_db.match_pkgs(slot_atom)): |
144 |
+ new_slot_count += 1 |
145 |
|
146 |
this_choice = _dep_choice(atoms=atoms, slot_map=slot_map, |
147 |
cp_map=cp_map, all_available=all_available, |
148 |
all_installed_slots=False, |
149 |
- new_slot_count=new_slot_count) |
150 |
+ new_slot_count=new_slot_count, |
151 |
+ all_in_graph=False, |
152 |
+ want_update=want_update) |
153 |
if all_available: |
154 |
# The "all installed" criterion is not version or slot specific. |
155 |
# If any version of a package is already in the graph then we |
156 |
@@ -567,6 +575,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
157 |
graph_db.match_pkgs(atom)): |
158 |
all_in_graph = False |
159 |
break |
160 |
+ this_choice.all_in_graph = all_in_graph |
161 |
+ |
162 |
circular_atom = None |
163 |
if not (parent is None or priority is None) and \ |
164 |
(parent.onlydeps or |
165 |
@@ -607,27 +617,8 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
166 |
elif all_installed: |
167 |
if all_installed_slots: |
168 |
preferred_installed.append(this_choice) |
169 |
- elif parent is None or want_update_pkg is None: |
170 |
- preferred_any_slot.append(this_choice) |
171 |
else: |
172 |
- # When appropriate, prefer a slot that is not |
173 |
- # installed yet for bug #478188. |
174 |
- want_update = True |
175 |
- for slot_atom, avail_pkg in slot_map.items(): |
176 |
- if avail_pkg in graph: |
177 |
- continue |
178 |
- # New-style virtuals have zero cost to install. |
179 |
- if slot_atom.startswith("virtual/") or \ |
180 |
- vardb.match(slot_atom): |
181 |
- continue |
182 |
- if not want_update_pkg(parent, avail_pkg): |
183 |
- want_update = False |
184 |
- break |
185 |
- |
186 |
- if want_update: |
187 |
- preferred_installed.append(this_choice) |
188 |
- else: |
189 |
- preferred_any_slot.append(this_choice) |
190 |
+ preferred_any_slot.append(this_choice) |
191 |
else: |
192 |
preferred_non_installed.append(this_choice) |
193 |
else: |
194 |
@@ -676,10 +667,6 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
195 |
if len(choices) < 2: |
196 |
continue |
197 |
|
198 |
- sort_keys = [] |
199 |
- # Prefer choices with all_installed_slots for bug #480736. |
200 |
- sort_keys.append(lambda x: not x.all_installed_slots) |
201 |
- |
202 |
if minimize_slots: |
203 |
# Prefer choices having fewer new slots. When used with DNF form, |
204 |
# this can eliminate unecessary packages that depclean would |
205 |
@@ -694,15 +681,35 @@ def dep_zapdeps(unreduced, reduced, myroot, use_binaries=0, trees=None, |
206 |
# contribute to outcomes that appear to be random. Meanwhile, |
207 |
# the order specified in the ebuild is without variance, so it |
208 |
# does not have this problem. |
209 |
- sort_keys.append(lambda x: x.new_slot_count) |
210 |
+ choices.sort(key=operator.attrgetter('new_slot_count')) |
211 |
|
212 |
- choices.sort(key=lambda x: tuple(f(x) for f in sort_keys)) |
213 |
for choice_1 in choices[1:]: |
214 |
cps = set(choice_1.cp_map) |
215 |
for choice_2 in choices: |
216 |
if choice_1 is choice_2: |
217 |
# choice_1 will not be promoted, so move on |
218 |
break |
219 |
+ if ( |
220 |
+ # For removal actions, prefer choices where all packages |
221 |
+ # have been pulled into the graph. |
222 |
+ (graph_interface and graph_interface.removal_action and |
223 |
+ choice_1.all_in_graph and not choice_2.all_in_graph) |
224 |
+ |
225 |
+ # Prefer choices where all_installed_slots is True, except |
226 |
+ # in cases where we want to upgrade to a new slot as in |
227 |
+ # bug 706278. Don't compare new_slot_count here since that |
228 |
+ # would aggressively override the preference order defined |
229 |
+ # in the ebuild, breaking the test case for bug 645002. |
230 |
+ or (choice_1.all_installed_slots and |
231 |
+ not choice_2.all_installed_slots and |
232 |
+ not choice_2.want_update) |
233 |
+ ): |
234 |
+ # promote choice_1 in front of choice_2 |
235 |
+ choices.remove(choice_1) |
236 |
+ index_2 = choices.index(choice_2) |
237 |
+ choices.insert(index_2, choice_1) |
238 |
+ break |
239 |
+ |
240 |
intersecting_cps = cps.intersection(choice_2.cp_map) |
241 |
if not intersecting_cps: |
242 |
continue |
243 |
|
244 |
diff --git a/lib/portage/tests/resolver/test_or_choices.py b/lib/portage/tests/resolver/test_or_choices.py |
245 |
index c0316bfb3..a50ad0151 100644 |
246 |
--- a/lib/portage/tests/resolver/test_or_choices.py |
247 |
+++ b/lib/portage/tests/resolver/test_or_choices.py |
248 |
@@ -288,6 +288,15 @@ class OrChoicesTestCase(TestCase): |
249 |
class OrChoicesLibpostprocTestCase(TestCase): |
250 |
|
251 |
def testOrChoicesLibpostproc(self): |
252 |
+ # This test case is expected to fail after the fix for bug 706278, |
253 |
+ # since the "undesirable" slot upgrade which triggers a blocker conflict |
254 |
+ # in this test case is practically indistinguishable from a desirable |
255 |
+ # slot upgrade. This particular blocker conflict is no longer relevant, |
256 |
+ # since current versions of media-libs/libpostproc are no longer |
257 |
+ # compatible with any available media-video/ffmpeg slot. In order to |
258 |
+ # solve this test case, some fancy backtracking (like for bug 382421) |
259 |
+ # will be required. |
260 |
+ self.todo = True |
261 |
|
262 |
ebuilds = { |
263 |
"media-video/ffmpeg-0.10" : { |
264 |
|
265 |
diff --git a/lib/portage/tests/resolver/test_or_upgrade_installed.py b/lib/portage/tests/resolver/test_or_upgrade_installed.py |
266 |
index c3efebf55..3889d53dc 100644 |
267 |
--- a/lib/portage/tests/resolver/test_or_upgrade_installed.py |
268 |
+++ b/lib/portage/tests/resolver/test_or_upgrade_installed.py |
269 |
@@ -213,8 +213,7 @@ class OrUpgradeInstalledTestCase(TestCase): |
270 |
['@world'], |
271 |
options={'--update': True, '--deep': True}, |
272 |
success=True, |
273 |
- mergelist=[], |
274 |
- #mergelist=['sys-devel/llvm-9'], |
275 |
+ mergelist=['sys-devel/llvm-9', 'media-libs/mesa-19.2.8'], |
276 |
), |
277 |
) |