backtracking: discard extra rebuilds, bug #439688
authorZac Medico <zmedico@gentoo.org>
Fri, 26 Oct 2012 07:43:20 +0000 (00:43 -0700)
committerZac Medico <zmedico@gentoo.org>
Fri, 26 Oct 2012 07:43:20 +0000 (00:43 -0700)
pym/_emerge/depgraph.py
pym/_emerge/resolver/backtracking.py
pym/portage/tests/resolver/test_slot_conflict_rebuild.py

index e129a810d94f8b08fc958fffe7a5780285a3468c..fd5dfe80655ffea3e2b59905e391c59af7f4115a 100644 (file)
@@ -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
index 5456ea4559c839df9cb453226df6e8099f0c125d..c29b9d42a3dc05d25d2e1e81a584d80d38354342 100644 (file)
@@ -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
index e255e2a53b23a6303bf9ec9edf2295bab130e58f..5ce2329e1235300ebe1f7c5bf52ea2700ed0a3dd 100644 (file)
@@ -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 = []),
 
                )