Rebuild for slot-abi downgrades, bug #424651.
authorZac Medico <zmedico@gentoo.org>
Thu, 5 Jul 2012 03:16:40 +0000 (20:16 -0700)
committerZac Medico <zmedico@gentoo.org>
Thu, 5 Jul 2012 03:16:40 +0000 (20:16 -0700)
pym/_emerge/depgraph.py
pym/portage/tests/resolver/test_slot_abi_downgrade.py [new file with mode: 0644]

index 480cb902af2c4a34c9a5f8d1c90ba7b2b5d99b7e..f819aef3d17607e7b50c421bc3ef9dd639ecd515 100644 (file)
@@ -24,7 +24,8 @@ from portage.dep import Atom, best_match_to_list, extract_affecting_use, \
        _repo_separator
 from portage.dep._slot_abi import ignore_built_slot_abi_deps
 from portage.eapi import eapi_has_strong_blocks, eapi_has_required_use
-from portage.exception import InvalidAtom, InvalidDependString, PortageException
+from portage.exception import (InvalidAtom, InvalidDependString,
+       PackageNotFound, PortageException)
 from portage.output import colorize, create_color_func, \
        darkgreen, green
 bad = create_color_func("BAD")
@@ -1047,6 +1048,7 @@ class depgraph(object):
                        return None
 
                debug = "--debug" in self._frozen_config.myopts
+               want_downgrade = None
 
                for replacement_parent in self._iter_similar_available(dep.parent,
                        dep.parent.slot_atom):
@@ -1087,10 +1089,13 @@ class depgraph(object):
                                                if pkg.slot != dep.child.slot:
                                                        continue
                                                if pkg < dep.child:
+                                                       if want_downgrade is None:
+                                                               want_downgrade = self._downgrade_probe(dep.child)
                                                        # be careful not to trigger a rebuild when
                                                        # the only version available with a
                                                        # different slot_abi is an older version
-                                                       continue
+                                                       if not want_downgrade:
+                                                               continue
 
                                        if debug:
                                                msg = []
@@ -1122,12 +1127,33 @@ class depgraph(object):
 
                return None
 
+       def _downgrade_probe(self, pkg):
+               """
+               Detect cases where a downgrade of the given package is considered
+               desirable due to the current version being masked or unavailable.
+               """
+               available_pkg = None
+               for available_pkg in self._iter_similar_available(pkg,
+                       pkg.slot_atom):
+                       if available_pkg >= pkg:
+                               # There's an available package of the same or higher
+                               # version, so downgrade seems undesirable.
+                               return False
+
+               return available_pkg is not None
+
        def _iter_similar_available(self, graph_pkg, atom):
                """
                Given a package that's in the graph, do a rough check to
                see if a similar package is available to install. The given
                graph_pkg itself may be yielded only if it's not installed.
                """
+
+               usepkgonly = "--usepkgonly" in self._frozen_config.myopts
+               useoldpkg_atoms = self._frozen_config.useoldpkg_atoms
+               use_ebuild_visibility = self._frozen_config.myopts.get(
+                       '--use-ebuild-visibility', 'n') != 'n'
+
                for pkg in self._iter_match_pkgs_any(
                        graph_pkg.root_config, atom):
                        if pkg.cp != graph_pkg.cp:
@@ -1142,6 +1168,14 @@ class depgraph(object):
                                continue
                        if not self._pkg_visibility_check(pkg):
                                continue
+                       if pkg.built:
+                               if self._equiv_binary_installed(pkg):
+                                       continue
+                               if not (not use_ebuild_visibility and
+                                       (usepkgonly or useoldpkg_atoms.findAtomForPackage(
+                                       pkg, modified_use=self._pkg_use_enabled(pkg)))) and \
+                                       not self._equiv_ebuild_visible(pkg):
+                                       continue
                        yield pkg
 
        def _slot_abi_trigger_reinstalls(self):
@@ -3811,6 +3845,38 @@ class depgraph(object):
 
                return not arg
 
+       def _equiv_ebuild_visible(self, pkg, autounmask_level=None):
+               try:
+                       pkg_eb = self._pkg(
+                               pkg.cpv, "ebuild", pkg.root_config, myrepo=pkg.repo)
+               except portage.exception.PackageNotFound:
+                       pkg_eb_visible = False
+                       for pkg_eb in self._iter_match_pkgs(pkg.root_config,
+                               "ebuild", Atom("=%s" % (pkg.cpv,))):
+                               if self._pkg_visibility_check(pkg_eb, autounmask_level):
+                                       pkg_eb_visible = True
+                                       break
+                       if not pkg_eb_visible:
+                               return False
+               else:
+                       if not self._pkg_visibility_check(pkg_eb, autounmask_level):
+                               return False
+
+               return True
+
+       def _equiv_binary_installed(self, pkg):
+               build_time = pkg.metadata.get('BUILD_TIME')
+               if not build_time:
+                       return False
+
+               try:
+                       inst_pkg = self._pkg(pkg.cpv, "installed",
+                               pkg.root_config, installed=True)
+               except PackageNotFound:
+                       return False
+
+               return build_time == inst_pkg.metadata.get('BUILD_TIME')
+
        class _AutounmaskLevel(object):
                __slots__ = ("allow_use_changes", "allow_unstable_keywords", "allow_license_changes", \
                        "allow_missing_keywords", "allow_unmasks")
