From: Zac Medico Date: Mon, 27 Aug 2012 22:13:29 +0000 (-0700) Subject: EAPI 5: REQUIRED_USE at-most-one-of ?? operator X-Git-Tag: v2.2.0_alpha124~39 X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=ac843c3df2210566b559dc57c5fb657e20933a58;p=portage.git EAPI 5: REQUIRED_USE at-most-one-of ?? operator See bug #354219 and the PMS patch: http://git.overlays.gentoo.org/gitweb/?p=proj/pms.git;a=commit;h=1c2dff2df2305aff88a734e3a2716de1bb69f3b6 --- diff --git a/bin/repoman b/bin/repoman index b50fac82c..dd065c8d6 100755 --- a/bin/repoman +++ b/bin/repoman @@ -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( diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py index 85fc59773..2087cbfe0 100644 --- a/pym/_emerge/Package.py +++ b/pym/_emerge/Package.py @@ -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 diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py index e547debd4..b7bb46f75 100644 --- a/pym/portage/dep/__init__.py +++ b/pym/portage/dep/__init__.py @@ -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) diff --git a/pym/portage/eapi.py b/pym/portage/eapi.py index a5ef30143..b701d02ab 100644 --- a/pym/portage/eapi.py +++ b/pym/portage/eapi.py @@ -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)), diff --git a/pym/portage/tests/dep/testCheckRequiredUse.py b/pym/portage/tests/dep/testCheckRequiredUse.py index 54791e016..d85ad92d3 100644 --- a/pym/portage/tests/dep/testCheckRequiredUse.py +++ b/pym/portage/tests/dep/testCheckRequiredUse.py @@ -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, diff --git a/pym/portage/tests/dep/test_get_required_use_flags.py b/pym/portage/tests/dep/test_get_required_use_flags.py index 06f81106a..90e096c78 100644 --- a/pym/portage/tests/dep/test_get_required_use_flags.py +++ b/pym/portage/tests/dep/test_get_required_use_flags.py @@ -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"]),