Gentoo Archives: gentoo-portage-dev

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

Attachments

File name MIME type
signature.asc application/pgp-signature