@@ -4241,22 +4307,9 @@ class depgraph(object):
                                                                if not use_ebuild_visibility and (usepkgonly or useoldpkg):
                                                                        if pkg.installed and pkg.masks:
                                                                                continue
-                                                               else:
-                                                                       try:
-                                                                               pkg_eb = self._pkg(
-                                                                                       pkg.cpv, "ebuild", root_config, myrepo=pkg.repo)
-                                                                       except portage.exception.PackageNotFound:
-                                                                               pkg_eb_visible = False
-                                                                               for pkg_eb in self._iter_match_pkgs(pkg.root_config,
-                                                                                       "ebuild", Atom("=%s" % (pkg.cpv,))):
-                                                                                       if self._pkg_visibility_check(pkg_eb, autounmask_level):
-                                                                                               pkg_eb_visible = True
-                                                                                               break
-                                                                               if not pkg_eb_visible:
-                                                                                       continue
-                                                                       else:
-                                                                               if not self._pkg_visibility_check(pkg_eb, autounmask_level):
-                                                                                       continue
+                                                               elif not self._equiv_ebuild_visible(pkg,
+                                                                       autounmask_level=autounmask_level):
+                                                                       continue
 
                                        # Calculation of USE for unbuilt ebuilds is relatively
                                        # expensive, so it is only performed lazily, after the
@@ -7137,24 +7190,8 @@ class _dep_check_composite_db(dbapi):
                        if not avoid_update:
                                if not use_ebuild_visibility and usepkgonly:
                                        return False
-                               else:
-                                       try:
-                                               pkg_eb = self._depgraph._pkg(
-                                                       pkg.cpv, "ebuild", pkg.root_config,
-                                                       myrepo=pkg.repo)
-                                       except portage.exception.PackageNotFound:
-                                               pkg_eb_visible = False
-                                               for pkg_eb in self._depgraph._iter_match_pkgs(
-                                                       pkg.root_config, "ebuild",
-                                                       Atom("=%s" % (pkg.cpv,))):
-                                                       if self._depgraph._pkg_visibility_check(pkg_eb):
-                                                               pkg_eb_visible = True
-                                                               break
-                                               if not pkg_eb_visible:
-                                                       return False
-                                       else:
-                                               if not self._depgraph._pkg_visibility_check(pkg_eb):
-                                                       return False
+                               elif not self._depgraph._equiv_ebuild_visible(pkg):
+                                       return False
 
                in_graph = self._depgraph._dynamic_config._slot_pkg_map[
                        self._root].get(pkg.slot_atom)
