depgraph: pull in new virtual slots with --update
authorZac Medico <zmedico@gentoo.org>
Sun, 11 Sep 2011 20:43:10 +0000 (13:43 -0700)
committerZac Medico <zmedico@gentoo.org>
Sun, 11 Sep 2011 20:43:10 +0000 (13:43 -0700)
This re-implements the fix from commit
21330075f07248765016e104b3ba8216903f1ecb, without introducing the
unwanted behavior reported in bug 382557. This involves checking the
direct dependencies of virtual slot updates to make sure they are all
visible, before pulling them in.

pym/_emerge/depgraph.py
pym/portage/tests/resolver/test_virtual_slot.py

index 6a04a7971487a3119d7290e3b85f9517bf0ae766..1e311e8f70f1f7ba568b9bccd53139b2f1e58ecc 100644 (file)
@@ -2500,24 +2500,36 @@ class depgraph(object):
                        # account for masking and USE settings.
                        _autounmask_backup = self._dynamic_config._autounmask
                        self._dynamic_config._autounmask = False
-                       mytrees["pkg_use_enabled"] = self._pkg_use_enabled
+                       # backup state for restoration, in case of recursive
+                       # calls to this method
+                       backup_state = mytrees.copy()
                        try:
+                               # clear state from previous call, in case this
+                               # call is recursive (we have a backup, that we
+                               # will use to restore it later)
+                               mytrees.pop("pkg_use_enabled", None)
+                               mytrees.pop("parent", None)
+                               mytrees.pop("atom_graph", None)
+                               mytrees.pop("priority", None)
+
+                               mytrees["pkg_use_enabled"] = self._pkg_use_enabled
                                if parent is not None:
-                                       trees[root]["parent"] = parent
-                                       trees[root]["atom_graph"] = atom_graph
+                                       mytrees["parent"] = parent
+                                       mytrees["atom_graph"] = atom_graph
                                if priority is not None:
-                                       trees[root]["priority"] = priority
+                                       mytrees["priority"] = priority
+
                                mycheck = portage.dep_check(depstring, None,
                                        pkgsettings, myuse=myuse,
                                        myroot=root, trees=trees)
                        finally:
+                               # restore state
                                self._dynamic_config._autounmask = _autounmask_backup
-                               del mytrees["pkg_use_enabled"]
-                               if parent is not None:
-                                       trees[root].pop("parent")
-                                       trees[root].pop("atom_graph")
-                               if priority is not None:
-                                       trees[root].pop("priority")
+                               mytrees.pop("pkg_use_enabled", None)
+                               mytrees.pop("parent", None)
+                               mytrees.pop("atom_graph", None)
+                               mytrees.pop("priority", None)
+                               mytrees.update(backup_state)
                        if not mycheck[0]:
                                raise portage.exception.InvalidDependString(mycheck[1])
                if parent is None:
@@ -2611,6 +2623,38 @@ class depgraph(object):
                                        continue
                                yield atom
 
+       def _virt_deps_visible(self, pkg, ignore_use=False):
+               """
+               Assumes pkg is a virtual package. Traverses virtual deps recursively
+               and returns True if all deps are visible, False otherwise. This is
+               useful for checking if it will be necessary to expand virtual slots,
+               for cases like bug #382557.
+               """
+               try:
+                       rdepend = self._select_atoms(
+                               pkg.root, pkg.metadata.get("RDEPEND", ""),
+                               myuse=self._pkg_use_enabled(pkg),
+                               parent=pkg, priority=self._priority(runtime=True))
+               except InvalidDependString as e:
+                       if not pkg.installed:
+                               raise
+                       writemsg_level("!!! Invalid RDEPEND in " + \
+                               "'%svar/db/pkg/%s/RDEPEND': %s\n" % \
+                               (pkg.root, pkg.cpv, e),
+                               noiselevel=-1, level=logging.ERROR)
+                       return False
+
+               for atoms in rdepend.values():
+                       for atom in atoms:
+                               if ignore_use:
+                                       atom = atom.without_use
+                               pkg, existing = self._select_package(
+                                       pkg.root, atom)
+                               if pkg is None or not self._pkg_visibility_check(pkg):
+                                       return False
+
+               return True
+
        def _get_dep_chain(self, start_node, target_atom=None,
                unsatisfied_dependency=False):
                """
@@ -6540,7 +6584,10 @@ class _dep_check_composite_db(dbapi):
 
                if pkg is not None and \
                        atom.slot is None and \
-                       pkg.cp.startswith("virtual/"):
+                       pkg.cp.startswith("virtual/") and \
+                       ("--update" not in self._depgraph._frozen_config.myopts or
+                       not ret or
+                       not self._depgraph._virt_deps_visible(pkg, ignore_use=True)):
                        # For new-style virtual lookahead that occurs inside dep_check()
                        # for bug #141118, examine all slots. This is needed so that newer
                        # slots will not unnecessarily be pulled in when a satisfying lower
index 40ed73b113fee197a9ca7245d7ae966e172e30f7..fb242011df8863641ae774f2542be98f55d0d81f 100644 (file)
@@ -44,3 +44,50 @@ class VirtualSlotResolverTestCase(TestCase):
                                self.assertEqual(test_case.test_success, True, test_case.fail_msg)
                finally:
                        playground.cleanup()
+
+       def testVirtualSlotUpdate(self):
+
+               ebuilds = {
+                       "dev-java/oracle-jdk-bin-1.7.0" : {"SLOT": "1.7", "LICENSE": "TEST"},
+                       "dev-java/sun-jdk-1.6.0" : {"SLOT": "1.6", "LICENSE": "TEST"},
+                       "dev-java/icedtea-6.1.10.3" : {"SLOT": "6"},
+                       "dev-java/icedtea-7" : {"SLOT": "7"},
+                       "app-misc/java-app-1": {"RDEPEND": ">=virtual/jdk-1.6.0"},
+                       "virtual/jdk-1.6.0": {"SLOT": "1.6", "RDEPEND": "|| ( =dev-java/icedtea-6* =dev-java/sun-jdk-1.6.0* )"},
+                       "virtual/jdk-1.7.0": {"SLOT": "1.7", "RDEPEND": "|| ( =dev-java/icedtea-7* =dev-java/oracle-jdk-bin-1.7.0* )"},
+               }
+
+               installed = {
+                       "app-misc/java-app-1": {"RDEPEND": ">=virtual/jdk-1.6.0"},
+                       "dev-java/icedtea-6.1.10.3" : {"SLOT": "6"},
+                       "virtual/jdk-1.6.0": {"SLOT" : "1.6", "RDEPEND": "|| ( =dev-java/icedtea-6* =dev-java/sun-jdk-1.6.0* )"},
+               }
+
+               world = ("app-misc/java-app",)
+
+               test_cases = (
+                       # Pull in the virtual/jdk-1.7.0 slot update since its dependencies
+                       # can only be satisfied by an unmasked package.
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--update" : True, "--deep" : True},
+                               success = True,
+                               mergelist = ["dev-java/icedtea-7", "virtual/jdk-1.7.0"]),
+
+                       # Bug #275945 - Don't pull in the virtual/jdk-1.7.0 slot update
+                       # unless --update is enabled.
+                       ResolverPlaygroundTestCase(
+                               ["@world"],
+                               options = {"--selective" : True, "--deep" : True},
+                               success = True,
+                               mergelist = []),
+               )
+
+               playground = ResolverPlayground(
+                       ebuilds=ebuilds, installed=installed, world=world)
+               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()