1 |
commit: ac843c3df2210566b559dc57c5fb657e20933a58 |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Mon Aug 27 22:13:29 2012 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Mon Aug 27 22:13:29 2012 +0000 |
6 |
URL: http://git.overlays.gentoo.org/gitweb/?p=proj/portage.git;a=commit;h=ac843c3d |
7 |
|
8 |
EAPI 5: REQUIRED_USE at-most-one-of ?? operator |
9 |
|
10 |
See bug #354219 and the PMS patch: |
11 |
http://git.overlays.gentoo.org/gitweb/?p=proj/pms.git;a=commit;h=1c2dff2df2305aff88a734e3a2716de1bb69f3b6 |
12 |
|
13 |
--- |
14 |
bin/repoman | 2 +- |
15 |
pym/_emerge/Package.py | 2 +- |
16 |
pym/portage/dep/__init__.py | 47 +++++++++++++------ |
17 |
pym/portage/eapi.py | 6 ++- |
18 |
pym/portage/tests/dep/testCheckRequiredUse.py | 16 ++++++- |
19 |
.../tests/dep/test_get_required_use_flags.py | 4 +- |
20 |
6 files changed, 57 insertions(+), 20 deletions(-) |
21 |
|
22 |
diff --git a/bin/repoman b/bin/repoman |
23 |
index b50fac8..dd065c8 100755 |
24 |
--- a/bin/repoman |
25 |
+++ b/bin/repoman |
26 |
@@ -2028,7 +2028,7 @@ for x in effective_scanlist: |
27 |
" not supported with EAPI='%s'" % (eapi,)) |
28 |
try: |
29 |
portage.dep.check_required_use(required_use, (), |
30 |
- pkg.iuse.is_valid_flag) |
31 |
+ pkg.iuse.is_valid_flag, eapi=eapi) |
32 |
except portage.exception.InvalidDependString as e: |
33 |
stats["REQUIRED_USE.syntax"] = stats["REQUIRED_USE.syntax"] + 1 |
34 |
fails["REQUIRED_USE.syntax"].append( |
35 |
|
36 |
diff --git a/pym/_emerge/Package.py b/pym/_emerge/Package.py |
37 |
index 85fc597..2087cbf 100644 |
38 |
--- a/pym/_emerge/Package.py |
39 |
+++ b/pym/_emerge/Package.py |
40 |
@@ -228,7 +228,7 @@ class Package(Task): |
41 |
else: |
42 |
try: |
43 |
check_required_use(v, (), |
44 |
- self.iuse.is_valid_flag) |
45 |
+ self.iuse.is_valid_flag, eapi=eapi) |
46 |
except InvalidDependString as e: |
47 |
# Force unicode format string for python-2.x safety, |
48 |
# ensuring that PortageException.__unicode__() is used |
49 |
|
50 |
diff --git a/pym/portage/dep/__init__.py b/pym/portage/dep/__init__.py |
51 |
index e547deb..b7bb46f 100644 |
52 |
--- a/pym/portage/dep/__init__.py |
53 |
+++ b/pym/portage/dep/__init__.py |
54 |
@@ -2314,9 +2314,9 @@ def match_from_list(mydep, candidate_list): |
55 |
return mylist |
56 |
|
57 |
def human_readable_required_use(required_use): |
58 |
- return required_use.replace("^^", "exactly-one-of").replace("||", "any-of") |
59 |
+ return required_use.replace("^^", "exactly-one-of").replace("||", "any-of").replace("??", "at-most-one-of") |
60 |
|
61 |
-def get_required_use_flags(required_use): |
62 |
+def get_required_use_flags(required_use, eapi=None): |
63 |
""" |
64 |
Returns a set of use flags that are used in the given REQUIRED_USE string |
65 |
|
66 |
@@ -2326,6 +2326,12 @@ def get_required_use_flags(required_use): |
67 |
@return: Set of use flags that are used in the given REQUIRED_USE string |
68 |
""" |
69 |
|
70 |
+ eapi_attrs = _get_eapi_attrs(eapi) |
71 |
+ if eapi_attrs.required_use_at_most_one_of: |
72 |
+ valid_operators = ("||", "^^", "??") |
73 |
+ else: |
74 |
+ valid_operators = ("||", "^^") |
75 |
+ |
76 |
mysplit = required_use.split() |
77 |
level = 0 |
78 |
stack = [[]] |
79 |
@@ -2354,7 +2360,7 @@ def get_required_use_flags(required_use): |
80 |
l = stack.pop() |
81 |
ignore = False |
82 |
if stack[level]: |
83 |
- if stack[level][-1] in ("||", "^^") or \ |
84 |
+ if stack[level][-1] in valid_operators or \ |
85 |
(not isinstance(stack[level][-1], bool) and \ |
86 |
stack[level][-1][-1] == "?"): |
87 |
ignore = True |
88 |
@@ -2366,15 +2372,14 @@ def get_required_use_flags(required_use): |
89 |
else: |
90 |
raise InvalidDependString( |
91 |
_("malformed syntax: '%s'") % required_use) |
92 |
- elif token in ("||", "^^"): |
93 |
+ elif token in valid_operators: |
94 |
if need_bracket: |
95 |
raise InvalidDependString( |
96 |
_("malformed syntax: '%s'") % required_use) |
97 |
need_bracket = True |
98 |
stack[level].append(token) |
99 |
else: |
100 |
- if need_bracket or "(" in token or ")" in token or \ |
101 |
- "|" in token or "^" in token: |
102 |
+ if need_bracket: |
103 |
raise InvalidDependString( |
104 |
_("malformed syntax: '%s'") % required_use) |
105 |
|
106 |
@@ -2429,7 +2434,7 @@ class _RequiredUseBranch(object): |
107 |
complex_nesting = False |
108 |
node = self |
109 |
while node != None and not complex_nesting: |
110 |
- if node._operator in ("||", "^^"): |
111 |
+ if node._operator in ("||", "^^", "??"): |
112 |
complex_nesting = True |
113 |
else: |
114 |
node = node._parent |
115 |
@@ -2450,7 +2455,7 @@ class _RequiredUseBranch(object): |
116 |
if sys.hexversion < 0x3000000: |
117 |
__nonzero__ = __bool__ |
118 |
|
119 |
-def check_required_use(required_use, use, iuse_match): |
120 |
+def check_required_use(required_use, use, iuse_match, eapi=None): |
121 |
""" |
122 |
Checks if the use flags listed in 'use' satisfy all |
123 |
constraints specified in 'constraints'. |
124 |
@@ -2466,6 +2471,12 @@ def check_required_use(required_use, use, iuse_match): |
125 |
@return: Indicates if REQUIRED_USE constraints are satisfied |
126 |
""" |
127 |
|
128 |
+ eapi_attrs = _get_eapi_attrs(eapi) |
129 |
+ if eapi_attrs.required_use_at_most_one_of: |
130 |
+ valid_operators = ("||", "^^", "??") |
131 |
+ else: |
132 |
+ valid_operators = ("||", "^^") |
133 |
+ |
134 |
def is_active(token): |
135 |
if token.startswith("!"): |
136 |
flag = token[1:] |
137 |
@@ -2475,6 +2486,11 @@ def check_required_use(required_use, use, iuse_match): |
138 |
is_negated = False |
139 |
|
140 |
if not flag or not iuse_match(flag): |
141 |
+ if not eapi_attrs.required_use_at_most_one_of and flag == "?": |
142 |
+ msg = _("Operator '??' is not supported with EAPI '%s'") \ |
143 |
+ % (eapi,) |
144 |
+ e = InvalidData(msg, category='EAPI.incompatible') |
145 |
+ raise InvalidDependString(msg, errors=(e,)) |
146 |
msg = _("USE flag '%s' is not in IUSE") \ |
147 |
% (flag,) |
148 |
e = InvalidData(msg, category='IUSE.missing') |
149 |
@@ -2492,6 +2508,8 @@ def check_required_use(required_use, use, iuse_match): |
150 |
return (True in argument) |
151 |
elif operator == "^^": |
152 |
return (argument.count(True) == 1) |
153 |
+ elif operator == "??": |
154 |
+ return (argument.count(True) <= 1) |
155 |
elif operator[-1] == "?": |
156 |
return (False not in argument) |
157 |
|
158 |
@@ -2521,7 +2539,7 @@ def check_required_use(required_use, use, iuse_match): |
159 |
l = stack.pop() |
160 |
op = None |
161 |
if stack[level]: |
162 |
- if stack[level][-1] in ("||", "^^"): |
163 |
+ if stack[level][-1] in valid_operators: |
164 |
op = stack[level].pop() |
165 |
satisfied = is_satisfied(op, l) |
166 |
stack[level].append(satisfied) |
167 |
@@ -2550,7 +2568,7 @@ def check_required_use(required_use, use, iuse_match): |
168 |
stack[level].append(satisfied) |
169 |
|
170 |
if len(node._children) <= 1 or \ |
171 |
- node._parent._operator not in ("||", "^^"): |
172 |
+ node._parent._operator not in valid_operators: |
173 |
last_node = node._parent._children.pop() |
174 |
if last_node is not node: |
175 |
raise AssertionError( |
176 |
@@ -2566,7 +2584,7 @@ def check_required_use(required_use, use, iuse_match): |
177 |
raise AssertionError( |
178 |
"node is not last child of parent") |
179 |
|
180 |
- elif len(node._children) == 1 and op in ("||", "^^"): |
181 |
+ elif len(node._children) == 1 and op in valid_operators: |
182 |
last_node = node._parent._children.pop() |
183 |
if last_node is not node: |
184 |
raise AssertionError( |
185 |
@@ -2576,7 +2594,7 @@ def check_required_use(required_use, use, iuse_match): |
186 |
node._children[0]._parent = node._parent |
187 |
node = node._children[0] |
188 |
if node._operator is None and \ |
189 |
- node._parent._operator not in ("||", "^^"): |
190 |
+ node._parent._operator not in valid_operators: |
191 |
last_node = node._parent._children.pop() |
192 |
if last_node is not node: |
193 |
raise AssertionError( |
194 |
@@ -2590,7 +2608,7 @@ def check_required_use(required_use, use, iuse_match): |
195 |
else: |
196 |
raise InvalidDependString( |
197 |
_("malformed syntax: '%s'") % required_use) |
198 |
- elif token in ("||", "^^"): |
199 |
+ elif token in valid_operators: |
200 |
if need_bracket: |
201 |
raise InvalidDependString( |
202 |
_("malformed syntax: '%s'") % required_use) |
203 |
@@ -2600,8 +2618,7 @@ def check_required_use(required_use, use, iuse_match): |
204 |
node._children.append(child) |
205 |
node = child |
206 |
else: |
207 |
- if need_bracket or "(" in token or ")" in token or \ |
208 |
- "|" in token or "^" in token: |
209 |
+ if need_bracket: |
210 |
raise InvalidDependString( |
211 |
_("malformed syntax: '%s'") % required_use) |
212 |
|
213 |
|
214 |
diff --git a/pym/portage/eapi.py b/pym/portage/eapi.py |
215 |
index a5ef301..b701d02 100644 |
216 |
--- a/pym/portage/eapi.py |
217 |
+++ b/pym/portage/eapi.py |
218 |
@@ -56,6 +56,9 @@ def eapi_has_dosed_dohard(eapi): |
219 |
def eapi_has_required_use(eapi): |
220 |
return eapi not in ("0", "1", "2", "3") |
221 |
|
222 |
+def eapi_has_required_use_at_most_one_of(eapi): |
223 |
+ return eapi not in ("0", "1", "2", "3", "4", "4-python", "4-slot-abi") |
224 |
+ |
225 |
def eapi_has_use_dep_defaults(eapi): |
226 |
return eapi not in ("0", "1", "2", "3") |
227 |
|
228 |
@@ -70,7 +73,7 @@ def eapi_allows_dots_in_use_flags(eapi): |
229 |
|
230 |
_eapi_attrs = collections.namedtuple('_eapi_attrs', |
231 |
'dots_in_PN dots_in_use_flags iuse_defaults ' |
232 |
- 'repo_deps required_use slot_abi slot_deps ' |
233 |
+ 'repo_deps required_use required_use_at_most_one_of slot_abi slot_deps ' |
234 |
'src_uri_arrows strong_blocks use_deps use_dep_defaults') |
235 |
|
236 |
_eapi_attrs_cache = {} |
237 |
@@ -97,6 +100,7 @@ def _get_eapi_attrs(eapi): |
238 |
iuse_defaults = (eapi is None or eapi_has_iuse_defaults(eapi)), |
239 |
repo_deps = (eapi is None or eapi_has_repo_deps(eapi)), |
240 |
required_use = (eapi is None or eapi_has_required_use(eapi)), |
241 |
+ required_use_at_most_one_of = (eapi is None or eapi_has_required_use_at_most_one_of(eapi)), |
242 |
slot_deps = (eapi is None or eapi_has_slot_deps(eapi)), |
243 |
slot_abi = (eapi is None or eapi_has_slot_abi(eapi)), |
244 |
src_uri_arrows = (eapi is None or eapi_has_src_uri_arrows(eapi)), |
245 |
|
246 |
diff --git a/pym/portage/tests/dep/testCheckRequiredUse.py b/pym/portage/tests/dep/testCheckRequiredUse.py |
247 |
index 54791e0..d85ad92 100644 |
248 |
--- a/pym/portage/tests/dep/testCheckRequiredUse.py |
249 |
+++ b/pym/portage/tests/dep/testCheckRequiredUse.py |
250 |
@@ -1,4 +1,4 @@ |
251 |
-# Copyright 2010-2011 Gentoo Foundation |
252 |
+# Copyright 2010-2012 Gentoo Foundation |
253 |
# Distributed under the terms of the GNU General Public License v2 |
254 |
|
255 |
from portage.tests import TestCase |
256 |
@@ -18,6 +18,11 @@ class TestCheckRequiredUse(TestCase): |
257 |
( "^^ ( a b )", ["a"], ["a", "b"], True), |
258 |
( "^^ ( a b )", ["b"], ["a", "b"], True), |
259 |
( "^^ ( a b )", ["a", "b"], ["a", "b"], False), |
260 |
+ ( "?? ( a b )", ["a", "b"], ["a", "b"], False), |
261 |
+ ( "?? ( a b )", ["a"], ["a", "b"], True), |
262 |
+ ( "?? ( a b )", ["b"], ["a", "b"], True), |
263 |
+ ( "?? ( a b )", [], ["a", "b"], True), |
264 |
+ ( "?? ( )", [], [], True), |
265 |
|
266 |
( "^^ ( || ( a b ) c )", [], ["a", "b", "c"], False), |
267 |
( "^^ ( || ( a b ) c )", ["a"], ["a", "b", "c"], True), |
268 |
@@ -102,6 +107,10 @@ class TestCheckRequiredUse(TestCase): |
269 |
( "^^ ( || ( a b ) ) ^^ ( b c ) )", [], ["a", "b", "c"]), |
270 |
) |
271 |
|
272 |
+ test_cases_xfail_eapi = ( |
273 |
+ ( "?? ( a b )", [], ["a", "b"], "4"), |
274 |
+ ) |
275 |
+ |
276 |
for required_use, use, iuse, expected in test_cases: |
277 |
self.assertEqual(bool(check_required_use(required_use, use, iuse.__contains__)), \ |
278 |
expected, required_use + ", USE = " + " ".join(use)) |
279 |
@@ -110,6 +119,11 @@ class TestCheckRequiredUse(TestCase): |
280 |
self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ |
281 |
InvalidDependString, check_required_use, required_use, use, iuse.__contains__) |
282 |
|
283 |
+ for required_use, use, iuse, eapi in test_cases_xfail_eapi: |
284 |
+ self.assertRaisesMsg(required_use + ", USE = " + " ".join(use), \ |
285 |
+ InvalidDependString, check_required_use, required_use, use, |
286 |
+ iuse.__contains__, eapi=eapi) |
287 |
+ |
288 |
def testCheckRequiredUseFilterSatisfied(self): |
289 |
""" |
290 |
Test filtering of satisfied parts of REQUIRED_USE, |
291 |
|
292 |
diff --git a/pym/portage/tests/dep/test_get_required_use_flags.py b/pym/portage/tests/dep/test_get_required_use_flags.py |
293 |
index 06f8110..90e096c 100644 |
294 |
--- a/pym/portage/tests/dep/test_get_required_use_flags.py |
295 |
+++ b/pym/portage/tests/dep/test_get_required_use_flags.py |
296 |
@@ -1,4 +1,4 @@ |
297 |
-# Copyright 2010 Gentoo Foundation |
298 |
+# Copyright 2010-2012 Gentoo Foundation |
299 |
# Distributed under the terms of the GNU General Public License v2 |
300 |
|
301 |
from portage.tests import TestCase |
302 |
@@ -13,6 +13,8 @@ class TestCheckRequiredUse(TestCase): |
303 |
|
304 |
("|| ( a b c )", ["a", "b", "c"]), |
305 |
("^^ ( a b c )", ["a", "b", "c"]), |
306 |
+ ("?? ( a b c )", ["a", "b", "c"]), |
307 |
+ ("?? ( )", []), |
308 |
|
309 |
("|| ( a b ^^ ( d e f ) )", ["a", "b", "d", "e", "f"]), |
310 |
("^^ ( a b || ( d e f ) )", ["a", "b", "d", "e", "f"]), |