From 222dfa56e8fb311f4bea54012bdfd5d1a474d56c Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Fri, 26 Oct 2012 00:43:20 -0700 Subject: [PATCH] backtracking: discard extra rebuilds, bug #439688 --- pym/_emerge/depgraph.py | 26 ++++++++++++++++++- pym/_emerge/resolver/backtracking.py | 26 ++++++++++++++++--- .../resolver/test_slot_conflict_rebuild.py | 6 ++--- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/pym/_emerge/depgraph.py b/pym/_emerge/depgraph.py index e129a810d..fd5dfe806 100644 --- a/pym/_emerge/depgraph.py +++ b/pym/_emerge/depgraph.py @@ -415,6 +415,7 @@ class _dynamic_depgraph_config(object): self._needed_use_config_changes = backtrack_parameters.needed_use_config_changes self._runtime_pkg_mask = backtrack_parameters.runtime_pkg_mask self._slot_operator_replace_installed = backtrack_parameters.slot_operator_replace_installed + self._prune_rebuilds = backtrack_parameters.prune_rebuilds self._need_restart = False # For conditions that always require user intervention, such as # unsatisfied REQUIRED_USE (currently has no autounmask support). @@ -633,7 +634,7 @@ class depgraph(object): line = colorize("INFORM", line) writemsg(line + "\n", noiselevel=-1) - def _show_missed_update(self): + def _get_missed_updates(self): # In order to minimize noise, show only the highest # missed update from each SLOT. @@ -659,6 +660,12 @@ class depgraph(object): missed_updates[k] = (pkg, mask_type, parent_atoms) break + return missed_updates + + def _show_missed_update(self): + + missed_updates = self._get_missed_updates() + if not missed_updates: return @@ -2737,6 +2744,23 @@ class depgraph(object): self.need_restart(): return False, myfavorites + if not self._dynamic_config._prune_rebuilds and \ + self._dynamic_config._slot_operator_replace_installed and \ + self._get_missed_updates(): + # When there are missed updates, we might have triggered + # some unnecessary rebuilds (see bug #439688). So, prune + # all the rebuilds and backtrack with the problematic + # updates masked. The next backtrack run should pull in + # any rebuilds that are really needed, and this + # prune_rebuilds path should never be entered more than + # once in a series of backtracking nodes (in order to + # avoid a backtracking loop). + backtrack_infos = self._dynamic_config._backtrack_infos + config = backtrack_infos.setdefault("config", {}) + config["prune_rebuilds"] = True + self._dynamic_config._need_restart = True + return False, myfavorites + # Any failures except those due to autounmask *alone* should return # before this point, since the success_without_autounmask flag that's # set below is reserved for cases where there are *zero* other diff --git a/pym/_emerge/resolver/backtracking.py b/pym/_emerge/resolver/backtracking.py index 5456ea455..c29b9d42a 100644 --- a/pym/_emerge/resolver/backtracking.py +++ b/pym/_emerge/resolver/backtracking.py @@ -1,4 +1,4 @@ -# Copyright 2010-2011 Gentoo Foundation +# Copyright 2010-2012 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 import copy @@ -7,8 +7,8 @@ class BacktrackParameter(object): __slots__ = ( "needed_unstable_keywords", "runtime_pkg_mask", "needed_use_config_changes", "needed_license_changes", - "rebuild_list", "reinstall_list", "needed_p_mask_changes", - "slot_operator_replace_installed" + "prune_rebuilds", "rebuild_list", "reinstall_list", "needed_p_mask_changes", + "slot_operator_mask_built", "slot_operator_replace_installed" ) def __init__(self): @@ -20,6 +20,8 @@ class BacktrackParameter(object): self.rebuild_list = set() self.reinstall_list = set() self.slot_operator_replace_installed = set() + self.slot_operator_mask_built = set() + self.prune_rebuilds = False def __deepcopy__(self, memo=None): if memo is None: @@ -36,6 +38,8 @@ class BacktrackParameter(object): result.rebuild_list = copy.copy(self.rebuild_list) result.reinstall_list = copy.copy(self.reinstall_list) result.slot_operator_replace_installed = copy.copy(self.slot_operator_replace_installed) + result.slot_operator_mask_built = self.slot_operator_mask_built.copy() + result.prune_rebuilds = self.prune_rebuilds # runtime_pkg_mask contains nested dicts that must also be copied result.runtime_pkg_mask = {} @@ -52,7 +56,9 @@ class BacktrackParameter(object): self.needed_license_changes == other.needed_license_changes and \ self.rebuild_list == other.rebuild_list and \ self.reinstall_list == other.reinstall_list and \ - self.slot_operator_replace_installed == other.slot_operator_replace_installed + self.slot_operator_replace_installed == other.slot_operator_replace_installed and \ + self.slot_operator_mask_built == other.slot_operator_mask_built and \ + self.prune_rebuilds == other.prune_rebuilds class _BacktrackNode(object): @@ -193,6 +199,7 @@ class Backtracker(object): elif change == "slot_conflict_abi": new_node.terminal = False elif change == "slot_operator_mask_built": + para.slot_operator_mask_built.update(data) for pkg, mask_reasons in data.items(): para.runtime_pkg_mask.setdefault(pkg, {}).update(mask_reasons) @@ -202,6 +209,17 @@ class Backtracker(object): para.rebuild_list.update(data) elif change == "reinstall_list": para.reinstall_list.update(data) + elif change == "prune_rebuilds": + para.prune_rebuilds = True + para.slot_operator_replace_installed.clear() + for pkg in para.slot_operator_mask_built: + runtime_masks = para.runtime_pkg_mask.get(pkg) + if runtime_masks is None: + continue + runtime_masks.pop("slot_operator_mask_built", None) + if not runtime_masks: + para.runtime_pkg_mask.pop(pkg) + para.slot_operator_mask_built.clear() self._add(new_node, explore=explore) self._current_node = new_node diff --git a/pym/portage/tests/resolver/test_slot_conflict_rebuild.py b/pym/portage/tests/resolver/test_slot_conflict_rebuild.py index e255e2a53..5ce2329e1 100644 --- a/pym/portage/tests/resolver/test_slot_conflict_rebuild.py +++ b/pym/portage/tests/resolver/test_slot_conflict_rebuild.py @@ -60,13 +60,13 @@ class SlotConflictRebuildTestCase(TestCase): test_cases = ( - # Demonstrate bug #439688, where a slot conflict prevents - # an upgrade and triggers unnecessary rebuilds. + # Test bug #439688, where a slot conflict prevents an + # upgrade and we don't want to trigger unnecessary rebuilds. ResolverPlaygroundTestCase( ["@world"], options = {"--update": True, "--deep": True}, success = True, - mergelist = ["app-misc/A-1", "app-misc/B-0"]), + mergelist = []), ) -- 2.26.2