From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) by finch.gentoo.org (Postfix) with ESMTP id AB96D1387FD for ; Sat, 5 Apr 2014 06:17:26 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id D7D68E09C1; Sat, 5 Apr 2014 06:17:21 +0000 (UTC) Received: from smtp.gentoo.org (smtp.gentoo.org [140.211.166.183]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 23286E09B3 for ; Sat, 5 Apr 2014 06:17:21 +0000 (UTC) Received: from sf (unknown [178.124.107.239]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) (Authenticated sender: slyfox) by smtp.gentoo.org (Postfix) with ESMTPSA id 1B4AF33FE96; Sat, 5 Apr 2014 06:17:18 +0000 (UTC) Date: Sat, 5 Apr 2014 09:16:05 +0300 From: Sergei Trofimovich To: gentoo-portage-dev@lists.gentoo.org Cc: SebastianLuther@gmx.de Subject: Re: [gentoo-portage-dev] [PATCH] Introduce depgraph._extend_slot_operator_conflicts Message-ID: <20140405091605.460bb5bc@sf> In-Reply-To: <1393528129-31216-2-git-send-email-SebastianLuther@gmx.de> References: <1393528129-31216-1-git-send-email-SebastianLuther@gmx.de> <1393528129-31216-2-git-send-email-SebastianLuther@gmx.de> X-Mailer: Claws Mail 3.9.0 (GTK+ 2.24.22; x86_64-pc-linux-gnu) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-portage-dev@lists.gentoo.org Reply-to: gentoo-portage-dev@lists.gentoo.org Mime-Version: 1.0 Content-Type: multipart/signed; micalg=PGP-SHA1; boundary="Sig_/RCOsp6p/RvUDOFpvHZUY7l9"; protocol="application/pgp-signature" X-Archives-Salt: cd981ae0-0315-433d-8e0a-96e2641edaae X-Archives-Hash: 35715e3e58d1368e0af3cb92722c0d18 --Sig_/RCOsp6p/RvUDOFpvHZUY7l9 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: quoted-printable On Thu, 27 Feb 2014 20:08:49 +0100 Sebastian Luther wrote: > This function allows emerge to solve more slot conflicts without > backtracking. >=20 > It does this by creating more conflicts for packages with built slot > operator dependencies. This gives more freedom to > depgraph._solve_non_slot_operator_slot_conflicts, which is then able > to solve conflicts it wouldn't have otherwise. > --- Nice patch! For haskell packages it makes huge difference. My typical workflow is: - bump some package foo and it's subslot - emerge -1 foo -j9=20 - see what will get rebuilt/broken by subslot rebuild If package would trigger 500 rebuilds portage had no chance to find a solution with backtracks. Now sometimes it does. Thanks! > pym/_emerge/depgraph.py | 483 +++++++++++++++++++++++++++++++++++= ------ > pym/_emerge/resolver/output.py | 5 +- > 2 files changed, 415 insertions(+), 73 deletions(-) >=20 > diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py > index 835bd6b..970a9c7 100644 > --- a/pym/_emerge/depgraph.py > +++ b/pym/_emerge/depgraph.py > @@ -427,6 +427,12 @@ class _dynamic_depgraph_config(object): > # Track missed updates caused by solved conflicts. > self._conflict_missed_update =3D collections.defaultdict(dict) > =20 > + # Rebuilds caused by slot conflicts. > + self._slot_conflict_rebuilds =3D {} > + # Rebuilds caused by missed slot operator updates or > + # slot conflicts. > + self._forced_rebuilds =3D None > + > for myroot in depgraph._frozen_config.trees: > self.sets[myroot] =3D _depgraph_sets() > vardb =3D depgraph._frozen_config.trees[myroot]["vartree"].dbapi > @@ -614,6 +620,9 @@ class depgraph(object): > Fill self._forced_rebuilds with packages that cause rebuilds. > """ > =20 > + if self._dynamic_config._forced_rebuilds is not None: > + return > + > debug =3D "--debug" in self._frozen_config.myopts > =20 > # Get all atoms that might have caused a forced rebuild. > @@ -687,19 +696,23 @@ class depgraph(object): > writemsg_level("\n\n", > level=3Dlogging.DEBUG, noiselevel=3D-1) > =20 > - self._forced_rebuilds =3D forced_rebuilds > + for child, parents in self._dynamic_config._slot_conflict_rebuilds.ite= ms(): > + forced_rebuilds.setdefault(child.root, {}).setdefault(child, set()).u= pdate(parents) > + > + self._dynamic_config._forced_rebuilds =3D forced_rebuilds > =20 > def _show_abi_rebuild_info(self): > =20 > - if not self._forced_rebuilds: > + forced_rebuilds =3D self._dynamic_config._forced_rebuilds > + if not forced_rebuilds: > return > =20 > writemsg_stdout("\nThe following packages are causing rebuilds:\n\n", = noiselevel=3D-1) > =20 > - for root in self._forced_rebuilds: > - for child in self._forced_rebuilds[root]: > + for root in forced_rebuilds: > + for child in forced_rebuilds[root]: > writemsg_stdout(" %s causes rebuilds for:\n" % (child,), noiselevel= =3D-1) > - for parent in self._forced_rebuilds[root][child]: > + for parent in forced_rebuilds[root][child]: > writemsg_stdout(" %s\n" % (parent,), noiselevel=3D-1) > =20 > def _show_ignored_binaries(self): > @@ -968,16 +981,16 @@ class depgraph(object): > writemsg(line + '\n', noiselevel=3D-1) > writemsg('\n', noiselevel=3D-1) > =20 > - def _solve_non_slot_operator_slot_conflicts(self): > + > + def _extend_slot_operator_conflicts(self): > """ > - This function solves slot conflicts which can > - be solved by simply choosing one of the conflicting > - and removing all the other ones. > - It is able to solve somewhat more complex cases where > - conflicts can only be solved simultaniously. > + This function searches for packages that cause > + slot conflicts by dependening on conflict packages > + with built slot operator deps. If such a package > + is found an alternative package is pulled in with > + the hope that the alternative package would not > + cuase the slot conflict. > """ > - debug =3D "--debug" in self._frozen_config.myopts > - > # List all conflicts. Ignore those that involve slot operator rebuilds > # as the logic there needs special slot conflict behavior which isn't > # provided by this function. > @@ -990,9 +1003,133 @@ class depgraph(object): > if not conflicts: > return > =20 > - # Get a set of all conflicting packages. > + # Compute a mapping from parent packages to hard > + # required conflict packages. > + conflict_parents =3D collections.defaultdict(list) > + for conflict in conflicts: > + all_parent_atoms =3D set() > + for pkg in conflict: > + all_parent_atoms.update( > + self._dynamic_config._parent_atoms.get(pkg, [])) > + > + for parent, atom in all_parent_atoms: > + atom_set =3D InternalPackageSet( > + initial_atoms=3D(atom,), allow_repo=3DTrue) > + > + for pkg in conflict: > + if not atom_set.findAtomForPackage(pkg, \ > + modified_use=3Dself._pkg_use_enabled(pkg)): > + conflict_parents[parent].append((pkg, atom)) > + > + def _iter_alternatives(pkg): > + """ > + A wrapper around _iter_similar_available that > + deals with autounmask. > + """ > + tried =3D set() > + for other in self._iter_similar_available(pkg, > + Atom(pkg.cp), autounmask_level=3DNone, installed=3DFalse): > + tried.add(other) > + yield other, None > + > + if self._dynamic_config._autounmask is not True: > + return > + > + for autounmask_level in self._autounmask_levels(): > + for other in self._iter_similar_available(pkg, > + Atom(pkg.cp), autounmask_level=3Dautounmask_level, installed=3DFals= e): > + if other not in tried: > + tried.add(other) > + yield other, autounmask_level > + > + > + # Compute a list of possible alternatives > + # for each conflict parent. > + parent_matches =3D {} > + for parent in conflict_parents: > + slot_op_children =3D [] > + for child, atom in conflict_parents[parent]: > + if atom.slot_operator =3D=3D "=3D" and parent.built: > + slot_op_children.append(child) > + > + if not slot_op_children: > + # This parent doesn't depend with a built slot > + # operator on a conflict package. > + continue > + > + matches =3D [] > + highest_ebuilds =3D {} > + for other, autounmask_level in _iter_alternatives(parent): > + if parent.slot_atom !=3D other.slot_atom and parent.cpv !=3D other.c= pv: > + # 'other' is not a replacement for 'parent'. > + continue > + > + highest_ebuild =3D highest_ebuilds.get(autounmask_level) > + if not other.built and \ > + (highest_ebuild is None or highest_ebuild < other): > + # Remember the highest ebuild for > + # downgrade testing later. > + highest_ebuilds[autounmask_level] =3D other > + > + # Go through 'parents' parents and check if 'other' > + # satisfies their dependencies. Ignore built slot > + # operator deps. > + is_match =3D True > + for pparent, patom in self._dynamic_config._parent_atoms.get(parent,= []): > + if patom.slot_operator =3D=3D "=3D" and pparent.built and parent.bu= ilt: > + continue > + > + atom_set =3D InternalPackageSet( > + initial_atoms=3D(patom,), allow_repo=3DTrue) > + > + if not atom_set.findAtomForPackage(other, \ > + modified_use=3Dself._pkg_use_enabled(other)): > + is_match =3D False > + break > + > + if is_match: > + matches.append((other, autounmask_level)) > + > + # Filter downgrades. > + no_downgrade_matches =3D [] > + for match, autounmask_level in matches: > + highest_ebuild =3D highest_ebuilds.get(autounmask_level) > + if highest_ebuild and match >=3D highest_ebuild: > + no_downgrade_matches.append(match) > + > + parent_matches[parent] =3D no_downgrade_matches > + > + # Pull in alternatives. > + for parent in parent_matches: > + for match in parent_matches[parent]: > + other.depth =3D parent.depth > + dep =3D Dependency(atom=3DAtom('=3D' + match.cpv), child=3Dmatch, > + parent=3DNone, root=3Dmatch.root) > + > + if not self._add_pkg(match, dep, reuse_existing=3DFalse) or \ > + not self._create_graph(): > + self._remove_pkg(match) > + continue > + > + # Record forces rebuilds. > + for child, atom in conflict_parents[parent]: > + self._dynamic_config._slot_conflict_rebuilds.setdefault( > + child, set()).add(match) > + break > + > + > + def _get_conflicts_data(self, conflicts): > + """ > + This function creates the conflict graph and some > + helper data structures for _solve_non_slot_operator_slot_conflicts. > + """ > + selective =3D "selective" in self._dynamic_config.myparams > + > + pkg_to_conflict =3D {} > conflict_pkgs =3D set() > for conflict in conflicts: > + for pkg in conflict: > + pkg_to_conflict[pkg] =3D conflict > conflict_pkgs.update(conflict) > =20 > # Get the list of other packages which are only > @@ -1050,7 +1187,7 @@ class depgraph(object): > self._dynamic_config._parent_atoms.get(pkg, [])) > =20 > for parent, atom in all_parent_atoms: > - is_arg_parent =3D isinstance(parent, AtomArg) > + is_arg_parent =3D isinstance(parent, AtomArg) and not selective > =20 > if parent not in conflict_pkgs and \ > parent not in indirect_conflict_pkgs: > @@ -1072,7 +1209,11 @@ class depgraph(object): > conflict_graph.add(matched[0], parent) > else: > # More than one packages matched, but not all. > - conflict_graph.add(or_tuple(matched), parent) > + match_tuple =3D or_tuple(matched) > + conflict_graph.add(match_tuple, parent) > + for match in matched: > + conflict_graph.add(match, match_tuple) > + > =20 > for pkg in indirect_conflict_pkgs: > for parent, atom in self._dynamic_config._parent_atoms.get(pkg, []): > @@ -1081,6 +1222,42 @@ class depgraph(object): > parent =3D non_conflict_node > conflict_graph.add(pkg, parent) > =20 > + for conflict in conflicts: > + if all(not conflict_graph.parent_nodes(node) for node in conflict): > + # No conflict parents, all parents accept all conflict packages. > + # This happens when _extend_slot_operator_conflict pulls in > + # alternative parents for other conflict paclages. > + conflict_graph.add(or_tuple(conflict), non_conflict_node) > + > + return conflict_graph, pkg_to_conflict, conflict_pkgs, non_conflict_no= de > + > + > + def _solve_non_slot_operator_slot_conflicts(self): > + """ > + This function solves slot conflicts which can > + be solved by simply choosing one of the conflicting > + packages and removing all the other ones. > + It is able to solve somewhat more complex cases where > + conflicts can only be solved simultaniously. > + """ > + debug =3D "--debug" in self._frozen_config.myopts > + selective =3D "selective" in self._dynamic_config.myparams > + > + # List all conflicts. Ignore those that involve slot operator rebuilds > + # as the logic there needs special slot conflict behavior which isn't > + # provided by this function. > + conflicts =3D [] > + for conflict in self._dynamic_config._package_tracker.slot_conflicts(): > + slot_key =3D conflict.root, conflict.atom > + if slot_key not in self._dynamic_config._slot_operator_replace_instal= led: > + conflicts.append(conflict) > + > + if not conflicts: > + return > + > + conflict_graph, pkg_to_conflict, conflict_pkgs, non_conflict_node =3D \ > + self._get_conflicts_data(conflicts) > + > if debug: > writemsg_level( > "\n!!! Slot conflict graph:\n", > @@ -1091,14 +1268,20 @@ class depgraph(object): > # 'forced' set. > forced =3D set([non_conflict_node]) > unexplored =3D set([non_conflict_node]) > + # Keep track of packages for which another conflicting > + # package has already been choosen. Discourage those > + # in case the choice between several packages has to be > + # made. > + discouraged =3D set() > # or_tuples get special handling. We first explore > # all packages in the hope of having forced one of > # the packages in the tuple. This way we don't have > # to choose one. > unexplored_tuples =3D set() > =20 > - while unexplored: > + while unexplored or unexplored_tuples: > # Handle all unexplored packages. > + new_discouraged =3D set() > while unexplored: > node =3D unexplored.pop() > for child in conflict_graph.child_nodes(node): > @@ -1107,30 +1290,129 @@ class depgraph(object): > forced.add(child) > if isinstance(child, Package): > unexplored.add(child) > + for other in pkg_to_conflict.get(child, []): > + if other in forced or other in discouraged: > + continue > + new_discouraged.add(other) > else: > unexplored_tuples.add(child) > + for other in pkg_to_conflict[child[0]]: > + if other in child: > + continue > + if other in forced or other in discouraged: > + continue > + new_discouraged.add(other) > + > + # Now mark packages which aren't forced yet > + # but would cause a conflict if they were as > + # discouraged. > + while new_discouraged: > + pkg =3D new_discouraged.pop() > + discouraged.add(pkg) > + for parent in conflict_graph.parent_nodes(pkg): > + if parent in forced or parent in discouraged: > + continue > + if isinstance(parent, Package): > + new_discouraged.add(parent) > + else: > + if all(other in discouraged and other not in forced \ > + for other in parent): > + new_discouraged.add(parent) > =20 > # Now handle unexplored or_tuples. Move on with packages > # once we had to choose one. > - while unexplored_tuples: > - nodes =3D unexplored_tuples.pop() > + remaining_unexplored_tuples =3D set() > + for nodes in unexplored_tuples: > if any(node in forced for node in nodes): > - # At least one of the packages in the > - # tuple is already forced, which means the > - # dependency represented by this tuple > - # is satisfied. > + # Already satisfied. > continue > =20 > - # We now have to choose one of packages in the tuple. > - # In theory one could solve more conflicts if we'd be > - # able to try different choices here, but that has lots > - # of other problems. For now choose the package that was > - # pulled first, as this should be the most desirable choice > - # (otherwise it wouldn't have been the first one). > - forced.add(nodes[0]) > - unexplored.add(nodes[0]) > + is_superset =3D False > + for other_nodes in unexplored_tuples: > + if other_nodes is nodes: > + continue > + if set(other_nodes).issubset(nodes): > + is_superset =3D True > + break > + if is_superset: > + continue > + > + non_dicouraged =3D list(node for node in nodes \ > + if node not in discouraged) > + > + if len(non_dicouraged) =3D=3D 1: > + forced.add(non_dicouraged[0]) > + unexplored.add(non_dicouraged[0]) > + continue > + > + remaining_unexplored_tuples.add(nodes) > + > + unexplored_tuples =3D remaining_unexplored_tuples > + if unexplored: > + continue > + > + # For each package compute if it is discouraged > + # and if all its dependencies are already forced. > + status =3D collections.defaultdict(set) > + for nodes in unexplored_tuples: > + for node in nodes: > + is_discouraged =3D node in discouraged > + > + is_satisfied =3D True > + for child in conflict_graph.child_nodes(node): > + if isinstance(child, Package): > + if child not in forced: > + is_satisfied =3D False > + break > + else: > + if any(child_node not in forced for child_node in child): > + is_satisfied =3D False > + break > + status[(not is_discouraged, is_satisfied)].add(node) > + > + # Go through the state combinations to find > + # an or_tuple with the least chance of > + # causing a conflict. At this point we resort to > + # educted guessing and force one package. > + for state in (True, True), (True, False), (False, True), (False, Fals= e): > + selected =3D {} > + for nodes in unexplored_tuples: > + candidates =3D [] > + for node in nodes: > + if node in status[state]: > + candidates.append(node) > + if candidates: > + selected[nodes] =3D candidates > + > + # Search for the or_tuple with the least > + # number of candidates. > + choosen =3D None > + for nodes in selected: > + candidates =3D selected[nodes] > + if choosen is None or len(choosen) > len(candidates): > + choosen =3D candidates > + > + if not choosen: > + continue > + > + if selective: > + # Prefer installed packages. > + selected =3D None > + for node in choosen: > + if node.installed: > + selected =3D node > + break > + if selected is None: > + selected =3D choosen[0] > + else: > + # Prefer the first package. > + selected =3D choosen[0] > + > + forced.add(selected) > + unexplored.add(selected) > break > =20 > + > # Remove 'non_conflict_node' and or_tuples from 'forced'. > forced =3D set(pkg for pkg in forced if isinstance(pkg, Package)) > non_forced =3D set(pkg for pkg in conflict_pkgs if pkg not in forced) > @@ -1206,9 +1488,10 @@ class depgraph(object): > If there are any slot conflicts and backtracking is enabled, > _complete_graph should complete the graph before this method > is called, so that all relevant reverse dependencies are > - available for use in backtracking decisions. > + available for use during conflict resolution. > """ > - > + self._solve_non_slot_operator_slot_conflicts() > + self._extend_slot_operator_conflicts() > self._solve_non_slot_operator_slot_conflicts() > =20 > for conflict in self._dynamic_config._package_tracker.slot_conflicts(): > @@ -1842,7 +2125,7 @@ class depgraph(object): > return frozenset(x.unevaluated_atom for > x in selected_atoms) > =20 > - def _iter_similar_available(self, graph_pkg, atom, autounmask_level=3DN= one): > + def _iter_similar_available(self, graph_pkg, atom, autounmask_level=3DN= one, installed=3DFalse): > """ > Given a package that's in the graph, do a rough check to > see if a similar package is available to install. The given > @@ -1859,7 +2142,7 @@ class depgraph(object): > if pkg.cp !=3D graph_pkg.cp: > # discard old-style virtual match > continue > - if pkg.installed: > + if pkg.installed and not installed: > continue > if pkg in self._dynamic_config._runtime_pkg_mask: > continue > @@ -2176,7 +2459,7 @@ class depgraph(object): > =20 > return (existing_node, matches) > =20 > - def _add_pkg(self, pkg, dep): > + def _add_pkg(self, pkg, dep, reuse_existing=3DTrue): > """ > Adds a package to the depgraph, queues dependencies, and handles > slot conflicts. > @@ -2268,7 +2551,7 @@ class depgraph(object): > existing_node, existing_node_matches =3D \ > self._check_slot_conflict(pkg, dep.atom) > if existing_node: > - if existing_node_matches: > + if existing_node_matches and reuse_existing: > # The existing node can be reused. > if pkg !=3D existing_node: > pkg =3D existing_node > @@ -2415,14 +2698,19 @@ class depgraph(object): > pass > =20 > # Remove slot operator dependencies. > - slot_key =3D (pkg.root, pkg.slot_atom) > - if slot_key in self._dynamic_config._slot_operator_deps: > + for slot_key in list(self._dynamic_config._slot_operator_deps.keys()): > self._dynamic_config._slot_operator_deps[slot_key] =3D \ > [dep for dep in self._dynamic_config._slot_operator_deps[slot_key] \ > - if dep.child is not pkg] > + if dep.child is not pkg and dep.parent is not pkg] > if not self._dynamic_config._slot_operator_deps[slot_key]: > del self._dynamic_config._slot_operator_deps[slot_key] > =20 > + # Rebuild tracking data structures. > + self._dynamic_config._forced_rebuilds =3D None > + self._dynamic_config._slot_conflict_rebuilds.pop(pkg, None) > + for child in self._dynamic_config._slot_conflict_rebuilds: > + self._dynamic_config._slot_conflict_rebuilds[child].discard(pkg) > + > # Remove blockers. > self._dynamic_config._blocker_parents.discard(pkg) > self._dynamic_config._irrelevant_blockers.discard(pkg) > @@ -2430,6 +2718,13 @@ class depgraph(object): > self._dynamic_config._blocked_pkgs.discard(pkg) > self._dynamic_config._blocked_world_pkgs.pop(pkg, None) > =20 > + # Remove package's unsatisfied dependencies. > + _unsatisfied_deps_for_display =3D [] > + for (root, atom), info in self._dynamic_config._unsatisfied_deps_for_d= isplay: > + if info["myparent"] is not pkg: > + _unsatisfied_deps_for_display.append(((root, atom), info)) > + self._dynamic_config._unsatisfied_deps_for_display =3D _unsatisfied_de= ps_for_display > + > for child in children: > if not self._dynamic_config.digraph.parent_nodes(child): > self._remove_pkg(child) > @@ -5630,6 +5925,63 @@ class depgraph(object): > =20 > return pkg, in_graph > =20 > + def _enable_complete_mode(self): > + """ > + Put the depgraph into a mode that causes it to only > + select packages that have already been added to the > + graph or those that are installed and have not been > + scheduled for replacement. Also, toggle the "deep" > + parameter so that all dependencies are traversed and > + accounted for. > + """ > + > + previous_state =3D {} > + previous_state["complete_mode"] =3D self._dynamic_config._complete_mode > + previous_state["_select_atoms"] =3D self._select_atoms > + previous_state["_select_package"] =3D self._select_package > + previous_state["_traverse_ignored_deps"] =3D \ > + self._dynamic_config._traverse_ignored_deps > + previous_state["deep"] =3D self._dynamic_config.myparams.get("deep") > + > + self._dynamic_config._complete_mode =3D True > + self._select_atoms =3D self._select_atoms_from_graph > + if "remove" in self._dynamic_config.myparams: > + self._select_package =3D self._select_pkg_from_installed > + else: > + self._select_package =3D self._select_pkg_from_graph > + self._dynamic_config._traverse_ignored_deps =3D True > + already_deep =3D self._dynamic_config.myparams.get("deep") is True > + if not already_deep: > + self._dynamic_config.myparams["deep"] =3D True > + > + # Invalidate the package selection cache, since > + # _select_package has just changed implementations. > + for trees in self._dynamic_config._filtered_trees.values(): > + trees["porttree"].dbapi._clear_cache() > + > + return previous_state > + > + def _disable_complete_mode(self, previous_state): > + """ > + Reverts the changes made by _enable_complete_mode. > + """ > + self._dynamic_config._complete_mode =3D previous_state["complete_mode"] > + self._select_atoms =3D previous_state["_select_atoms"] > + self._select_package =3D previous_state["_select_package"] > + self._dynamic_config._traverse_ignored_deps =3D \ > + previous_state["_traverse_ignored_deps"] > + > + if previous_state["deep"] is None: > + del self._dynamic_config.myparams["deep"] > + else: > + self._dynamic_config.myparams["deep"] =3D previous_state["deep"] > + > + # Invalidate the package selection cache, since > + # _select_package has just changed implementations. > + for trees in self._dynamic_config._filtered_trees.values(): > + trees["porttree"].dbapi._clear_cache() > + > + > def _complete_graph(self, required_sets=3DNone): > """ > Add any deep dependencies of required sets (args, system, world) that > @@ -5713,27 +6065,8 @@ class depgraph(object): > =20 > self._load_vdb() > =20 > - # Put the depgraph into a mode that causes it to only > - # select packages that have already been added to the > - # graph or those that are installed and have not been > - # scheduled for replacement. Also, toggle the "deep" > - # parameter so that all dependencies are traversed and > - # accounted for. > - self._dynamic_config._complete_mode =3D True > - self._select_atoms =3D self._select_atoms_from_graph > - if "remove" in self._dynamic_config.myparams: > - self._select_package =3D self._select_pkg_from_installed > - else: > - self._select_package =3D self._select_pkg_from_graph > - self._dynamic_config._traverse_ignored_deps =3D True > - already_deep =3D self._dynamic_config.myparams.get("deep") is True > - if not already_deep: > - self._dynamic_config.myparams["deep"] =3D True > - > - # Invalidate the package selection cache, since > - # _select_package has just changed implementations. > - for trees in self._dynamic_config._filtered_trees.values(): > - trees["porttree"].dbapi._clear_cache() > + previous_state =3D self._enable_complete_mode() > + already_deep =3D previous_state["deep"] is True > =20 > args =3D self._dynamic_config._initial_arg_list[:] > for root in self._frozen_config.roots: > @@ -5778,12 +6111,12 @@ class depgraph(object): > Dependency(atom=3Datom, root=3Darg.root_config.root, > parent=3Darg)) > =20 > - if True: > - if self._dynamic_config._ignored_deps: > - self._dynamic_config._dep_stack.extend(self._dynamic_config._ignored= _deps) > - self._dynamic_config._ignored_deps =3D [] > - if not self._create_graph(allow_unsatisfied=3DTrue): > - return 0 > + if self._dynamic_config._ignored_deps: > + self._dynamic_config._dep_stack.extend(self._dynamic_config._ignored_= deps) > + self._dynamic_config._ignored_deps =3D [] > + > + ret =3D 1 > + if self._create_graph(allow_unsatisfied=3DTrue): > # Check the unsatisfied deps to see if any initially satisfied deps > # will become unsatisfied due to an upgrade. Initially unsatisfied > # deps are irrelevant since we only want to avoid breaking deps > @@ -5802,10 +6135,17 @@ class depgraph(object): > # (possibly solvable via backtracking). > pkg =3D matches[-1] # highest match > if not self._add_pkg(pkg, dep): > - return 0 > + ret =3D 0 > + break > if not self._create_graph(allow_unsatisfied=3DTrue): > - return 0 > - return 1 > + ret =3D 0 > + break > + else: > + ret =3D 0 > + > + self._disable_complete_mode(previous_state) > + > + return ret > =20 > def _pkg(self, cpv, type_name, root_config, installed=3DFalse, > onlydeps=3DFalse, myrepo =3D None): > @@ -7285,8 +7625,9 @@ class depgraph(object): > if "--tree" in self._frozen_config.myopts: > mylist =3D tuple(reversed(mylist)) > =20 > - display =3D Display() > + self._compute_abi_rebuild_info() > =20 > + display =3D Display() > return display(self, mylist, favorites, verbosity) > =20 > def _display_autounmask(self): > diff --git a/pym/_emerge/resolver/output.py b/pym/_emerge/resolver/output= .py > index 5f550be..72a1ec2 100644 > --- a/pym/_emerge/resolver/output.py > +++ b/pym/_emerge/resolver/output.py > @@ -836,8 +836,9 @@ class Display(object): > self._get_installed_best(pkg, pkg_info) > if ordered and pkg_info.merge and \ > not pkg_info.attr_display.new: > - for arg, atom in depgraph._iter_atoms_for_pkg(pkg): > - if arg.force_reinstall: > + forced_rebuilds =3D depgraph._dynamic_config._forced_rebuilds.get(p= kg.root, {}) > + for child in forced_rebuilds: > + if pkg in forced_rebuilds[child]: > pkg_info.attr_display.force_reinstall =3D True > break > =20 > --=20 > 1.8.3.2 >=20 >=20 >=20 --=20 Sergei --Sig_/RCOsp6p/RvUDOFpvHZUY7l9 Content-Type: application/pgp-signature; name=signature.asc Content-Disposition: attachment; filename=signature.asc -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.22 (GNU/Linux) iEYEARECAAYFAlM/n6gACgkQcaHudmEf86pH8ACfR6m7fcwHN4Dj44LHsy2zkW9W +3AAnRfwICITuKlrcb8Xe7RLzsf7xtCz =om8H -----END PGP SIGNATURE----- --Sig_/RCOsp6p/RvUDOFpvHZUY7l9--