Backtrack unsatisfied slot-operator, bug #456340.
authorZac Medico <zmedico@gentoo.org>
Tue, 12 Feb 2013 02:50:36 +0000 (18:50 -0800)
committerZac Medico <zmedico@gentoo.org>
Tue, 12 Feb 2013 02:50:36 +0000 (18:50 -0800)
pym/_emerge/depgraph.py
pym/portage/tests/resolver/test_slot_operator_unsolved.py

index 085ac9bb6681d66b7dd4dbc73eb9e2cac31576b3..eb88357e1558970563200474ba5583bf433a995d 100644 (file)
@@ -1273,6 +1273,100 @@ class depgraph(object):
 
                return None
 
+       def _slot_operator_unsatisfied_probe(self, dep):
+
+               if dep.parent.installed and \
+                       self._frozen_config.excluded_pkgs.findAtomForPackage(dep.parent,
+                       modified_use=self._pkg_use_enabled(dep.parent)):
+                       return False
+
+               debug = "--debug" in self._frozen_config.myopts
+
+               for replacement_parent in self._iter_similar_available(dep.parent,
+                       dep.parent.slot_atom):
+
+                       for atom in replacement_parent.validated_atoms:
+                               if not atom.slot_operator == "=" or \
+                                       atom.blocker or \
+                                       atom.cp != dep.atom.cp:
+                                       continue
+
+                               # Discard USE deps, we're only searching for an approximate
+                               # pattern, and dealing with USE states is too complex for
+                               # this purpose.
+                               atom = atom.without_use
+
+                               pkg, existing_node = self._select_package(dep.root, atom,
+                                       onlydeps=dep.onlydeps)
+
+                               if pkg is not None:
+
+                                       if debug:
+                                               msg = []
+                                               msg.append("")
+                                               msg.append("")
+                                               msg.append("slot_operator_unsatisfied_probe:")
+                                               msg.append("   existing parent package: %s" % dep.parent)
+                                               msg.append("   existing parent atom: %s" % dep.atom)
+                                               msg.append("   new parent package: %s" % replacement_parent)
+                                               msg.append("   new child package:  %s" % pkg)
+                                               msg.append("")
+                                               writemsg_level("\n".join(msg),
+                                                       noiselevel=-1, level=logging.DEBUG)
+
+                                       return True
+
+               if debug:
+                       msg = []
+                       msg.append("")
+                       msg.append("")
+                       msg.append("slot_operator_unsatisfied_probe:")
+                       msg.append("   existing parent package: %s" % dep.parent)
+                       msg.append("   existing parent atom: %s" % dep.atom)
+                       msg.append("   new parent package: %s" % None)
+                       msg.append("   new child package:  %s" % None)
+                       msg.append("")
+                       writemsg_level("\n".join(msg),
+                               noiselevel=-1, level=logging.DEBUG)
+
+               return False
+
+       def _slot_operator_unsatisfied_backtrack(self, dep):
+
+               parent = dep.parent
+
+               if "--debug" in self._frozen_config.myopts:
+                       msg = []
+                       msg.append("")
+                       msg.append("")
+                       msg.append("backtracking due to unsatisfied "
+                               "built slot-operator dep:")
+                       msg.append("   parent package: %s" % parent)
+                       msg.append("   atom: %s" % dep.atom)
+                       msg.append("")
+                       writemsg_level("\n".join(msg),
+                               noiselevel=-1, level=logging.DEBUG)
+
+               backtrack_infos = self._dynamic_config._backtrack_infos
+               config = backtrack_infos.setdefault("config", {})
+
+               # mask unwanted binary packages if necessary
+               masks = {}
+               if not parent.installed:
+                       masks.setdefault(parent, {})["slot_operator_mask_built"] = None
+               if masks:
+                       config.setdefault("slot_operator_mask_built", {}).update(masks)
+
+               # trigger replacement of installed packages if necessary
+               reinstalls = set()
+               if parent.installed:
+                       reinstalls.add((parent.root, parent.slot_atom))
+               if reinstalls:
+                       config.setdefault("slot_operator_replace_installed",
+                               set()).update(reinstalls)
+
+               self._dynamic_config._need_restart = True
+
        def _downgrade_probe(self, pkg):
                """
                Detect cases where a downgrade of the given package is considered
@@ -1529,6 +1623,10 @@ class depgraph(object):
                                                        (dep.parent,
                                                        self._dynamic_config._runtime_pkg_mask[
                                                        dep.parent]), noiselevel=-1)
+                               elif dep.atom.slot_operator_built and \
+                                       self._slot_operator_unsatisfied_probe(dep):
+                                       self._slot_operator_unsatisfied_backtrack(dep)
+                                       return 1
                                elif not self.need_restart():
                                        # Do not backtrack if only USE have to be changed in
                                        # order to satisfy the dependency.
index a4962523f52c412291596a83ff515383091c2fb4..7ae3d9e281f0ac4b23c16445dfa05e4e585640eb 100644 (file)
@@ -9,11 +9,6 @@ class SlotOperatorUnsolvedTestCase(TestCase):
        """
        Demonstrate bug #456340, where an unsolved circular dependency
        interacts with an unsatisfied built slot-operator dep.
-
-       The problem here results from poor handling of the unsatisfied built
-       slot operator dep inside _add_dep, where it aborts the graph and tries
-       to backtrack immediately. We really want it to queue a rebuild here,
-       and continue filling out the graph.
        """
        def __init__(self, *args, **kwargs):
                super(SlotOperatorUnsolvedTestCase, self).__init__(*args, **kwargs)
@@ -31,12 +26,14 @@ class SlotOperatorUnsolvedTestCase(TestCase):
                        },
                        "dev-ruby/rdoc-3.12.1" : {
                                "EAPI": "5",
-                               "DEPEND": ">=dev-ruby/hoe-2.7.0",
+                               "IUSE": "test",
+                               "DEPEND": "test? ( >=dev-ruby/hoe-2.7.0 )",
                        },
                        "dev-ruby/hoe-2.13.0" : {
                                "EAPI": "5",
-                               "DEPEND": ">=dev-ruby/rdoc-3.10",
-                               "RDEPEND": ">=dev-ruby/rdoc-3.10",
+                               "IUSE": "test",
+                               "DEPEND": "test? ( >=dev-ruby/rdoc-3.10 )",
+                               "RDEPEND": "test? ( >=dev-ruby/rdoc-3.10 )",
                        },
                }
 
@@ -52,6 +49,10 @@ class SlotOperatorUnsolvedTestCase(TestCase):
                        },
                }
 
+               user_config = {
+                       "make.conf" : ("FEATURES=test",)
+               }
+
                world = ["net-libs/webkit-gtk", "dev-ruby/hoe"]
 
                test_cases = (
@@ -59,12 +60,18 @@ class SlotOperatorUnsolvedTestCase(TestCase):
                        ResolverPlaygroundTestCase(
                                ["@world"],
                                options = {"--update": True, "--deep": True},
-                               success = False),
+                               circular_dependency_solutions = {
+                                       'dev-ruby/hoe-2.13.0': frozenset([frozenset([('test', False)])]),
+                                       'dev-ruby/rdoc-3.12.1': frozenset([frozenset([('test', False)])])
+                               },
+                               success = False
+                       ),
 
                )
 
                playground = ResolverPlayground(ebuilds=ebuilds,
-                       installed=installed, world=world, debug=False)
+                       installed=installed, user_config=user_config,
+                       world=world, debug=False)
                try:
                        for test_case in test_cases:
                                playground.run_TestCase(test_case)