EAPI 5: REQUIRED_USE at-most-one-of ?? operator
authorZac Medico <zmedico@gentoo.org>
Mon, 27 Aug 2012 22:13:29 +0000 (15:13 -0700)
committerZac Medico <zmedico@gentoo.org>
Mon, 27 Aug 2012 22:13:29 +0000 (15:13 -0700)
See bug #354219 and the PMS patch:
http://git.overlays.gentoo.org/gitweb/?p=proj/pms.git;a=commit;h=1c2dff2df2305aff88a734e3a2716de1bb69f3b6

bin/repoman
pym/_emerge/Package.py
pym/portage/dep/__init__.py
pym/portage/eapi.py
pym/portage/tests/dep/testCheckRequiredUse.py
pym/portage/tests/dep/test_get_required_use_flags.py

index b50fac82c21421e0d84d140df2e47da2f5f4084d..dd065c8d6bf4615dfc52ff182401f0e03cea13f7 100755 (executable)
@@ -2028,7 +2028,7 @@ for x in effective_scanlist:
                                        " not supported with EAPI='%s'" % (eapi,))
                        try:
                                portage.dep.check_required_use(required_use, (),
-                                       pkg.iuse.is_valid_flag)
+                                       pkg.iuse.is_valid_flag, eapi=eapi)
                        except portage.exception.InvalidDependString as e:
                                stats["REQUIRED_USE.syntax"] = stats["REQUIRED_USE.syntax"] + 1
                                fails["REQUIRED_USE.syntax"].append(
index 85fc597736b23eaf4dce210ef35bbb909b3397d9..2087cbfe0f1fd507cc9e5ecae691bdcfacd31361 100644 (file)
@@ -228,7 +228,7 @@ class Package(Task):
                        else:
                                try:
                                        check_required_use(v, (),
-                                               self.iuse.is_valid_flag)
+                                               self.iuse.is_valid_flag, eapi=eapi)
                                except InvalidDependString as e:
                                        # Force unicode format string for python-2.x safety,
                                        # ensuring that PortageException.__unicode__() is used
index e547debd41c42ec5c704945f4ac8241bebd34ebb..b7bb46f751541edd0dfdb3755d5f83bb5a1983cd 100644 (file)
@@ -2314,9 +2314,9 @@ def match_from_list(mydep, candidate_list):
        return mylist
 
 def human_readable_required_use(required_use):
-       return required_use.replace("^^", "exactly-one-of").replace("||", "any-of")
+       return required_use.replace("^^", "exactly-one-of").replace("||", "any-of").replace("??", "at-most-one-of")
 
-def get_required_use_flags(required_use):
+def get_required_use_flags(required_use, eapi=None):
        """
        Returns a set of use flags that are used in the given REQUIRED_USE string
 
@@ -2326,6 +2326,12 @@ def get_required_use_flags(required_use):
        @return: Set of use flags that are used in the given REQUIRED_USE string
        """
 
+       eapi_attrs = _get_eapi_attrs(eapi)
+       if eapi_attrs.required_use_at_most_one_of:
+               valid_operators = ("||", "^^", "??")
+       else:
+               valid_operators = ("||", "^^")
+
        mysplit = required_use.split()
        level = 0
        stack = [[]]
@@ -2354,7 +2360,7 @@ def get_required_use_flags(required_use):
                                l = stack.pop()
                                ignore = False
                                if stack[level]:
-                                       if stack[level][-1] in ("||", "^^") or \
+                                       if stack[level][-1] in valid_operators or \
                                                (not isinstance(stack[level][-1], bool) and \
                                                stack[level][-1][-1] == "?"):
                                                ignore = True
@@ -2366,15 +2372,14 @@ def get_required_use_flags(required_use):
                        else:
                                raise InvalidDependString(
                                        _("malformed syntax: '%s'") % required_use)
-               elif token in ("||", "^^"):
+               elif token in valid_operators:
                        if need_bracket:
                                raise InvalidDependString(
                                        _("malformed syntax: '%s'") % required_use)
                        need_bracket = True
                        stack[level].append(token)
                else:
-                       if need_bracket or "(" in token or ")" in token or \
-                               "|" in token or "^" in token:
+                       if need_bracket:
                                raise InvalidDependString(
                                        _("malformed syntax: '%s'") % required_use)
 
@@ -2429,7 +2434,7 @@ class _RequiredUseBranch(object):
                complex_nesting = False
                node = self
                while node != None and not complex_nesting:
-                       if node._operator in ("||", "^^"):
+                       if node._operator in ("||", "^^", "??"):
                                complex_nesting = True
                        else:
                                node = node._parent
