circular dependency handler: Add support for REQUIRED_USE
authorSebastian Luther <SebastianLuther@gmx.de>
Fri, 20 Aug 2010 07:24:46 +0000 (09:24 +0200)
committerZac Medico <zmedico@gentoo.org>
Fri, 20 Aug 2010 12:15:54 +0000 (05:15 -0700)
pym/_emerge/resolver/circular_dependency.py
pym/portage/tests/resolver/test_circular_dependencies.py

index 5029a7050a70c30391adb6cd99142cb5c13eff75..dbb1adf497fc1421d0543664844c5aea586b67a5 100644 (file)
@@ -3,8 +3,10 @@
 
 from __future__ import print_function
 
+from itertools import chain
+
 from portage.util import writemsg
-from portage.dep import use_reduce, extract_affecting_use
+from portage.dep import use_reduce, extract_affecting_use, check_required_use, get_required_use_flags
 from portage.output import colorize
 from _emerge.DepPrioritySatisfiedRange import DepPrioritySatisfiedRange
 
@@ -118,9 +120,21 @@ class circular_dependency_handler(object):
                        # Make sure we don't want to change a flag that is 
                        #       a) in use.mask or use.force
                        #       b) changed by autounmask
+                       
                        usemask, useforce = self._get_use_mask_and_force(parent)
                        autounmask_changes = self._get_autounmask_changes(parent)
-                       affecting_use.difference_update(usemask, useforce, autounmask_changes)
+                       untouchable_flags = frozenset(chain(usemask, useforce, autounmask_changes))
+
+                       affecting_use.difference_update(untouchable_flags)
+
+                       #If any of the flags we're going to touch is in REQUIRED_USE, add all
+                       #other flags in REQUIRED_USE to affecting_use, to not lose any solution.
+                       required_use_flags = get_required_use_flags(parent.metadata["REQUIRED_USE"])
+
+                       if affecting_use.intersection(required_use_flags):
+                               affecting_use.update(required_use_flags)
+                               affecting_use.difference_update(untouchable_flags)
+
                        affecting_use = tuple(affecting_use)
 
                        if not affecting_use:
@@ -160,17 +174,21 @@ class circular_dependency_handler(object):
                                        uselist=current_use, flat=True)
 
                                if parent_atom not in reduced_dep:
-                                       #we found a valid solution
-                                       solution = set()
-                                       use = self.depgraph._pkg_use_enabled(parent)
-                                       for flag, state in zip(affecting_use, use_state):
-                                               if state == "enabled" and \
-                                                       flag not in use:
-                                                       solution.add((flag, True))
-                                               elif state == "disabled" and \
-                                                       flag in use:
-                                                       solution.add((flag, False))
-                                       solutions.add(frozenset(solution))
+                                       #We found an assignment that removes the atom from 'dep'.
+                                       #Make sure it doesn't conflict with REQUIRED_USE.
+                                       required_use = parent.metadata["REQUIRED_USE"]
+
+                                       if check_required_use(required_use, current_use, parent.iuse.is_valid_flag):
+                                               use = self.depgraph._pkg_use_enabled(parent)
+                                               solution = set()
+                                               for flag, state in zip(affecting_use, use_state):
+                                                       if state == "enabled" and \
+                                                               flag not in use:
+                                                               solution.add((flag, True))
+                                                       elif state == "disabled" and \
+                                                               flag in use:
+                                                               solution.add((flag, False))
+                                               solutions.add(frozenset(solution))
 
                                if not _next_use_state(use_state):
                                        break
index 73cb95a638b64b2b03c249afa0e8769df36a39d7..e702986421c492d43d852942840d49365218e7fa 100644 (file)
@@ -24,6 +24,11 @@ class CircularDependencyTestCase(TestCase):
                        "dev-libs/W-1": { "DEPEND": "dev-libs/Z[foo] dev-libs/Y", "EAPI": 2 },
                        "dev-libs/W-2": { "DEPEND": "dev-libs/Z[foo=] dev-libs/Y", "IUSE": "+foo", "EAPI": 2 },
                        "dev-libs/W-3": { "DEPEND": "dev-libs/Z[bar] dev-libs/Y", "EAPI": 2 },
+
+                       #~ "app-misc/A-1": { "DEPEND": "foo? ( =app-misc/B-1 )", "IUSE": "+foo bar", "REQUIRED_USE": "^^ ( foo bar )", "EAPI": 4 },
+                       #~ "app-misc/A-2": { "DEPEND": "foo? ( =app-misc/B-2 ) bar? ( =app-misc/B-2 )", "IUSE": "+foo bar", "REQUIRED_USE": "^^ ( foo bar )", "EAPI": 4 },
+                       #~ "app-misc/B-1": { "DEPEND": "=app-misc/A-1" },
+                       #~ "app-misc/B-2": { "DEPEND": "=app-misc/A-2" },
                        }
 
                test_cases = (
@@ -58,6 +63,16 @@ class CircularDependencyTestCase(TestCase):
                                circular_dependency_solutions = { "dev-libs/Y-1": frozenset([frozenset([("foo", False)])])},
                                use_changes = { "dev-libs/Z-3": {"bar": True}},
                                success = False),
+
+                       #Conflict with REQUIRED_USE
+                       #~ ResolverPlaygroundTestCase(
+                               #~ ["=app-misc/B-1"],
+                               #~ circular_dependency_solutions = { "app-misc/B-1": frozenset([frozenset([("foo", False), ("bar", True)])])},
+                               #~ success = False),
+                       #~ ResolverPlaygroundTestCase(
+                               #~ ["=app-misc/B-2"],
+                               #~ circular_dependency_solutions = {},
+                               #~ success = False),
                )
 
                playground = ResolverPlayground(ebuilds=ebuilds)