Gentoo Archives: gentoo-portage-dev

From: Sebastian Luther <SebastianLuther@×××.de>
To: gentoo-portage-dev@l.g.o
Subject: [gentoo-portage-dev] [PATCH] Introduce depgraph._extend_slot_operator_conflicts
Date: Thu, 27 Feb 2014 19:09:12
Message-Id: 1393528129-31216-2-git-send-email-SebastianLuther@gmx.de
In Reply to: [gentoo-portage-dev] Next steps to get rid of backtracking by Sebastian Luther
1 This function allows emerge to solve more slot conflicts without
2 backtracking.
3
4 It does this by creating more conflicts for packages with built slot
5 operator dependencies. This gives more freedom to
6 depgraph._solve_non_slot_operator_slot_conflicts, which is then able
7 to solve conflicts it wouldn't have otherwise.
8 ---
9 pym/_emerge/depgraph.py | 483 +++++++++++++++++++++++++++++++++++------
10 pym/_emerge/resolver/output.py | 5 +-
11 2 files changed, 415 insertions(+), 73 deletions(-)
12
13 diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py
14 index 835bd6b..970a9c7 100644
15 --- a/pym/_emerge/depgraph.py
16 +++ b/pym/_emerge/depgraph.py
17 @@ -427,6 +427,12 @@ class _dynamic_depgraph_config(object):
18 # Track missed updates caused by solved conflicts.
19 self._conflict_missed_update = collections.defaultdict(dict)
20
21 + # Rebuilds caused by slot conflicts.
22 + self._slot_conflict_rebuilds = {}
23 + # Rebuilds caused by missed slot operator updates or
24 + # slot conflicts.
25 + self._forced_rebuilds = None
26 +
27 for myroot in depgraph._frozen_config.trees:
28 self.sets[myroot] = _depgraph_sets()
29 vardb = depgraph._frozen_config.trees[myroot]["vartree"].dbapi
30 @@ -614,6 +620,9 @@ class depgraph(object):
31 Fill self._forced_rebuilds with packages that cause rebuilds.
32 """
33
34 + if self._dynamic_config._forced_rebuilds is not None:
35 + return
36 +
37 debug = "--debug" in self._frozen_config.myopts
38
39 # Get all atoms that might have caused a forced rebuild.
40 @@ -687,19 +696,23 @@ class depgraph(object):
41 writemsg_level("\n\n",
42 level=logging.DEBUG, noiselevel=-1)
43
44 - self._forced_rebuilds = forced_rebuilds
45 + for child, parents in self._dynamic_config._slot_conflict_rebuilds.items():
46 + forced_rebuilds.setdefault(child.root, {}).setdefault(child, set()).update(parents)
47 +
48 + self._dynamic_config._forced_rebuilds = forced_rebuilds
49
50 def _show_abi_rebuild_info(self):
51
52 - if not self._forced_rebuilds:
53 + forced_rebuilds = self._dynamic_config._forced_rebuilds
54 + if not forced_rebuilds:
55 return
56
57 writemsg_stdout("\nThe following packages are causing rebuilds:\n\n", noiselevel=-1)
58
59 - for root in self._forced_rebuilds:
60 - for child in self._forced_rebuilds[root]:
61 + for root in forced_rebuilds:
62 + for child in forced_rebuilds[root]:
63 writemsg_stdout(" %s causes rebuilds for:\n" % (child,), noiselevel=-1)
64 - for parent in self._forced_rebuilds[root][child]:
65 + for parent in forced_rebuilds[root][child]:
66 writemsg_stdout(" %s\n" % (parent,), noiselevel=-1)
67
68 def _show_ignored_binaries(self):
69 @@ -968,16 +981,16 @@ class depgraph(object):
70 writemsg(line + '\n', noiselevel=-1)
71 writemsg('\n', noiselevel=-1)
72
73 - def _solve_non_slot_operator_slot_conflicts(self):
74 +
75 + def _extend_slot_operator_conflicts(self):
76 """
77 - This function solves slot conflicts which can
78 - be solved by simply choosing one of the conflicting
79 - and removing all the other ones.
80 - It is able to solve somewhat more complex cases where
81 - conflicts can only be solved simultaniously.
82 + This function searches for packages that cause
83 + slot conflicts by dependening on conflict packages
84 + with built slot operator deps. If such a package
85 + is found an alternative package is pulled in with
86 + the hope that the alternative package would not
87 + cuase the slot conflict.
88 """
89 - debug = "--debug" in self._frozen_config.myopts
90 -
91 # List all conflicts. Ignore those that involve slot operator rebuilds
92 # as the logic there needs special slot conflict behavior which isn't
93 # provided by this function.
94 @@ -990,9 +1003,133 @@ class depgraph(object):
95 if not conflicts:
96 return
97
98 - # Get a set of all conflicting packages.
99 + # Compute a mapping from parent packages to hard
100 + # required conflict packages.
101 + conflict_parents = collections.defaultdict(list)
102 + for conflict in conflicts:
103 + all_parent_atoms = set()
104 + for pkg in conflict:
105 + all_parent_atoms.update(
106 + self._dynamic_config._parent_atoms.get(pkg, []))
107 +
108 + for parent, atom in all_parent_atoms:
109 + atom_set = InternalPackageSet(
110 + initial_atoms=(atom,), allow_repo=True)
111 +
112 + for pkg in conflict:
113 + if not atom_set.findAtomForPackage(pkg, \
114 + modified_use=self._pkg_use_enabled(pkg)):
115 + conflict_parents[parent].append((pkg, atom))
116 +
117 + def _iter_alternatives(pkg):
118 + """
119 + A wrapper around _iter_similar_available that
120 + deals with autounmask.
121 + """
122 + tried = set()
123 + for other in self._iter_similar_available(pkg,
124 + Atom(pkg.cp), autounmask_level=None, installed=False):
125 + tried.add(other)
126 + yield other, None
127 +
128 + if self._dynamic_config._autounmask is not True:
129 + return
130 +
131 + for autounmask_level in self._autounmask_levels():
132 + for other in self._iter_similar_available(pkg,
133 + Atom(pkg.cp), autounmask_level=autounmask_level, installed=False):
134 + if other not in tried:
135 + tried.add(other)
136 + yield other, autounmask_level
137 +
138 +
139 + # Compute a list of possible alternatives
140 + # for each conflict parent.
141 + parent_matches = {}
142 + for parent in conflict_parents:
143 + slot_op_children = []
144 + for child, atom in conflict_parents[parent]:
145 + if atom.slot_operator == "=" and parent.built:
146 + slot_op_children.append(child)
147 +
148 + if not slot_op_children:
149 + # This parent doesn't depend with a built slot
150 + # operator on a conflict package.
151 + continue
152 +
153 + matches = []
154 + highest_ebuilds = {}
155 + for other, autounmask_level in _iter_alternatives(parent):
156 + if parent.slot_atom != other.slot_atom and parent.cpv != other.cpv:
157 + # 'other' is not a replacement for 'parent'.
158 + continue
159 +
160 + highest_ebuild = highest_ebuilds.get(autounmask_level)
161 + if not other.built and \
162 + (highest_ebuild is None or highest_ebuild < other):
163 + # Remember the highest ebuild for
164 + # downgrade testing later.
165 + highest_ebuilds[autounmask_level] = other
166 +
167 + # Go through 'parents' parents and check if 'other'
168 + # satisfies their dependencies. Ignore built slot
169 + # operator deps.
170 + is_match = True
171 + for pparent, patom in self._dynamic_config._parent_atoms.get(parent, []):
172 + if patom.slot_operator == "=" and pparent.built and parent.built:
173 + continue
174 +
175 + atom_set = InternalPackageSet(
176 + initial_atoms=(patom,), allow_repo=True)
177 +
178 + if not atom_set.findAtomForPackage(other, \
179 + modified_use=self._pkg_use_enabled(other)):
180 + is_match = False
181 + break
182 +
183 + if is_match:
184 + matches.append((other, autounmask_level))
185 +
186 + # Filter downgrades.
187 + no_downgrade_matches = []
188 + for match, autounmask_level in matches:
189 + highest_ebuild = highest_ebuilds.get(autounmask_level)
190 + if highest_ebuild and match >= highest_ebuild:
191 + no_downgrade_matches.append(match)
192 +
193 + parent_matches[parent] = no_downgrade_matches
194 +
195 + # Pull in alternatives.
196 + for parent in parent_matches:
197 + for match in parent_matches[parent]:
198 + other.depth = parent.depth
199 + dep = Dependency(atom=Atom('=' + match.cpv), child=match,
200 + parent=None, root=match.root)
201 +
202 + if not self._add_pkg(match, dep, reuse_existing=False) or \
203 + not self._create_graph():
204 + self._remove_pkg(match)
205 + continue
206 +
207 + # Record forces rebuilds.
208 + for child, atom in conflict_parents[parent]:
209 + self._dynamic_config._slot_conflict_rebuilds.setdefault(
210 + child, set()).add(match)
211 + break
212 +
213 +
214 + def _get_conflicts_data(self, conflicts):
215 + """
216 + This function creates the conflict graph and some
217 + helper data structures for _solve_non_slot_operator_slot_conflicts.
218 + """
219 + selective = "selective" in self._dynamic_config.myparams
220 +
221 + pkg_to_conflict = {}
222 conflict_pkgs = set()
223 for conflict in conflicts:
224 + for pkg in conflict:
225 + pkg_to_conflict[pkg] = conflict
226 conflict_pkgs.update(conflict)
227
228 # Get the list of other packages which are only
229 @@ -1050,7 +1187,7 @@ class depgraph(object):
230 self._dynamic_config._parent_atoms.get(pkg, []))
231
232 for parent, atom in all_parent_atoms:
233 - is_arg_parent = isinstance(parent, AtomArg)
234 + is_arg_parent = isinstance(parent, AtomArg) and not selective
235
236 if parent not in conflict_pkgs and \
237 parent not in indirect_conflict_pkgs:
238 @@ -1072,7 +1209,11 @@ class depgraph(object):
239 conflict_graph.add(matched[0], parent)
240 else:
241 # More than one packages matched, but not all.
242 - conflict_graph.add(or_tuple(matched), parent)
243 + match_tuple = or_tuple(matched)
244 + conflict_graph.add(match_tuple, parent)
245 + for match in matched:
246 + conflict_graph.add(match, match_tuple)
247 +
248
249 for pkg in indirect_conflict_pkgs:
250 for parent, atom in self._dynamic_config._parent_atoms.get(pkg, []):
251 @@ -1081,6 +1222,42 @@ class depgraph(object):
252 parent = non_conflict_node
253 conflict_graph.add(pkg, parent)
254
255 + for conflict in conflicts:
256 + if all(not conflict_graph.parent_nodes(node) for node in conflict):
257 + # No conflict parents, all parents accept all conflict packages.
258 + # This happens when _extend_slot_operator_conflict pulls in
259 + # alternative parents for other conflict paclages.
260 + conflict_graph.add(or_tuple(conflict), non_conflict_node)
261 +
262 + return conflict_graph, pkg_to_conflict, conflict_pkgs, non_conflict_node
263 +
264 +
265 + def _solve_non_slot_operator_slot_conflicts(self):
266 + """
267 + This function solves slot conflicts which can
268 + be solved by simply choosing one of the conflicting
269 + packages and removing all the other ones.
270 + It is able to solve somewhat more complex cases where
271 + conflicts can only be solved simultaniously.
272 + """
273 + debug = "--debug" in self._frozen_config.myopts
274 + selective = "selective" in self._dynamic_config.myparams
275 +
276 + # List all conflicts. Ignore those that involve slot operator rebuilds
277 + # as the logic there needs special slot conflict behavior which isn't
278 + # provided by this function.
279 + conflicts = []
280 + for conflict in self._dynamic_config._package_tracker.slot_conflicts():
281 + slot_key = conflict.root, conflict.atom
282 + if slot_key not in self._dynamic_config._slot_operator_replace_installed:
283 + conflicts.append(conflict)
284 +
285 + if not conflicts:
286 + return
287 +
288 + conflict_graph, pkg_to_conflict, conflict_pkgs, non_conflict_node = \
289 + self._get_conflicts_data(conflicts)
290 +
291 if debug:
292 writemsg_level(
293 "\n!!! Slot conflict graph:\n",
294 @@ -1091,14 +1268,20 @@ class depgraph(object):
295 # 'forced' set.
296 forced = set([non_conflict_node])
297 unexplored = set([non_conflict_node])
298 + # Keep track of packages for which another conflicting
299 + # package has already been choosen. Discourage those
300 + # in case the choice between several packages has to be
301 + # made.
302 + discouraged = set()
303 # or_tuples get special handling. We first explore
304 # all packages in the hope of having forced one of
305 # the packages in the tuple. This way we don't have
306 # to choose one.
307 unexplored_tuples = set()
308
309 - while unexplored:
310 + while unexplored or unexplored_tuples:
311 # Handle all unexplored packages.
312 + new_discouraged = set()
313 while unexplored:
314 node = unexplored.pop()
315 for child in conflict_graph.child_nodes(node):
316 @@ -1107,30 +1290,129 @@ class depgraph(object):
317 forced.add(child)
318 if isinstance(child, Package):
319 unexplored.add(child)
320 + for other in pkg_to_conflict.get(child, []):
321 + if other in forced or other in discouraged:
322 + continue
323 + new_discouraged.add(other)
324 else:
325 unexplored_tuples.add(child)
326 + for other in pkg_to_conflict[child[0]]:
327 + if other in child:
328 + continue
329 + if other in forced or other in discouraged:
330 + continue
331 + new_discouraged.add(other)
332 +
333 + # Now mark packages which aren't forced yet
334 + # but would cause a conflict if they were as
335 + # discouraged.
336 + while new_discouraged:
337 + pkg = new_discouraged.pop()
338 + discouraged.add(pkg)
339 + for parent in conflict_graph.parent_nodes(pkg):
340 + if parent in forced or parent in discouraged:
341 + continue
342 + if isinstance(parent, Package):
343 + new_discouraged.add(parent)
344 + else:
345 + if all(other in discouraged and other not in forced \
346 + for other in parent):
347 + new_discouraged.add(parent)
348
349 # Now handle unexplored or_tuples. Move on with packages
350 # once we had to choose one.
351 - while unexplored_tuples:
352 - nodes = unexplored_tuples.pop()
353 + remaining_unexplored_tuples = set()
354 + for nodes in unexplored_tuples:
355 if any(node in forced for node in nodes):
356 - # At least one of the packages in the
357 - # tuple is already forced, which means the
358 - # dependency represented by this tuple
359 - # is satisfied.
360 + # Already satisfied.
361 continue
362
363 - # We now have to choose one of packages in the tuple.
364 - # In theory one could solve more conflicts if we'd be
365 - # able to try different choices here, but that has lots
366 - # of other problems. For now choose the package that was
367 - # pulled first, as this should be the most desirable choice
368 - # (otherwise it wouldn't have been the first one).
369 - forced.add(nodes[0])
370 - unexplored.add(nodes[0])
371 + is_superset = False
372 + for other_nodes in unexplored_tuples:
373 + if other_nodes is nodes:
374 + continue
375 + if set(other_nodes).issubset(nodes):
376 + is_superset = True
377 + break
378 + if is_superset:
379 + continue
380 +
381 + non_dicouraged = list(node for node in nodes \
382 + if node not in discouraged)
383 +
384 + if len(non_dicouraged) == 1:
385 + forced.add(non_dicouraged[0])
386 + unexplored.add(non_dicouraged[0])
387 + continue
388 +
389 + remaining_unexplored_tuples.add(nodes)
390 +
391 + unexplored_tuples = remaining_unexplored_tuples
392 + if unexplored:
393 + continue
394 +
395 + # For each package compute if it is discouraged
396 + # and if all its dependencies are already forced.
397 + status = collections.defaultdict(set)
398 + for nodes in unexplored_tuples:
399 + for node in nodes:
400 + is_discouraged = node in discouraged
401 +
402 + is_satisfied = True
403 + for child in conflict_graph.child_nodes(node):
404 + if isinstance(child, Package):
405 + if child not in forced:
406 + is_satisfied = False
407 + break
408 + else:
409 + if any(child_node not in forced for child_node in child):
410 + is_satisfied = False
411 + break
412 + status[(not is_discouraged, is_satisfied)].add(node)
413 +
414 + # Go through the state combinations to find
415 + # an or_tuple with the least chance of
416 + # causing a conflict. At this point we resort to
417 + # educted guessing and force one package.
418 + for state in (True, True), (True, False), (False, True), (False, False):
419 + selected = {}
420 + for nodes in unexplored_tuples:
421 + candidates = []
422 + for node in nodes:
423 + if node in status[state]:
424 + candidates.append(node)
425 + if candidates:
426 + selected[nodes] = candidates
427 +
428 + # Search for the or_tuple with the least
429 + # number of candidates.
430 + choosen = None
431 + for nodes in selected:
432 + candidates = selected[nodes]
433 + if choosen is None or len(choosen) > len(candidates):
434 + choosen = candidates
435 +
436 + if not choosen:
437 + continue
438 +
439 + if selective:
440 + # Prefer installed packages.
441 + selected = None
442 + for node in choosen:
443 + if node.installed:
444 + selected = node
445 + break
446 + if selected is None:
447 + selected = choosen[0]
448 + else:
449 + # Prefer the first package.
450 + selected = choosen[0]
451 +
452 + forced.add(selected)
453 + unexplored.add(selected)
454 break
455
456 +
457 # Remove 'non_conflict_node' and or_tuples from 'forced'.
458 forced = set(pkg for pkg in forced if isinstance(pkg, Package))
459 non_forced = set(pkg for pkg in conflict_pkgs if pkg not in forced)
460 @@ -1206,9 +1488,10 @@ class depgraph(object):
461 If there are any slot conflicts and backtracking is enabled,
462 _complete_graph should complete the graph before this method
463 is called, so that all relevant reverse dependencies are
464 - available for use in backtracking decisions.
465 + available for use during conflict resolution.
466 """
467 -
468 + self._solve_non_slot_operator_slot_conflicts()
469 + self._extend_slot_operator_conflicts()
470 self._solve_non_slot_operator_slot_conflicts()
471
472 for conflict in self._dynamic_config._package_tracker.slot_conflicts():
473 @@ -1842,7 +2125,7 @@ class depgraph(object):
474 return frozenset(x.unevaluated_atom for
475 x in selected_atoms)
476
477 - def _iter_similar_available(self, graph_pkg, atom, autounmask_level=None):
478 + def _iter_similar_available(self, graph_pkg, atom, autounmask_level=None, installed=False):
479 """
480 Given a package that's in the graph, do a rough check to
481 see if a similar package is available to install. The given
482 @@ -1859,7 +2142,7 @@ class depgraph(object):
483 if pkg.cp != graph_pkg.cp:
484 # discard old-style virtual match
485 continue
486 - if pkg.installed:
487 + if pkg.installed and not installed:
488 continue
489 if pkg in self._dynamic_config._runtime_pkg_mask:
490 continue
491 @@ -2176,7 +2459,7 @@ class depgraph(object):
492
493 return (existing_node, matches)
494
495 - def _add_pkg(self, pkg, dep):
496 + def _add_pkg(self, pkg, dep, reuse_existing=True):
497 """
498 Adds a package to the depgraph, queues dependencies, and handles
499 slot conflicts.
500 @@ -2268,7 +2551,7 @@ class depgraph(object):
501 existing_node, existing_node_matches = \
502 self._check_slot_conflict(pkg, dep.atom)
503 if existing_node:
504 - if existing_node_matches:
505 + if existing_node_matches and reuse_existing:
506 # The existing node can be reused.
507 if pkg != existing_node:
508 pkg = existing_node
509 @@ -2415,14 +2698,19 @@ class depgraph(object):
510 pass
511
512 # Remove slot operator dependencies.
513 - slot_key = (pkg.root, pkg.slot_atom)
514 - if slot_key in self._dynamic_config._slot_operator_deps:
515 + for slot_key in list(self._dynamic_config._slot_operator_deps.keys()):
516 self._dynamic_config._slot_operator_deps[slot_key] = \
517 [dep for dep in self._dynamic_config._slot_operator_deps[slot_key] \
518 - if dep.child is not pkg]
519 + if dep.child is not pkg and dep.parent is not pkg]
520 if not self._dynamic_config._slot_operator_deps[slot_key]:
521 del self._dynamic_config._slot_operator_deps[slot_key]
522
523 + # Rebuild tracking data structures.
524 + self._dynamic_config._forced_rebuilds = None
525 + self._dynamic_config._slot_conflict_rebuilds.pop(pkg, None)
526 + for child in self._dynamic_config._slot_conflict_rebuilds:
527 + self._dynamic_config._slot_conflict_rebuilds[child].discard(pkg)
528 +
529 # Remove blockers.
530 self._dynamic_config._blocker_parents.discard(pkg)
531 self._dynamic_config._irrelevant_blockers.discard(pkg)
532 @@ -2430,6 +2718,13 @@ class depgraph(object):
533 self._dynamic_config._blocked_pkgs.discard(pkg)
534 self._dynamic_config._blocked_world_pkgs.pop(pkg, None)
535
536 + # Remove package's unsatisfied dependencies.
537 + _unsatisfied_deps_for_display = []
538 + for (root, atom), info in self._dynamic_config._unsatisfied_deps_for_display:
539 + if info["myparent"] is not pkg:
540 + _unsatisfied_deps_for_display.append(((root, atom), info))
541 + self._dynamic_config._unsatisfied_deps_for_display = _unsatisfied_deps_for_display
542 +
543 for child in children:
544 if not self._dynamic_config.digraph.parent_nodes(child):
545 self._remove_pkg(child)
546 @@ -5630,6 +5925,63 @@ class depgraph(object):
547
548 return pkg, in_graph
549
550 + def _enable_complete_mode(self):
551 + """
552 + Put the depgraph into a mode that causes it to only
553 + select packages that have already been added to the
554 + graph or those that are installed and have not been
555 + scheduled for replacement. Also, toggle the "deep"
556 + parameter so that all dependencies are traversed and
557 + accounted for.
558 + """
559 +
560 + previous_state = {}
561 + previous_state["complete_mode"] = self._dynamic_config._complete_mode
562 + previous_state["_select_atoms"] = self._select_atoms
563 + previous_state["_select_package"] = self._select_package
564 + previous_state["_traverse_ignored_deps"] = \
565 + self._dynamic_config._traverse_ignored_deps
566 + previous_state["deep"] = self._dynamic_config.myparams.get("deep")
567 +
568 + self._dynamic_config._complete_mode = True
569 + self._select_atoms = self._select_atoms_from_graph
570 + if "remove" in self._dynamic_config.myparams:
571 + self._select_package = self._select_pkg_from_installed
572 + else:
573 + self._select_package = self._select_pkg_from_graph
574 + self._dynamic_config._traverse_ignored_deps = True
575 + already_deep = self._dynamic_config.myparams.get("deep") is True
576 + if not already_deep:
577 + self._dynamic_config.myparams["deep"] = True
578 +
579 + # Invalidate the package selection cache, since
580 + # _select_package has just changed implementations.
581 + for trees in self._dynamic_config._filtered_trees.values():
582 + trees["porttree"].dbapi._clear_cache()
583 +
584 + return previous_state
585 +
586 + def _disable_complete_mode(self, previous_state):
587 + """
588 + Reverts the changes made by _enable_complete_mode.
589 + """
590 + self._dynamic_config._complete_mode = previous_state["complete_mode"]
591 + self._select_atoms = previous_state["_select_atoms"]
592 + self._select_package = previous_state["_select_package"]
593 + self._dynamic_config._traverse_ignored_deps = \
594 + previous_state["_traverse_ignored_deps"]
595 +
596 + if previous_state["deep"] is None:
597 + del self._dynamic_config.myparams["deep"]
598 + else:
599 + self._dynamic_config.myparams["deep"] = previous_state["deep"]
600 +
601 + # Invalidate the package selection cache, since
602 + # _select_package has just changed implementations.
603 + for trees in self._dynamic_config._filtered_trees.values():
604 + trees["porttree"].dbapi._clear_cache()
605 +
606 +
607 def _complete_graph(self, required_sets=None):
608 """
609 Add any deep dependencies of required sets (args, system, world) that
610 @@ -5713,27 +6065,8 @@ class depgraph(object):
611
612 self._load_vdb()
613
614 - # Put the depgraph into a mode that causes it to only
615 - # select packages that have already been added to the
616 - # graph or those that are installed and have not been
617 - # scheduled for replacement. Also, toggle the "deep"
618 - # parameter so that all dependencies are traversed and
619 - # accounted for.
620 - self._dynamic_config._complete_mode = True
621 - self._select_atoms = self._select_atoms_from_graph
622 - if "remove" in self._dynamic_config.myparams:
623 - self._select_package = self._select_pkg_from_installed
624 - else:
625 - self._select_package = self._select_pkg_from_graph
626 - self._dynamic_config._traverse_ignored_deps = True
627 - already_deep = self._dynamic_config.myparams.get("deep") is True
628 - if not already_deep:
629 - self._dynamic_config.myparams["deep"] = True
630 -
631 - # Invalidate the package selection cache, since
632 - # _select_package has just changed implementations.
633 - for trees in self._dynamic_config._filtered_trees.values():
634 - trees["porttree"].dbapi._clear_cache()
635 + previous_state = self._enable_complete_mode()
636 + already_deep = previous_state["deep"] is True
637
638 args = self._dynamic_config._initial_arg_list[:]
639 for root in self._frozen_config.roots:
640 @@ -5778,12 +6111,12 @@ class depgraph(object):
641 Dependency(atom=atom, root=arg.root_config.root,
642 parent=arg))
643
644 - if True:
645 - if self._dynamic_config._ignored_deps:
646 - self._dynamic_config._dep_stack.extend(self._dynamic_config._ignored_deps)
647 - self._dynamic_config._ignored_deps = []
648 - if not self._create_graph(allow_unsatisfied=True):
649 - return 0
650 + if self._dynamic_config._ignored_deps:
651 + self._dynamic_config._dep_stack.extend(self._dynamic_config._ignored_deps)
652 + self._dynamic_config._ignored_deps = []
653 +
654 + ret = 1
655 + if self._create_graph(allow_unsatisfied=True):
656 # Check the unsatisfied deps to see if any initially satisfied deps
657 # will become unsatisfied due to an upgrade. Initially unsatisfied
658 # deps are irrelevant since we only want to avoid breaking deps
659 @@ -5802,10 +6135,17 @@ class depgraph(object):
660 # (possibly solvable via backtracking).
661 pkg = matches[-1] # highest match
662 if not self._add_pkg(pkg, dep):
663 - return 0
664 + ret = 0
665 + break
666 if not self._create_graph(allow_unsatisfied=True):
667 - return 0
668 - return 1
669 + ret = 0
670 + break
671 + else:
672 + ret = 0
673 +
674 + self._disable_complete_mode(previous_state)
675 +
676 + return ret
677
678 def _pkg(self, cpv, type_name, root_config, installed=False,
679 onlydeps=False, myrepo = None):
680 @@ -7285,8 +7625,9 @@ class depgraph(object):
681 if "--tree" in self._frozen_config.myopts:
682 mylist = tuple(reversed(mylist))
683
684 - display = Display()
685 + self._compute_abi_rebuild_info()
686
687 + display = Display()
688 return display(self, mylist, favorites, verbosity)
689
690 def _display_autounmask(self):
691 diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output.py
692 index 5f550be..72a1ec2 100644
693 --- a/pym/_emerge/resolver/output.py
694 +++ b/pym/_emerge/resolver/output.py
695 @@ -836,8 +836,9 @@ class Display(object):
696 self._get_installed_best(pkg, pkg_info)
697 if ordered and pkg_info.merge and \
698 not pkg_info.attr_display.new:
699 - for arg, atom in depgraph._iter_atoms_for_pkg(pkg):
700 - if arg.force_reinstall:
701 + forced_rebuilds = depgraph._dynamic_config._forced_rebuilds.get(pkg.root, {})
702 + for child in forced_rebuilds:
703 + if pkg in forced_rebuilds[child]:
704 pkg_info.attr_display.force_reinstall = True
705 break
706
707 --
708 1.8.3.2

Replies