diff --git a/pym/portage/tests/resolver/test_slot_abi_downgrade.py b/pym/portage/tests/resolver/test_slot_abi_downgrade.py
new file mode 100644 (file)
index 0000000..45a7555
--- /dev/null
@@ -0,0 +1,225 @@
+# Copyright 2012 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 SlotAbiDowngradeTestCase(TestCase):
+
+       def __init__(self, *args, **kwargs):
+               super(SlotAbiDowngradeTestCase, self).__init__(*args, **kwargs)
+
+       def testSubSlot(self):
+               ebuilds = {
+                       "dev-libs/icu-4.8" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "0/48"
+                       },
+                       "dev-libs/libxml2-2.7.8" : {
+                               "EAPI": "4-slot-abi",
+                               "DEPEND":  "dev-libs/icu:=",
+                               "RDEPEND": "dev-libs/icu:="
+                       },
+               }
+               binpkgs = {
+                       "dev-libs/icu-49" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "0/49"
+                       },
+                       "dev-libs/icu-4.8" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "0/48"
+                       },
+                       "dev-libs/libxml2-2.7.8" : {
+                               "EAPI": "4-slot-abi",
+                               "DEPEND":  "dev-libs/icu:0/49=",
+                               "RDEPEND": "dev-libs/icu:0/49="
+                       },
+               }
+               installed = {
+                       "dev-libs/icu-49" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "0/49"
+                       },
+                       "dev-libs/libxml2-2.7.8" : {
+                               "EAPI": "4-slot-abi",
+                               "DEPEND":  "dev-libs/icu:0/49=",
+                               "RDEPEND": "dev-libs/icu:0/49="
+                       },
+               }
+
+               world = ["dev-libs/libxml2"]
+
+               test_cases = (
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/icu"],
+                               options = {"--oneshot": True},
+                               success = True,
+                               mergelist = ["dev-libs/icu-4.8", "dev-libs/libxml2-2.7.8" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/icu"],
+                               options = {"--oneshot": True, "--ignore-built-slot-abi-deps": "y"},
+                               success = True,
+                               mergelist = ["dev-libs/icu-4.8"]),
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/icu"],
+                               options = {"--oneshot": True, "--usepkg": True},
+                               success = True,
+                               mergelist = ["[binary]dev-libs/icu-4.8", "dev-libs/libxml2-2.7.8" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/icu"],
+                               options = {"--oneshot": True, "--usepkgonly": True},
+                               success = True,
+                               mergelist = ["[binary]dev-libs/icu-49"]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True},
+                               success = True,
+                               mergelist = ["dev-libs/icu-4.8", "dev-libs/libxml2-2.7.8" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True, "--ignore-built-slot-abi-deps": "y"},
+                               success = True,
+                               mergelist = ["dev-libs/icu-4.8"]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True, "--usepkg": True},
+                               success = True,
+                               mergelist = ["[binary]dev-libs/icu-4.8", "dev-libs/libxml2-2.7.8" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True, "--usepkgonly": True},
+                               success = True,
+                               mergelist = []),
+
+               )
+
+               playground = ResolverPlayground(ebuilds=ebuilds, binpkgs=binpkgs,
+                       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()
+
+       def testWholeSlotSubSlotMix(self):
+               ebuilds = {
+                       "dev-libs/glib-1.2.10" : {
+                               "SLOT": "1"
+                       },
+                       "dev-libs/glib-2.30.2" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "2/2.30"
+                       },
+                       "dev-libs/dbus-glib-0.98" : {
+                               "EAPI": "4-slot-abi",
+                               "DEPEND":  "dev-libs/glib:2=",
+                               "RDEPEND": "dev-libs/glib:2="
+                       },
+               }
+               binpkgs = {
+                       "dev-libs/glib-1.2.10" : {
+                               "SLOT": "1"
+                       },
+                       "dev-libs/glib-2.30.2" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "2/2.30"
+                       },
+                       "dev-libs/glib-2.32.3" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "2/2.32"
+                       },
+                       "dev-libs/dbus-glib-0.98" : {
+                               "EAPI": "4-slot-abi",
+                               "DEPEND":  "dev-libs/glib:2/2.32=",
+                               "RDEPEND": "dev-libs/glib:2/2.32="
+                       },
+               }
+               installed = {
+                       "dev-libs/glib-1.2.10" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "1"
+                       },
+                       "dev-libs/glib-2.32.3" : {
+                               "EAPI": "4-slot-abi",
+                               "SLOT": "2/2.32"
+                       },
+                       "dev-libs/dbus-glib-0.98" : {
+                               "EAPI": "4-slot-abi",
+                               "DEPEND":  "dev-libs/glib:2/2.32=",
+                               "RDEPEND": "dev-libs/glib:2/2.32="
+                       },
+               }
+
+               world = ["dev-libs/glib:1", "dev-libs/dbus-glib"]
+
+               test_cases = (
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/glib"],
+                               options = {"--oneshot": True},
+                               success = True,
+                               mergelist = ["dev-libs/glib-2.30.2", "dev-libs/dbus-glib-0.98" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/glib"],
+                               options = {"--oneshot": True, "--ignore-built-slot-abi-deps": "y"},
+                               success = True,
+                               mergelist = ["dev-libs/glib-2.30.2"]),
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/glib"],
+                               options = {"--oneshot": True, "--usepkg": True},
+                               success = True,
+                               mergelist = ["[binary]dev-libs/glib-2.30.2", "dev-libs/dbus-glib-0.98" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["dev-libs/glib"],
+                               options = {"--oneshot": True, "--usepkgonly": True},
+                               success = True,
+                               mergelist = ["[binary]dev-libs/glib-2.32.3"]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True},
+                               success = True,
+                               mergelist = ["dev-libs/glib-2.30.2", "dev-libs/dbus-glib-0.98" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True, "--ignore-built-slot-abi-deps": "y"},
+                               success = True,
+                               mergelist = ["dev-libs/glib-2.30.2"]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True, "--usepkg": True},
+                               success = True,
+                               mergelist = ["[binary]dev-libs/glib-2.30.2", "dev-libs/dbus-glib-0.98" ]),
+
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update": True, "--deep": True, "--usepkgonly": True},
+                               success = True,
+                               mergelist = []),
+
+               )
+
+               playground = ResolverPlayground(ebuilds=ebuilds, binpkgs=binpkgs,
+                       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()