@@ -2450,7 +2455,7 @@ class _RequiredUseBranch(object):
        if sys.hexversion < 0x3000000:
                __nonzero__ = __bool__
 
-def check_required_use(required_use, use, iuse_match):
+def check_required_use(required_use, use, iuse_match, eapi=None):
        """
        Checks if the use flags listed in 'use' satisfy all
        constraints specified in 'constraints'.
@@ -2466,6 +2471,12 @@ def check_required_use(required_use, use, iuse_match):
        @return: Indicates if REQUIRED_USE constraints are satisfied
        """
 
+       eapi_attrs = _get_eapi_attrs(eapi)
+       if eapi_attrs.required_use_at_most_one_of:
+               valid_operators = ("||", "^^", "??")
+       else:
+               valid_operators = ("||", "^^")
+
        def is_active(token):
                if token.startswith("!"):
                        flag = token[1:]
@@ -2475,6 +2486,11 @@ def check_required_use(required_use, use, iuse_match):
                        is_negated = False
 
                if not flag or not iuse_match(flag):
+                       if not eapi_attrs.required_use_at_most_one_of and flag == "?":
+                               msg = _("Operator '??' is not supported with EAPI '%s'") \
+                                       % (eapi,)
+                               e = InvalidData(msg, category='EAPI.incompatible')
+                               raise InvalidDependString(msg, errors=(e,))
                        msg = _("USE flag '%s' is not in IUSE") \
                                % (flag,)
                        e = InvalidData(msg, category='IUSE.missing')
@@ -2492,6 +2508,8 @@ def check_required_use(required_use, use, iuse_match):
                        return (True in argument)
                elif operator == "^^":
                        return (argument.count(True) == 1)
+               elif operator == "??":
+                       return (argument.count(True) <= 1)
                elif operator[-1] == "?":
                        return (False not in argument)
 
@@ -2521,7 +2539,7 @@ def check_required_use(required_use, use, iuse_match):
                                l = stack.pop()
                                op = None
                                if stack[level]:
-                                       if stack[level][-1] in ("||", "^^"):
+                                       if stack[level][-1] in valid_operators:
                                                op = stack[level].pop()
                                                satisfied = is_satisfied(op, l)
                                                stack[level].append(satisfied)
@@ -2550,7 +2568,7 @@ def check_required_use(required_use, use, iuse_match):
                                                stack[level].append(satisfied)
 
                                        if len(node._children) <= 1 or \
