From 88462b25ff80d247984319eb93d7d41fb5299874 Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Sat, 4 Jul 2009 05:32:59 +0000 Subject: Bug #275217 - Part 5 - When a slot conflict occurs, mask the first package that got pulled in and restart the calculation. Thanks to Sebastian Mingramm (few) for the initial patch which I added some additional features to: * display message about missed updates * cache frozen_config instance, to optimize performance * disable backtracking if it fails, fall back to a normal dep calculation + error message. svn path=/main/trunk/; revision=13769 --- pym/_emerge/depgraph.py | 112 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 8 deletions(-) (limited to 'pym/_emerge/depgraph.py') diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index 144f57bc9..509f298e3 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -91,8 +91,10 @@ class _frozen_depgraph_config(object): class _dynamic_depgraph_config(object): - def __init__(self, depgraph, myparams): - self.myparams = myparams + def __init__(self, depgraph, myparams, allow_backtracking, + runtime_pkg_mask): + self.myparams = myparams.copy() + self._allow_backtracking = allow_backtracking # Maps slot atom to package for each Package added to the graph. self._slot_pkg_map = {} # Maps nodes to the reasons they were selected for reinstallation. @@ -155,6 +157,13 @@ class _dynamic_depgraph_config(object): self._initially_unsatisfied_deps = [] self._ignored_deps = [] self._highest_pkg_cache = {} + if runtime_pkg_mask is None: + runtime_pkg_mask = {} + else: + runtime_pkg_mask = dict((k, v.copy()) for (k, v) in \ + runtime_pkg_mask.iteritems()) + self._runtime_pkg_mask = runtime_pkg_mask + self._need_restart = False for myroot in depgraph._frozen_config.trees: self._slot_pkg_map[myroot] = {} @@ -245,16 +254,63 @@ class depgraph(object): _dep_keys = ["DEPEND", "RDEPEND", "PDEPEND"] def __init__(self, settings, trees, myopts, myparams, spinner, - frozen_config=None): + frozen_config=None, runtime_pkg_mask=None, allow_backtracking=False): if frozen_config is None: frozen_config = _frozen_depgraph_config(settings, trees, myopts, spinner) self._frozen_config = frozen_config - self._dynamic_config = _dynamic_depgraph_config(self, myparams) + self._dynamic_config = _dynamic_depgraph_config(self, myparams, + allow_backtracking, runtime_pkg_mask) self._select_atoms = self._select_atoms_highest_available self._select_package = self._select_pkg_highest_available + def _show_missed_update(self): + + missed_updates = {} + for pkg, mask_reasons in \ + self._dynamic_config._runtime_pkg_mask.iteritems(): + if mask_reasons.get("slot conflict"): + if pkg.slot_atom in missed_updates: + other_pkg, parent_atoms = missed_updates[pkg.slot_atom] + if other_pkg > pkg: + continue + missed_updates[pkg.slot_atom] = \ + (pkg, mask_reasons["slot conflict"]) + + if not missed_updates: + return + + msg = [] + msg.append("\n!!! One or more updates have been skipped due to " + \ + "a dependency conflict:\n\n") + + indent = " " + for pkg, parent_atoms in missed_updates.itervalues(): + msg.append(str(pkg.slot_atom)) + msg.append("\n\n") + + for parent, atom in parent_atoms: + msg.append(indent) + msg.append(str(pkg)) + + msg.append(" conflicts with\n") + for parent, atom in parent_atoms: + msg.append(2*indent) + if isinstance(parent, + (PackageArg, AtomArg)): + # For PackageArg and AtomArg types, it's + # redundant to display the atom attribute. + msg.append(str(parent)) + else: + # Display the specific atom from SetArg or + # Package types. + msg.append("%s required by %s" % (atom, parent)) + msg.append("\n") + msg.append("\n") + sys.stderr.write("".join(msg)) + sys.stderr.flush() + def _show_slot_collision_notice(self): """Show an informational message advising the user to mask one of the the packages. In some cases it may be possible to resolve this @@ -696,6 +752,31 @@ class depgraph(object): (dep.parent, dep.atom)) return 1 else: + # A slot conflict has occurred. + if self._dynamic_config._allow_backtracking and \ + not self._accept_blocker_conflicts(): + self._add_slot_conflict(pkg) + if dep.atom is not None and dep.parent is not None: + self._add_parent_atom(pkg, (dep.parent, dep.atom)) + if arg_atoms: + for parent_atom in arg_atoms: + parent, atom = parent_atom + self._add_parent_atom(pkg, parent_atom) + self._process_slot_conflicts() + + parent_atoms = \ + self._dynamic_config._parent_atoms.get(pkg, set()) + if parent_atoms: + parent_atoms = self._dynamic_config._slot_conflict_parent_atoms.intersection(parent_atoms) + if pkg >= existing_node: + # We only care about the parent atoms + # when they trigger a downgrade. + parent_atoms = set() + + self._dynamic_config._runtime_pkg_mask.setdefault( + existing_node, {})["slot conflict"] = parent_atoms + self._dynamic_config._need_restart = True + return 0 # A slot collision has occurred. Sometimes this coincides # with unresolvable blockers, so the slot collision will be @@ -1380,8 +1461,10 @@ class depgraph(object): if isinstance(arg, PackageArg): if not self._add_pkg(arg.package, dep) or \ not self._create_graph(): - sys.stderr.write(("\n\n!!! Problem resolving " + \ - "dependencies for %s\n") % arg.arg) + if not self._dynamic_config._need_restart: + sys.stderr.write(("\n\n!!! Problem " + \ + "resolving dependencies for %s\n") % \ + arg.arg) return 0, myfavorites continue if debug: @@ -1441,7 +1524,9 @@ class depgraph(object): # so that later dep_check() calls can use it as feedback # for making more consistent atom selections. if not self._add_pkg(pkg, dep): - if isinstance(arg, SetArg): + if self._dynamic_config._need_restart: + pass + elif isinstance(arg, SetArg): sys.stderr.write(("\n\n!!! Problem resolving " + \ "dependencies for %s from %s\n") % \ (atom, arg.arg)) @@ -1945,6 +2030,9 @@ class depgraph(object): for pkg in self._iter_match_pkgs(root_config, pkg_type, atom, onlydeps=onlydeps): + if pkg in self._dynamic_config._runtime_pkg_mask: + # The package has been masked by the backtracking logic + continue cpv = pkg.cpv # Make --noreplace take precedence over --newuse. if not pkg.installed and noreplace and \ @@ -4212,8 +4300,10 @@ class depgraph(object): if self._dynamic_config._unsatisfied_blockers_for_display is not None: self._show_unsatisfied_blockers( self._dynamic_config._unsatisfied_blockers_for_display) - else: + elif self._dynamic_config._slot_collision_info: self._show_slot_collision_notice() + else: + self._show_missed_update() # TODO: Add generic support for "set problem" handlers so that # the below warnings aren't special cases for world only. @@ -4572,6 +4662,12 @@ class depgraph(object): graph in order to avoid making a potentially unsafe decision. """ + def need_restart(self): + return self._dynamic_config._need_restart + + def get_runtime_pkg_mask(self): + return self._dynamic_config._runtime_pkg_mask.copy() + class _dep_check_composite_db(portage.dbapi): """ A dbapi-like interface that is optimized for use in dep_check() calls. -- cgit v1.2.3-1-g7c22