1 |
Author: grobian |
2 |
Date: 2008-12-01 21:14:48 +0000 (Mon, 01 Dec 2008) |
3 |
New Revision: 12133 |
4 |
|
5 |
Modified: |
6 |
main/branches/prefix/bin/ebuild.sh |
7 |
main/branches/prefix/man/emerge.1 |
8 |
main/branches/prefix/pym/_emerge/__init__.py |
9 |
Log: |
10 |
Merged from trunk -r12117:12126 |
11 |
|
12 |
| 12120 | Add 'automatically resolved' blockers to the --pretend docs. | |
13 |
| zmedico | | |
14 |
|
15 |
| 12122 | Add a sanity check inside depgraph._add_pkg() to ensure that | |
16 |
| zmedico | the dependencies of the same package are never processed | |
17 |
| | more than once. | |
18 |
|
19 |
| 12124 | Change depgraph._slot_collision_info() from a set to a dict | |
20 |
| zmedico | that contains sets of packages pulled into a given slot. | |
21 |
| | This will make the data easier to analyze when implementing | |
22 |
| | a fix for bug #249185. | |
23 |
|
24 |
| 12125 | Bug #249185 - For more useful output in cases when one or | |
25 |
| zmedico | more USE deps trigger "SLOT conflicts", show the specific | |
26 |
| | atoms that triggered the conflict. TODO: Distiguish between | |
27 |
| | various possible causes and tailor messages to suit them. | |
28 |
|
29 |
| 12126 | Fix inconsistencies between the "clean" and "cleanrm" | |
30 |
| zmedico | phases. | |
31 |
|
32 |
|
33 |
Modified: main/branches/prefix/bin/ebuild.sh |
34 |
=================================================================== |
35 |
--- main/branches/prefix/bin/ebuild.sh 2008-12-01 21:10:53 UTC (rev 12132) |
36 |
+++ main/branches/prefix/bin/ebuild.sh 2008-12-01 21:14:48 UTC (rev 12133) |
37 |
@@ -1810,7 +1810,7 @@ |
38 |
unset BIN_PATH BIN BODY FUNC_SRC |
39 |
fi |
40 |
|
41 |
-if ! hasq ${EBUILD_PHASE} clean depend && \ |
42 |
+if ! hasq "$EBUILD_PHASE" clean cleanrm depend && \ |
43 |
[ -f "${T}"/environment ] ; then |
44 |
# The environment may have been extracted from environment.bz2 or |
45 |
# may have come from another version of ebuild.sh or something. |
46 |
@@ -1856,7 +1856,7 @@ |
47 |
source_all_bashrcs |
48 |
fi |
49 |
|
50 |
-if ! hasq ${EBUILD_PHASE} clean && \ |
51 |
+if ! hasq "$EBUILD_PHASE" clean cleanrm && \ |
52 |
( |
53 |
hasq ${EBUILD_PHASE} depend || \ |
54 |
[ ! -f "${T}"/environment ] || \ |
55 |
@@ -1903,7 +1903,7 @@ |
56 |
[[ -n ${EAPI/prefix/} ]] || EAPI="${EAPI}${EAPI:+ }0" |
57 |
|
58 |
# enable bashrc support for the clean phase |
59 |
-[[ ${EBUILD_PHASE} == clean ]] && source_all_bashrcs |
60 |
+hasq "$EBUILD_PHASE" clean cleanrm && source_all_bashrcs |
61 |
|
62 |
# unset USE_EXPAND variables that contain only the special "*" token |
63 |
for x in ${USE_EXPAND} ; do |
64 |
|
65 |
Modified: main/branches/prefix/man/emerge.1 |
66 |
=================================================================== |
67 |
--- main/branches/prefix/man/emerge.1 2008-12-01 21:10:53 UTC (rev 12132) |
68 |
+++ main/branches/prefix/man/emerge.1 2008-12-01 21:14:48 UTC (rev 12133) |
69 |
@@ -405,7 +405,8 @@ |
70 |
F fetch restricted (must be manually downloaded) |
71 |
f fetch restricted (already downloaded) |
72 |
I interactive (requires user input) |
73 |
-B blocked by an already installed package |
74 |
+B blocked by another package (unresolved conflict) |
75 |
+b blocked by another package (automatically resolved conflict) |
76 |
.TE |
77 |
.TP |
78 |
.BR "\-\-quiet " (\fB\-q\fR) |
79 |
|
80 |
Modified: main/branches/prefix/pym/_emerge/__init__.py |
81 |
=================================================================== |
82 |
--- main/branches/prefix/pym/_emerge/__init__.py 2008-12-01 21:10:53 UTC (rev 12132) |
83 |
+++ main/branches/prefix/pym/_emerge/__init__.py 2008-12-01 21:14:48 UTC (rev 12133) |
84 |
@@ -4346,10 +4346,12 @@ |
85 |
self._irrelevant_blockers = digraph() |
86 |
# Contains only unsolvable Package -> Blocker edges |
87 |
self._unsolvable_blockers = digraph() |
88 |
- self._slot_collision_info = set() |
89 |
+ self._slot_collision_info = {} |
90 |
# Slot collision nodes are not allowed to block other packages since |
91 |
# blocker validation is only able to account for one package per slot. |
92 |
self._slot_collision_nodes = set() |
93 |
+ self._parent_atoms = {} |
94 |
+ self._slot_conflict_parent_atoms = set() |
95 |
self._serialized_tasks_cache = None |
96 |
self._scheduler_graph = None |
97 |
self._displayed_list = None |
98 |
@@ -4373,8 +4375,26 @@ |
99 |
the packages. In some cases it may be possible to resolve this |
100 |
automatically, but support for backtracking (removal nodes that have |
101 |
already been selected) will be required in order to handle all possible |
102 |
- cases.""" |
103 |
+ cases. |
104 |
|
105 |
+ When a slot conflict occurs due to USE deps, there are a few |
106 |
+ different cases to consider: |
107 |
+ |
108 |
+ 1) New USE are correctly set but --newuse wasn't requested so an |
109 |
+ installed package with incorrect USE happened to get pulled |
110 |
+ into graph before the new one. |
111 |
+ |
112 |
+ 2) New USE are incorrectly set but an installed package has correct |
113 |
+ USE so it got pulled into the graph, and a new instance also got |
114 |
+ pulled in due to --newuse or an upgrade. |
115 |
+ |
116 |
+ 3) Multiple USE deps exist that can't be satisfied simultaneously, |
117 |
+ and multiple package instances got pulled into the same slot to |
118 |
+ satisfy the conflicting deps. |
119 |
+ |
120 |
+ TODO: Distinguish the above cases and tailor messages to suit them. |
121 |
+ """ |
122 |
+ |
123 |
if not self._slot_collision_info: |
124 |
return |
125 |
|
126 |
@@ -4388,46 +4408,64 @@ |
127 |
indent = " " |
128 |
# Max number of parents shown, to avoid flooding the display. |
129 |
max_parents = 3 |
130 |
- for slot_atom, root in self._slot_collision_info: |
131 |
+ for (slot_atom, root), slot_nodes \ |
132 |
+ in self._slot_collision_info.iteritems(): |
133 |
msg.append(str(slot_atom)) |
134 |
msg.append("\n\n") |
135 |
- slot_nodes = [] |
136 |
- for node in self._slot_collision_nodes: |
137 |
- if node.slot_atom == slot_atom: |
138 |
- slot_nodes.append(node) |
139 |
- slot_nodes.append(self._slot_pkg_map[root][slot_atom]) |
140 |
+ |
141 |
for node in slot_nodes: |
142 |
msg.append(indent) |
143 |
msg.append(str(node)) |
144 |
- parents = self.digraph.parent_nodes(node) |
145 |
- if parents: |
146 |
- omitted_parents = 0 |
147 |
- if len(parents) > max_parents: |
148 |
- pruned_list = [] |
149 |
+ parent_atoms = self._parent_atoms.get(node) |
150 |
+ if parent_atoms: |
151 |
+ pruned_list = set() |
152 |
+ # Prefer conflict atoms over others. |
153 |
+ for parent_atom in parent_atoms: |
154 |
+ if len(pruned_list) >= max_parents: |
155 |
+ break |
156 |
+ if parent_atom in self._slot_conflict_parent_atoms: |
157 |
+ pruned_list.add(parent_atom) |
158 |
+ |
159 |
+ # If this package was pulled in by conflict atoms then |
160 |
+ # show those alone since those are the most interesting. |
161 |
+ if not pruned_list: |
162 |
# When generating the pruned list, prefer instances |
163 |
# of DependencyArg over instances of Package. |
164 |
- for parent in parents: |
165 |
+ for parent_atom in parent_atoms: |
166 |
+ if len(pruned_list) >= max_parents: |
167 |
+ break |
168 |
+ parent, atom = parent_atom |
169 |
if isinstance(parent, DependencyArg): |
170 |
- pruned_list.append(parent) |
171 |
+ pruned_list.add(parent_atom) |
172 |
# Prefer Packages instances that themselves have been |
173 |
# pulled into collision slots. |
174 |
- for parent in parents: |
175 |
+ for parent_atom in parent_atoms: |
176 |
+ if len(pruned_list) >= max_parents: |
177 |
+ break |
178 |
+ parent, atom = parent_atom |
179 |
if isinstance(parent, Package) and \ |
180 |
(parent.slot_atom, parent.root) \ |
181 |
in self._slot_collision_info: |
182 |
- pruned_list.append(parent) |
183 |
- for parent in parents: |
184 |
+ pruned_list.add(parent_atom) |
185 |
+ for parent_atom in parent_atoms: |
186 |
if len(pruned_list) >= max_parents: |
187 |
break |
188 |
- if not isinstance(parent, DependencyArg) and \ |
189 |
- parent not in pruned_list: |
190 |
- pruned_list.append(parent) |
191 |
- omitted_parents = len(parents) - len(pruned_list) |
192 |
- parents = pruned_list |
193 |
+ pruned_list.add(parent_atom) |
194 |
+ omitted_parents = len(parent_atoms) - len(pruned_list) |
195 |
+ parent_atoms = pruned_list |
196 |
msg.append(" pulled in by\n") |
197 |
- for parent in parents: |
198 |
+ for parent_atom in parent_atoms: |
199 |
+ parent, atom = parent_atom |
200 |
msg.append(2*indent) |
201 |
- msg.append(str(parent)) |
202 |
+ if isinstance(parent, |
203 |
+ (PackageArg, AtomArg)): |
204 |
+ # For PackageArg and AtomArg types, it's |
205 |
+ # redundant to display the atom attribute. |
206 |
+ msg.append(str(parent)) |
207 |
+ else: |
208 |
+ # Display the specific atom from SetArg or |
209 |
+ # Package types. |
210 |
+ msg.append("%s required by %s" % (atom, parent)) |
211 |
msg.append("\n") |
212 |
if omitted_parents: |
213 |
msg.append(2*indent) |
214 |
@@ -4467,6 +4505,40 @@ |
215 |
f.end_paragraph(1) |
216 |
f.writer.flush() |
217 |
|
218 |
+ def _process_slot_conflicts(self): |
219 |
+ """ |
220 |
+ Process slot conflict data to identify specific atoms which |
221 |
+ lead to conflict. These atoms only match a subset of the |
222 |
+ packages that have been pulled into a given slot. |
223 |
+ """ |
224 |
+ for (slot_atom, root), slot_nodes \ |
225 |
+ in self._slot_collision_info.iteritems(): |
226 |
+ |
227 |
+ all_parent_atoms = set() |
228 |
+ for pkg in slot_nodes: |
229 |
+ parent_atoms = self._parent_atoms.get(pkg) |
230 |
+ if not parent_atoms: |
231 |
+ continue |
232 |
+ all_parent_atoms.update(parent_atoms) |
233 |
+ |
234 |
+ for pkg in slot_nodes: |
235 |
+ parent_atoms = self._parent_atoms.get(pkg) |
236 |
+ if parent_atoms is None: |
237 |
+ parent_atoms = set() |
238 |
+ self._parent_atoms[pkg] = parent_atoms |
239 |
+ for parent_atom in all_parent_atoms: |
240 |
+ if parent_atom in parent_atoms: |
241 |
+ continue |
242 |
+ # Use package set for matching since it will match via |
243 |
+ # PROVIDE when necessary, while match_from_list does not. |
244 |
+ parent, atom = parent_atom |
245 |
+ atom_set = InternalPackageSet( |
246 |
+ initial_atoms=(atom,)) |
247 |
+ if atom_set.findAtomForPackage(pkg): |
248 |
+ parent_atoms.add(parent_atom) |
249 |
+ else: |
250 |
+ self._slot_conflict_parent_atoms.add(parent_atom) |
251 |
+ |
252 |
def _reinstall_for_flags(self, forced_flags, |
253 |
orig_use, orig_iuse, cur_use, cur_iuse): |
254 |
"""Return a set of flags that trigger reinstallation, or None if there |
255 |
@@ -4579,12 +4651,14 @@ |
256 |
#IUSE-aware emerge -> USE DEP aware depgraph |
257 |
#"no downgrade" emerge |
258 |
""" |
259 |
+ # Ensure that the dependencies of the same package |
260 |
+ # are never processed more than once. |
261 |
+ previously_added = pkg in self.digraph |
262 |
|
263 |
# select the correct /var database that we'll be checking against |
264 |
vardbapi = self.trees[pkg.root]["vartree"].dbapi |
265 |
pkgsettings = self.pkgsettings[pkg.root] |
266 |
|
267 |
- args = None |
268 |
arg_atoms = None |
269 |
if True: |
270 |
try: |
271 |
@@ -4595,8 +4669,6 @@ |
272 |
pkg, pkg.metadata["PROVIDE"], str(e)) |
273 |
return 0 |
274 |
del e |
275 |
- else: |
276 |
- args = [arg for arg, atom in arg_atoms] |
277 |
|
278 |
if not pkg.onlydeps: |
279 |
if not pkg.installed and \ |
280 |
@@ -4626,10 +4698,12 @@ |
281 |
existing_node_matches = False |
282 |
if existing_node_matches: |
283 |
# The existing node can be reused. |
284 |
- if args: |
285 |
- for arg in args: |
286 |
- self.digraph.add(existing_node, arg, |
287 |
+ if arg_atoms: |
288 |
+ for parent_atom in arg_atoms: |
289 |
+ parent, atom = parent_atom |
290 |
+ self.digraph.add(existing_node, parent, |
291 |
priority=priority) |
292 |
+ self._add_parent_atom(existing_node, parent_atom) |
293 |
# If a direct circular dependency is not an unsatisfied |
294 |
# buildtime dependency then drop it here since otherwise |
295 |
# it can skew the merge order calculation in an unwanted |
296 |
@@ -4638,32 +4712,16 @@ |
297 |
(priority.buildtime and not priority.satisfied): |
298 |
self.digraph.addnode(existing_node, myparent, |
299 |
priority=priority) |
300 |
+ if dep.atom is not None and dep.parent is not None: |
301 |
+ self._add_parent_atom(existing_node, |
302 |
+ (dep.parent, dep.atom)) |
303 |
return 1 |
304 |
else: |
305 |
|
306 |
- if pkg.cpv == existing_node.cpv and \ |
307 |
- dep.atom is not None and \ |
308 |
- dep.atom.use: |
309 |
- # Multiple different instances of the same version |
310 |
- # (typically one installed and another not yet |
311 |
- # installed) have been pulled into the graph due |
312 |
- # to a USE dependency. The "slot collision" display |
313 |
- # is not helpful in a case like this, so display it |
314 |
- # as an unsatisfied dependency. |
315 |
- self._unsatisfied_deps_for_display.append( |
316 |
- ((dep.root, dep.atom), {"myparent":dep.parent})) |
317 |
- self._slot_collision_info.add((pkg.slot_atom, pkg.root)) |
318 |
- self._slot_collision_nodes.add(pkg) |
319 |
- self.digraph.addnode(pkg, myparent, priority=priority) |
320 |
- return 0 |
321 |
- |
322 |
- if pkg in self._slot_collision_nodes: |
323 |
- return 1 |
324 |
# A slot collision has occurred. Sometimes this coincides |
325 |
# with unresolvable blockers, so the slot collision will be |
326 |
# shown later if there are no unresolvable blockers. |
327 |
- self._slot_collision_info.add((pkg.slot_atom, pkg.root)) |
328 |
- self._slot_collision_nodes.add(pkg) |
329 |
+ self._add_slot_conflict(pkg) |
330 |
slot_collision = True |
331 |
|
332 |
if slot_collision: |
333 |
@@ -4682,8 +4740,6 @@ |
334 |
self._slot_pkg_map[pkg.root][pkg.slot_atom] = pkg |
335 |
self.mydbapi[pkg.root].cpv_inject(pkg) |
336 |
|
337 |
- self.digraph.addnode(pkg, myparent, priority=priority) |
338 |
- |
339 |
if not pkg.installed: |
340 |
# Allow this package to satisfy old-style virtuals in case it |
341 |
# doesn't already. Any pre-existing providers will be preferred |
342 |
@@ -4701,18 +4757,22 @@ |
343 |
del e |
344 |
return 0 |
345 |
|
346 |
- if args: |
347 |
+ if arg_atoms: |
348 |
self._set_nodes.add(pkg) |
349 |
|
350 |
# Do this even when addme is False (--onlydeps) so that the |
351 |
# parent/child relationship is always known in case |
352 |
# self._show_slot_collision_notice() needs to be called later. |
353 |
- if pkg.onlydeps: |
354 |
- self.digraph.add(pkg, myparent, priority=priority) |
355 |
- if args: |
356 |
- for arg in args: |
357 |
- self.digraph.add(pkg, arg, priority=priority) |
358 |
+ self.digraph.add(pkg, myparent, priority=priority) |
359 |
+ if dep.atom is not None and dep.parent is not None: |
360 |
+ self._add_parent_atom(pkg, (dep.parent, dep.atom)) |
361 |
|
362 |
+ if arg_atoms: |
363 |
+ for parent_atom in arg_atoms: |
364 |
+ parent, atom = parent_atom |
365 |
+ self.digraph.add(pkg, parent, priority=priority) |
366 |
+ self._add_parent_atom(pkg, parent_atom) |
367 |
+ |
368 |
""" This section determines whether we go deeper into dependencies or not. |
369 |
We want to go deeper on a few occasions: |
370 |
Installing package A, we need to make sure package A's deps are met. |
371 |
@@ -4728,12 +4788,30 @@ |
372 |
|
373 |
self.spinner.update() |
374 |
|
375 |
- if args: |
376 |
+ if arg_atoms: |
377 |
depth = 0 |
378 |
pkg.depth = depth |
379 |
- dep_stack.append(pkg) |
380 |
+ if not previously_added: |
381 |
+ dep_stack.append(pkg) |
382 |
return 1 |
383 |
|
384 |
+ def _add_parent_atom(self, pkg, parent_atom): |
385 |
+ parent_atoms = self._parent_atoms.get(pkg) |
386 |
+ if parent_atoms is None: |
387 |
+ parent_atoms = set() |
388 |
+ self._parent_atoms[pkg] = parent_atoms |
389 |
+ parent_atoms.add(parent_atom) |
390 |
+ |
391 |
+ def _add_slot_conflict(self, pkg): |
392 |
+ self._slot_collision_nodes.add(pkg) |
393 |
+ slot_key = (pkg.slot_atom, pkg.root) |
394 |
+ slot_nodes = self._slot_collision_info.get(slot_key) |
395 |
+ if slot_nodes is None: |
396 |
+ slot_nodes = set() |
397 |
+ slot_nodes.add(self._slot_pkg_map[pkg.root][pkg.slot_atom]) |
398 |
+ self._slot_collision_info[slot_key] = slot_nodes |
399 |
+ slot_nodes.add(pkg) |
400 |
+ |
401 |
def _add_pkg_deps(self, pkg, allow_unsatisfied=False): |
402 |
|
403 |
mytype = pkg.type_name |
404 |
@@ -6288,6 +6366,9 @@ |
405 |
if not self.validate_blockers(): |
406 |
raise self._unknown_internal_error() |
407 |
|
408 |
+ if self._slot_collision_info: |
409 |
+ self._process_slot_conflicts() |
410 |
+ |
411 |
def _serialize_tasks(self): |
412 |
scheduler_graph = self.digraph.copy() |
413 |
mygraph=self.digraph.copy() |