1 |
Author: zmedico |
2 |
Date: 2008-12-01 06:51:28 +0000 (Mon, 01 Dec 2008) |
3 |
New Revision: 12125 |
4 |
|
5 |
Modified: |
6 |
main/trunk/pym/_emerge/__init__.py |
7 |
Log: |
8 |
Bug #249185 - For more useful output in cases when one or more USE deps |
9 |
trigger "SLOT conflicts", show the specific atoms that triggered the |
10 |
conflict. TODO: Distiguish between various possible causes and tailor |
11 |
messages to suit them. |
12 |
|
13 |
|
14 |
Modified: main/trunk/pym/_emerge/__init__.py |
15 |
=================================================================== |
16 |
--- main/trunk/pym/_emerge/__init__.py 2008-11-30 00:12:34 UTC (rev 12124) |
17 |
+++ main/trunk/pym/_emerge/__init__.py 2008-12-01 06:51:28 UTC (rev 12125) |
18 |
@@ -4323,6 +4323,8 @@ |
19 |
# Slot collision nodes are not allowed to block other packages since |
20 |
# blocker validation is only able to account for one package per slot. |
21 |
self._slot_collision_nodes = set() |
22 |
+ self._parent_atoms = {} |
23 |
+ self._slot_conflict_parent_atoms = set() |
24 |
self._serialized_tasks_cache = None |
25 |
self._scheduler_graph = None |
26 |
self._displayed_list = None |
27 |
@@ -4346,8 +4348,26 @@ |
28 |
the packages. In some cases it may be possible to resolve this |
29 |
automatically, but support for backtracking (removal nodes that have |
30 |
already been selected) will be required in order to handle all possible |
31 |
- cases.""" |
32 |
+ cases. |
33 |
|
34 |
+ When a slot conflict occurs due to USE deps, there are a few |
35 |
+ different cases to consider: |
36 |
+ |
37 |
+ 1) New USE are correctly set but --newuse wasn't requested so an |
38 |
+ installed package with incorrect USE happened to get pulled |
39 |
+ into graph before the new one. |
40 |
+ |
41 |
+ 2) New USE are incorrectly set but an installed package has correct |
42 |
+ USE so it got pulled into the graph, and a new instance also got |
43 |
+ pulled in due to --newuse or an upgrade. |
44 |
+ |
45 |
+ 3) Multiple USE deps exist that can't be satisfied simultaneously, |
46 |
+ and multiple package instances got pulled into the same slot to |
47 |
+ satisfy the conflicting deps. |
48 |
+ |
49 |
+ TODO: Distinguish the above cases and tailor messages to suit them. |
50 |
+ """ |
51 |
+ |
52 |
if not self._slot_collision_info: |
53 |
return |
54 |
|
55 |
@@ -4369,35 +4389,56 @@ |
56 |
for node in slot_nodes: |
57 |
msg.append(indent) |
58 |
msg.append(str(node)) |
59 |
- parents = self.digraph.parent_nodes(node) |
60 |
- if parents: |
61 |
- omitted_parents = 0 |
62 |
- if len(parents) > max_parents: |
63 |
- pruned_list = [] |
64 |
+ parent_atoms = self._parent_atoms.get(node) |
65 |
+ if parent_atoms: |
66 |
+ pruned_list = set() |
67 |
+ # Prefer conflict atoms over others. |
68 |
+ for parent_atom in parent_atoms: |
69 |
+ if len(pruned_list) >= max_parents: |
70 |
+ break |
71 |
+ if parent_atom in self._slot_conflict_parent_atoms: |
72 |
+ pruned_list.add(parent_atom) |
73 |
+ |
74 |
+ # If this package was pulled in by conflict atoms then |
75 |
+ # show those alone since those are the most interesting. |
76 |
+ if not pruned_list: |
77 |
# When generating the pruned list, prefer instances |
78 |
# of DependencyArg over instances of Package. |
79 |
- for parent in parents: |
80 |
+ for parent_atom in parent_atoms: |
81 |
+ if len(pruned_list) >= max_parents: |
82 |
+ break |
83 |
+ parent, atom = parent_atom |
84 |
if isinstance(parent, DependencyArg): |
85 |
- pruned_list.append(parent) |
86 |
+ pruned_list.add(parent_atom) |
87 |
# Prefer Packages instances that themselves have been |
88 |
# pulled into collision slots. |
89 |
- for parent in parents: |
90 |
+ for parent_atom in parent_atoms: |
91 |
+ if len(pruned_list) >= max_parents: |
92 |
+ break |
93 |
+ parent, atom = parent_atom |
94 |
if isinstance(parent, Package) and \ |
95 |
(parent.slot_atom, parent.root) \ |
96 |
in self._slot_collision_info: |
97 |
- pruned_list.append(parent) |
98 |
- for parent in parents: |
99 |
+ pruned_list.add(parent_atom) |
100 |
+ for parent_atom in parent_atoms: |
101 |
if len(pruned_list) >= max_parents: |
102 |
break |
103 |
- if not isinstance(parent, DependencyArg) and \ |
104 |
- parent not in pruned_list: |
105 |
- pruned_list.append(parent) |
106 |
- omitted_parents = len(parents) - len(pruned_list) |
107 |
- parents = pruned_list |
108 |
+ pruned_list.add(parent_atom) |
109 |
+ omitted_parents = len(parent_atoms) - len(pruned_list) |
110 |
+ parent_atoms = pruned_list |
111 |
msg.append(" pulled in by\n") |
112 |
- for parent in parents: |
113 |
+ for parent_atom in parent_atoms: |
114 |
+ parent, atom = parent_atom |
115 |
msg.append(2*indent) |
116 |
- msg.append(str(parent)) |
117 |
+ if isinstance(parent, |
118 |
+ (PackageArg, AtomArg)): |
119 |
+ # For PackageArg and AtomArg types, it's |
120 |
+ # redundant to display the atom attribute. |
121 |
+ msg.append(str(parent)) |
122 |
+ else: |
123 |
+ # Display the specific atom from SetArg or |
124 |
+ # Package types. |
125 |
+ msg.append("%s required by %s" % (atom, parent)) |
126 |
msg.append("\n") |
127 |
if omitted_parents: |
128 |
msg.append(2*indent) |
129 |
@@ -4437,6 +4478,40 @@ |
130 |
f.end_paragraph(1) |
131 |
f.writer.flush() |
132 |
|
133 |
+ def _process_slot_conflicts(self): |
134 |
+ """ |
135 |
+ Process slot conflict data to identify specific atoms which |
136 |
+ lead to conflict. These atoms only match a subset of the |
137 |
+ packages that have been pulled into a given slot. |
138 |
+ """ |
139 |
+ for (slot_atom, root), slot_nodes \ |
140 |
+ in self._slot_collision_info.iteritems(): |
141 |
+ |
142 |
+ all_parent_atoms = set() |
143 |
+ for pkg in slot_nodes: |
144 |
+ parent_atoms = self._parent_atoms.get(pkg) |
145 |
+ if not parent_atoms: |
146 |
+ continue |
147 |
+ all_parent_atoms.update(parent_atoms) |
148 |
+ |
149 |
+ for pkg in slot_nodes: |
150 |
+ parent_atoms = self._parent_atoms.get(pkg) |
151 |
+ if parent_atoms is None: |
152 |
+ parent_atoms = set() |
153 |
+ self._parent_atoms[pkg] = parent_atoms |
154 |
+ for parent_atom in all_parent_atoms: |
155 |
+ if parent_atom in parent_atoms: |
156 |
+ continue |
157 |
+ # Use package set for matching since it will match via |
158 |
+ # PROVIDE when necessary, while match_from_list does not. |
159 |
+ parent, atom = parent_atom |
160 |
+ atom_set = InternalPackageSet( |
161 |
+ initial_atoms=(atom,)) |
162 |
+ if atom_set.findAtomForPackage(pkg): |
163 |
+ parent_atoms.add(parent_atom) |
164 |
+ else: |
165 |
+ self._slot_conflict_parent_atoms.add(parent_atom) |
166 |
+ |
167 |
def _reinstall_for_flags(self, forced_flags, |
168 |
orig_use, orig_iuse, cur_use, cur_iuse): |
169 |
"""Return a set of flags that trigger reinstallation, or None if there |
170 |
@@ -4557,7 +4632,6 @@ |
171 |
vardbapi = self.trees[pkg.root]["vartree"].dbapi |
172 |
pkgsettings = self.pkgsettings[pkg.root] |
173 |
|
174 |
- args = None |
175 |
arg_atoms = None |
176 |
if True: |
177 |
try: |
178 |
@@ -4568,8 +4642,6 @@ |
179 |
pkg, pkg.metadata["PROVIDE"], str(e)) |
180 |
return 0 |
181 |
del e |
182 |
- else: |
183 |
- args = [arg for arg, atom in arg_atoms] |
184 |
|
185 |
if not pkg.onlydeps: |
186 |
if not pkg.installed and \ |
187 |
@@ -4599,10 +4671,12 @@ |
188 |
existing_node_matches = False |
189 |
if existing_node_matches: |
190 |
# The existing node can be reused. |
191 |
- if args: |
192 |
- for arg in args: |
193 |
- self.digraph.add(existing_node, arg, |
194 |
+ if arg_atoms: |
195 |
+ for parent_atom in arg_atoms: |
196 |
+ parent, atom = parent_atom |
197 |
+ self.digraph.add(existing_node, parent, |
198 |
priority=priority) |
199 |
+ self._add_parent_atom(existing_node, parent_atom) |
200 |
# If a direct circular dependency is not an unsatisfied |
201 |
# buildtime dependency then drop it here since otherwise |
202 |
# it can skew the merge order calculation in an unwanted |
203 |
@@ -4611,26 +4685,12 @@ |
204 |
(priority.buildtime and not priority.satisfied): |
205 |
self.digraph.addnode(existing_node, myparent, |
206 |
priority=priority) |
207 |
+ if dep.atom is not None and dep.parent is not None: |
208 |
+ self._add_parent_atom(existing_node, |
209 |
+ (dep.parent, dep.atom)) |
210 |
return 1 |
211 |
else: |
212 |
|
213 |
- if pkg.cpv == existing_node.cpv and \ |
214 |
- dep.atom is not None and \ |
215 |
- dep.atom.use: |
216 |
- # Multiple different instances of the same version |
217 |
- # (typically one installed and another not yet |
218 |
- # installed) have been pulled into the graph due |
219 |
- # to a USE dependency. The "slot collision" display |
220 |
- # is not helpful in a case like this, so display it |
221 |
- # as an unsatisfied dependency. |
222 |
- self._unsatisfied_deps_for_display.append( |
223 |
- ((dep.root, dep.atom), {"myparent":dep.parent})) |
224 |
- self._add_slot_conflict(pkg) |
225 |
- self.digraph.addnode(pkg, myparent, priority=priority) |
226 |
- return 0 |
227 |
- |
228 |
- if pkg in self._slot_collision_nodes: |
229 |
- return 1 |
230 |
# A slot collision has occurred. Sometimes this coincides |
231 |
# with unresolvable blockers, so the slot collision will be |
232 |
# shown later if there are no unresolvable blockers. |
233 |
@@ -4653,8 +4713,6 @@ |
234 |
self._slot_pkg_map[pkg.root][pkg.slot_atom] = pkg |
235 |
self.mydbapi[pkg.root].cpv_inject(pkg) |
236 |
|
237 |
- self.digraph.addnode(pkg, myparent, priority=priority) |
238 |
- |
239 |
if not pkg.installed: |
240 |
# Allow this package to satisfy old-style virtuals in case it |
241 |
# doesn't already. Any pre-existing providers will be preferred |
242 |
@@ -4672,18 +4730,22 @@ |
243 |
del e |
244 |
return 0 |
245 |
|
246 |
- if args: |
247 |
+ if arg_atoms: |
248 |
self._set_nodes.add(pkg) |
249 |
|
250 |
# Do this even when addme is False (--onlydeps) so that the |
251 |
# parent/child relationship is always known in case |
252 |
# self._show_slot_collision_notice() needs to be called later. |
253 |
- if pkg.onlydeps: |
254 |
- self.digraph.add(pkg, myparent, priority=priority) |
255 |
- if args: |
256 |
- for arg in args: |
257 |
- self.digraph.add(pkg, arg, priority=priority) |
258 |
+ self.digraph.add(pkg, myparent, priority=priority) |
259 |
+ if dep.atom is not None and dep.parent is not None: |
260 |
+ self._add_parent_atom(pkg, (dep.parent, dep.atom)) |
261 |
|
262 |
+ if arg_atoms: |
263 |
+ for parent_atom in arg_atoms: |
264 |
+ parent, atom = parent_atom |
265 |
+ self.digraph.add(pkg, parent, priority=priority) |
266 |
+ self._add_parent_atom(pkg, parent_atom) |
267 |
+ |
268 |
""" This section determines whether we go deeper into dependencies or not. |
269 |
We want to go deeper on a few occasions: |
270 |
Installing package A, we need to make sure package A's deps are met. |
271 |
@@ -4699,13 +4761,20 @@ |
272 |
|
273 |
self.spinner.update() |
274 |
|
275 |
- if args: |
276 |
+ if arg_atoms: |
277 |
depth = 0 |
278 |
pkg.depth = depth |
279 |
if not previously_added: |
280 |
dep_stack.append(pkg) |
281 |
return 1 |
282 |
|
283 |
+ def _add_parent_atom(self, pkg, parent_atom): |
284 |
+ parent_atoms = self._parent_atoms.get(pkg) |
285 |
+ if parent_atoms is None: |
286 |
+ parent_atoms = set() |
287 |
+ self._parent_atoms[pkg] = parent_atoms |
288 |
+ parent_atoms.add(parent_atom) |
289 |
+ |
290 |
def _add_slot_conflict(self, pkg): |
291 |
self._slot_collision_nodes.add(pkg) |
292 |
slot_key = (pkg.slot_atom, pkg.root) |
293 |
@@ -6266,6 +6335,9 @@ |
294 |
if not self.validate_blockers(): |
295 |
raise self._unknown_internal_error() |
296 |
|
297 |
+ if self._slot_collision_info: |
298 |
+ self._process_slot_conflicts() |
299 |
+ |
300 |
def _serialize_tasks(self): |
301 |
scheduler_graph = self.digraph.copy() |
302 |
mygraph=self.digraph.copy() |