Trigger rebuild for sub-slot change, bug #456208.
authorZac Medico <zmedico@gentoo.org>
Mon, 11 Feb 2013 01:58:16 +0000 (17:58 -0800)
committerZac Medico <zmedico@gentoo.org>
Mon, 11 Feb 2013 01:58:16 +0000 (17:58 -0800)
If sub-slot changes without a revbump, trigger a rebuild so that
dependent packages will have the new sub-slot recorded in their
slot-operator deps. Hopefully this will fix bug #456208.

pym/_emerge/depgraph.py
pym/portage/tests/resolver/test_slot_change_without_revbump.py [new file with mode: 0644]

index 27104e17ee86115d32819ac582b527f5aae72329..34a4604c807ae1a1b835b8b13314a99093f397f4 100644 (file)
@@ -1034,6 +1034,71 @@ class depgraph(object):
 
                return found_update
 
+       def _slot_change_probe(self, dep):
+               """
+               @rtype: bool
+               @return: True if dep.child should be rebuilt due to a change
+                       in sub-slot (without revbump, as in bug #456208).
+               """
+               if not (isinstance(dep.parent, Package) and \
+                       not dep.parent.built and dep.child.built):
+                       return None
+
+               root_config = self._frozen_config.roots[dep.root]
+               try:
+                       unbuilt_child  = self._pkg(dep.child.cpv, "ebuild",
+                               root_config, myrepo=dep.child.repo)
+               except PackageNotFound:
+                       for unbuilt_child in self._iter_match_pkgs(root_config,
+                               "ebuild", Atom("=%s" % (dep.child.cpv,))):
+                               break
+                       else:
+                               return None
+
+               if unbuilt_child.slot == dep.child.slot and \
+                       unbuilt_child.sub_slot == dep.child.sub_slot:
+                       return None
+
+               return unbuilt_child
+
+       def _slot_change_backtrack(self, dep, new_child_slot):
+               child = dep.child
+               if "--debug" in self._frozen_config.myopts:
+                       msg = []
+                       msg.append("")
+                       msg.append("")
+                       msg.append("backtracking due to slot/sub-slot change:")
+                       msg.append("   child package:  %s" % child)
+                       msg.append("      child slot:  %s/%s" %
+                               (child.slot, child.sub_slot))
+                       msg.append("       new child:  %s" % new_child_slot)
+                       msg.append("  new child slot:  %s/%s" %
+                               (new_child_slot.slot, new_child_slot.sub_slot))
+                       msg.append("   parent package: %s" % dep.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 child.installed:
+                       masks.setdefault(dep.child, {})["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 child.installed:
+                       reinstalls.add((child.root, child.slot_atom))
+               if reinstalls:
+                       config.setdefault("slot_operator_replace_installed",
+                               set()).update(reinstalls)
+
+               self._dynamic_config._need_restart = True
+
        def _slot_operator_update_backtrack(self, dep, new_child_slot=None):
                if new_child_slot is None:
                        child = dep.child
@@ -1241,6 +1306,17 @@ class depgraph(object):
                for slot_key, slot_info in self._dynamic_config._slot_operator_deps.items():
 
                        for dep in slot_info:
+
+                               atom = dep.atom
+                               if atom.slot_operator is None:
+                                       continue
+
+                               if not atom.slot_operator_built:
+                                       new_child_slot = self._slot_change_probe(dep)
+                                       if new_child_slot is not None:
+                                               self._slot_change_backtrack(dep, new_child_slot)
+                                       continue
+
                                if not (dep.parent and
                                        isinstance(dep.parent, Package) and dep.parent.built):
                                        continue
@@ -1669,7 +1745,7 @@ class depgraph(object):
 
                dep.child = pkg
                if (not pkg.onlydeps and
-                       dep.atom and dep.atom.slot_operator_built):
+                       dep.atom and dep.atom.slot_operator is not None):
                        self._add_slot_operator_dep(dep)
 
                recurse = deep is True or depth + 1 <= deep
diff --git a/pym/portage/tests/resolver/test_slot_change_without_revbump.py b/pym/portage/tests/resolver/test_slot_change_without_revbump.py
new file mode 100644 (file)
index 0000000..dbd78dc
--- /dev/null
@@ -0,0 +1,62 @@
+# Copyright 2013 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.tests import TestCase
+from portage.tests.resolver.ResolverPlayground import (ResolverPlayground,
+       ResolverPlaygroundTestCase)
+
+class SlotChangeWithoutRevBumpTestCase(TestCase):
+
+       def testSlotChangeWithoutRevBump(self):
+
+               ebuilds = {
+                       "app-arch/libarchive-3.1.1" : {
+                               "EAPI": "5",
+                               "SLOT": "0/13"
+                       },
+                       "app-arch/libarchive-3.0.4-r1" : {
+                               "EAPI": "5",
+                               "SLOT": "0"
+                       },
+                       "kde-base/ark-4.10.0" : {
+                               "EAPI": "5",
+                               "DEPEND": "app-arch/libarchive:=",
+                               "RDEPEND": "app-arch/libarchive:="
+                       },
+               }
+
+               installed = {
+                       "app-arch/libarchive-3.1.1" : {
+                               "EAPI": "5",
+                               "SLOT": "0"
+                       },
+
+                       "kde-base/ark-4.10.0" : {
+                               "EAPI": "5",
+                               "DEPEND": "app-arch/libarchive:0/0=",
+                               "RDEPEND": "app-arch/libarchive:0/0="
+                       },
+               }
+
+               world = ["kde-base/ark"]
+
+               test_cases = (
+
+                       # Demonstrate bug #456208, where a sub-slot change
+                       # without revbump needs to trigger a rebuild.
+                       ResolverPlaygroundTestCase(
+                               ["kde-base/ark"],
+                               options = {"--oneshot": True},
+                               success = True,
+                               mergelist = ['app-arch/libarchive-3.1.1', "kde-base/ark-4.10.0"]),
+
+               )
+
+               playground = ResolverPlayground(ebuilds=ebuilds,
+                       installed=installed, world=world, debug=False)
+               try:
+                       for test_case in test_cases:
+                               playground.run_TestCase(test_case)
+                               self.assertEqual(test_case.test_success, True, test_case.fail_msg)
+               finally:
+                       playground.cleanup()