-                                               node._parent._operator not in ("||", "^^"):
+                                               node._parent._operator not in valid_operators:
                                                last_node = node._parent._children.pop()
                                                if last_node is not node:
                                                        raise AssertionError(
@@ -2566,7 +2584,7 @@ def check_required_use(required_use, use, iuse_match):
                                                raise AssertionError(
                                                        "node is not last child of parent")
 
-                               elif len(node._children) == 1 and op in ("||", "^^"):
+                               elif len(node._children) == 1 and op in valid_operators:
                                        last_node = node._parent._children.pop()
                                        if last_node is not node:
                                                raise AssertionError(
@@ -2576,7 +2594,7 @@ def check_required_use(required_use, use, iuse_match):
                                                node._children[0]._parent = node._parent
                                                node = node._children[0]
                                                if node._operator is None and \
-                                                       node._parent._operator not in ("||", "^^"):
+                                                       node._parent._operator not in valid_operators:
                                                        last_node = node._parent._children.pop()
                                                        if last_node is not node:
                                                                raise AssertionError(
@@ -2590,7 +2608,7 @@ def check_required_use(required_use, use, iuse_match):
                        else:
                                raise InvalidDependString(
                                        _("malformed syntax: '%s'") % required_use)
-               elif token in ("||", "^^"):
+               elif token in valid_operators:
                        if need_bracket:
                                raise InvalidDependString(
                                        _("malformed syntax: '%s'") % required_use)
@@ -2600,8 +2618,7 @@ def check_required_use(required_use, use, iuse_match):
                        node._children.append(child)
                        node = child
                else:
-                       if need_bracket or "(" in token or ")" in token or \
-                               "|" in token or "^" in token:
+                       if need_bracket:
                                raise InvalidDependString(
                                        _("malformed syntax: '%s'") % required_use)
 
index a5ef30143fb54f043c7de5554572106887239ea8..b701d02ab614d1f1476da3808d6a6554dff82a22 100644 (file)
@@ -56,6 +56,9 @@ def eapi_has_dosed_dohard(eapi):
 def eapi_has_required_use(eapi):
        return eapi not in ("0", "1", "2", "3")
 
+def eapi_has_required_use_at_most_one_of(eapi):
+       return eapi not in ("0", "1", "2", "3", "4", "4-python", "4-slot-abi")
+
 def eapi_has_use_dep_defaults(eapi):
        return eapi not in ("0", "1", "2", "3")
 
@@ -70,7 +73,7 @@ def eapi_allows_dots_in_use_flags(eapi):
 
 _eapi_attrs = collections.namedtuple('_eapi_attrs',
        'dots_in_PN dots_in_use_flags iuse_defaults '
-       'repo_deps required_use slot_abi slot_deps '
+       'repo_deps required_use required_use_at_most_one_of slot_abi slot_deps '
        'src_uri_arrows strong_blocks use_deps use_dep_defaults')
 
 _eapi_attrs_cache = {}
@@ -97,6 +100,7 @@ def _get_eapi_attrs(eapi):
                iuse_defaults = (eapi is None or eapi_has_iuse_defaults(eapi)),
                repo_deps = (eapi is None or eapi_has_repo_deps(eapi)),
                required_use = (eapi is None or eapi_has_required_use(eapi)),
+               required_use_at_most_one_of = (eapi is None or eapi_has_required_use_at_most_one_of(eapi)),
                slot_deps = (eapi is None or eapi_has_slot_deps(eapi)),
                slot_abi = (eapi is None or eapi_has_slot_abi(eapi)),
                src_uri_arrows = (eapi is None or eapi_has_src_uri_arrows(eapi)),
index 54791e016b467ff59c3a821dd0779647fb684fd5..d85ad92d3bc9f2218c42f7fe0df202cc1f00dcc8 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2010-2011 Gentoo Foundation
+# Copyright 2010-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -18,6 +18,11 @@ class TestCheckRequiredUse(TestCase):
                        ( "^^ ( a b )", ["a"], ["a", "b"], True),
                        ( "^^ ( a b )", ["b"], ["a", "b"], True),
                        ( "^^ ( a b )", ["a", "b"], ["a", "b"], False),
+                       ( "?? ( a b )", ["a", "b"], ["a", "b"], False),
+                       ( "?? ( a b )", ["a"], ["a", "b"], True),
+                       ( "?? ( a b )", ["b"], ["a", "b"], True),
+                       ( "?? ( a b )", [], ["a", "b"], True),
+                       ( "?? ( )", [], [], True),
 
                        ( "^^ ( || ( a b ) c )", [], ["a", "b", "c"], False),
                        ( "^^ ( || ( a b ) c )", ["a"], ["a", "b", "c"], True),
@@ -102,6 +107,10 @@ class TestCheckRequiredUse(TestCase):
                        ( "^^ ( || ( a b ) ) ^^ ( b c ) )", [], ["a", "b", "c"]),
                )
 
+               test_cases_xfail_eapi = (
+                       ( "?? ( a b )", [], ["a", "b"], "4"),
+               )
+
                for required_use, use, iuse, expected in test_cases:
                        self.assertEqual(bool(check_required_use(required_use, use, iuse.__contains__)), \
                                expected, required_use + ", USE = " + " ".join(use))
@@ -110,6 +119,11 @@ class TestCheckRequiredUse(TestCase):
                        self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \
                                InvalidDependString, check_required_use, required_use, use, iuse.__contains__)
 
+               for required_use, use, iuse, eapi in test_cases_xfail_eapi:
+                       self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \
+                               InvalidDependString, check_required_use, required_use, use,
+                               iuse.__contains__, eapi=eapi)
+
        def testCheckRequiredUseFilterSatisfied(self):
                """
                Test filtering of satisfied parts of REQUIRED_USE,
index 06f81106aa6361e22068d1235cbe7fc3677dcae3..90e096c781cd53254ade2df2f32fdc9eb5425526 100644 (file)
@@ -1,4 +1,4 @@
-# Copyright 2010 Gentoo Foundation
+# Copyright 2010-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
 from portage.tests import TestCase
@@ -13,6 +13,8 @@ class TestCheckRequiredUse(TestCase):
 
                        ("|| ( a b c )", ["a", "b", "c"]),
                        ("^^ ( a b c )", ["a", "b", "c"]),
+                       ("?? ( a b c )", ["a", "b", "c"]),
+                       ("?? ( )", []),
 
                        ("|| ( a b ^^ ( d e f ) )", ["a", "b", "d", "e", "f"]),
                        ("^^ ( a b || ( d e f ) )", ["a", "b", "d", "e", "f"]),