1 |
commit: 85f0dd173ab75bcc39c3616b5a3a967bdc88cf73 |
2 |
Author: Zac Medico <zmedico <AT> gentoo <DOT> org> |
3 |
AuthorDate: Fri Dec 20 06:58:58 2019 +0000 |
4 |
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org> |
5 |
CommitDate: Mon Dec 23 22:23:15 2019 +0000 |
6 |
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=85f0dd17 |
7 |
|
8 |
emerge --with-test-deps: allow circular deps |
9 |
|
10 |
When USE=test is not enabled, allow circular test dependencies |
11 |
by treating them like PDEPEND. When USE=test is enabled, circular |
12 |
dependencies are still not allowed, as shown in unit tests. |
13 |
|
14 |
Suggested-by: Michał Górny <mgorny <AT> gentoo.org> |
15 |
Bug: https://bugs.gentoo.org/703348 |
16 |
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org> |
17 |
|
18 |
lib/_emerge/depgraph.py | 19 ++++-- |
19 |
lib/portage/dep/__init__.py | 44 +++++++++++++- |
20 |
lib/portage/tests/dep/test_use_reduce.py | 72 ++++++++++++++++++++++- |
21 |
lib/portage/tests/resolver/test_with_test_deps.py | 39 +++++++++++- |
22 |
4 files changed, 166 insertions(+), 8 deletions(-) |
23 |
|
24 |
diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py |
25 |
index 1a5448c8f..83631fe70 100644 |
26 |
--- a/lib/_emerge/depgraph.py |
27 |
+++ b/lib/_emerge/depgraph.py |
28 |
@@ -3325,10 +3325,6 @@ class depgraph(object): |
29 |
pkg.iuse.is_valid_flag("test") and \ |
30 |
self._is_argument(pkg) |
31 |
|
32 |
- if with_test_deps: |
33 |
- use_enabled = set(use_enabled) |
34 |
- use_enabled.add("test") |
35 |
- |
36 |
if not pkg.built and \ |
37 |
"--buildpkgonly" in self._frozen_config.myopts and \ |
38 |
"deep" not in self._dynamic_config.myparams: |
39 |
@@ -3430,6 +3426,21 @@ class depgraph(object): |
40 |
noiselevel=-1, level=logging.DEBUG) |
41 |
|
42 |
try: |
43 |
+ if (with_test_deps and 'test' not in use_enabled and |
44 |
+ pkg.iuse.is_valid_flag('test')): |
45 |
+ test_deps = portage.dep.use_reduce(dep_string, |
46 |
+ uselist=use_enabled | {'test'}, |
47 |
+ is_valid_flag=pkg.iuse.is_valid_flag, |
48 |
+ opconvert=True, token_class=Atom, |
49 |
+ eapi=pkg.eapi, |
50 |
+ subset={'test'}) |
51 |
+ |
52 |
+ if test_deps and not self._add_pkg_dep_string( |
53 |
+ pkg, dep_root, self._priority(runtime_post=True), |
54 |
+ test_deps, |
55 |
+ allow_unsatisfied): |
56 |
+ return 0 |
57 |
+ |
58 |
dep_string = portage.dep.use_reduce(dep_string, |
59 |
uselist=use_enabled, |
60 |
is_valid_flag=pkg.iuse.is_valid_flag, |
61 |
|
62 |
diff --git a/lib/portage/dep/__init__.py b/lib/portage/dep/__init__.py |
63 |
index f08f6ba4c..72988357a 100644 |
64 |
--- a/lib/portage/dep/__init__.py |
65 |
+++ b/lib/portage/dep/__init__.py |
66 |
@@ -405,7 +405,8 @@ def paren_enclose(mylist, unevaluated_atom=False, opconvert=False): |
67 |
return " ".join(mystrparts) |
68 |
|
69 |
def use_reduce(depstr, uselist=(), masklist=(), matchall=False, excludeall=(), is_src_uri=False, \ |
70 |
- eapi=None, opconvert=False, flat=False, is_valid_flag=None, token_class=None, matchnone=False): |
71 |
+ eapi=None, opconvert=False, flat=False, is_valid_flag=None, token_class=None, matchnone=False, |
72 |
+ subset=None): |
73 |
""" |
74 |
Takes a dep string and reduces the use? conditionals out, leaving an array |
75 |
with subarrays. All redundant brackets are removed. |
76 |
@@ -434,6 +435,8 @@ def use_reduce(depstr, uselist=(), masklist=(), matchall=False, excludeall=(), i |
77 |
@type token_class: Class |
78 |
@param matchnone: Treat all conditionals as inactive. Used by digestgen(). |
79 |
@type matchnone: Bool |
80 |
+ @param subset: Select a subset of dependencies conditional on the given flags |
81 |
+ @type subset: Sequence |
82 |
@rtype: List |
83 |
@return: The use reduced depend array |
84 |
""" |
85 |
@@ -491,6 +494,45 @@ def use_reduce(depstr, uselist=(), masklist=(), matchall=False, excludeall=(), i |
86 |
return (flag in uselist and not is_negated) or \ |
87 |
(flag not in uselist and is_negated) |
88 |
|
89 |
+ if subset: |
90 |
+ def select_subset(dep_struct, disjunction, selected): |
91 |
+ result = [] |
92 |
+ stack = list(dep_struct) |
93 |
+ stack.reverse() |
94 |
+ while stack: |
95 |
+ token = stack.pop() |
96 |
+ try: |
97 |
+ conditional = token.endswith('?') |
98 |
+ except AttributeError: |
99 |
+ if disjunction: |
100 |
+ children = select_subset(token, False, selected) |
101 |
+ if children: |
102 |
+ result.append(children) |
103 |
+ else: |
104 |
+ result.extend(select_subset(token, False, selected)) |
105 |
+ else: |
106 |
+ if conditional: |
107 |
+ children = stack.pop() |
108 |
+ if is_active(token): |
109 |
+ if disjunction: |
110 |
+ children = select_subset(children, False, selected or token[:-1] in subset) |
111 |
+ if children: |
112 |
+ result.append(children) |
113 |
+ else: |
114 |
+ result.extend(select_subset(children, False, selected or token[:-1] in subset)) |
115 |
+ elif token == '||': |
116 |
+ children = select_subset(stack.pop(), True, selected) |
117 |
+ if children: |
118 |
+ if disjunction: |
119 |
+ result.extend(children) |
120 |
+ else: |
121 |
+ result.append(token) |
122 |
+ result.append(children) |
123 |
+ elif selected: |
124 |
+ result.append(token) |
125 |
+ return result |
126 |
+ depstr = paren_enclose(select_subset(paren_reduce(depstr, _deprecation_warn=False), False, False)) |
127 |
+ |
128 |
def missing_white_space_check(token, pos): |
129 |
""" |
130 |
Used to generate good error messages for invalid tokens. |
131 |
|
132 |
diff --git a/lib/portage/tests/dep/test_use_reduce.py b/lib/portage/tests/dep/test_use_reduce.py |
133 |
index 4f65567cf..d9ee5a309 100644 |
134 |
--- a/lib/portage/tests/dep/test_use_reduce.py |
135 |
+++ b/lib/portage/tests/dep/test_use_reduce.py |
136 |
@@ -9,7 +9,7 @@ class UseReduceTestCase(object): |
137 |
def __init__(self, deparray, uselist=[], masklist=[], |
138 |
matchall=0, excludeall=[], is_src_uri=False, |
139 |
eapi='0', opconvert=False, flat=False, expected_result=None, |
140 |
- is_valid_flag=None, token_class=None): |
141 |
+ is_valid_flag=None, token_class=None, subset=None): |
142 |
self.deparray = deparray |
143 |
self.uselist = uselist |
144 |
self.masklist = masklist |
145 |
@@ -21,13 +21,15 @@ class UseReduceTestCase(object): |
146 |
self.flat = flat |
147 |
self.is_valid_flag = is_valid_flag |
148 |
self.token_class = token_class |
149 |
+ self.subset = subset |
150 |
self.expected_result = expected_result |
151 |
|
152 |
def run(self): |
153 |
try: |
154 |
return use_reduce(self.deparray, self.uselist, self.masklist, |
155 |
self.matchall, self.excludeall, self.is_src_uri, self.eapi, |
156 |
- self.opconvert, self.flat, self.is_valid_flag, self.token_class) |
157 |
+ self.opconvert, self.flat, self.is_valid_flag, self.token_class, |
158 |
+ subset=self.subset) |
159 |
except InvalidDependString as e: |
160 |
raise InvalidDependString("%s: %s" % (e, self.deparray)) |
161 |
|
162 |
@@ -50,6 +52,72 @@ class UseReduce(TestCase): |
163 |
uselist=["a", "b", "c", "d"], |
164 |
expected_result=["A", "B"] |
165 |
), |
166 |
+ UseReduceTestCase( |
167 |
+ "a? ( A ) b? ( B ) !c? ( C ) !d? ( D )", |
168 |
+ uselist=["a", "b", "c", "d"], |
169 |
+ subset=["b"], |
170 |
+ expected_result=["B"] |
171 |
+ ), |
172 |
+ UseReduceTestCase( |
173 |
+ "bar? ( || ( foo bar? ( baz ) ) )", |
174 |
+ uselist=["bar"], |
175 |
+ subset=["bar"], |
176 |
+ expected_result=['||', ['foo', 'baz']] |
177 |
+ ), |
178 |
+ UseReduceTestCase( |
179 |
+ "bar? ( foo bar? ( baz ) foo )", |
180 |
+ uselist=["bar"], |
181 |
+ subset=["bar"], |
182 |
+ expected_result=['foo', 'baz', 'foo'] |
183 |
+ ), |
184 |
+ UseReduceTestCase( |
185 |
+ "|| ( ( a b ) ( c d ) )", |
186 |
+ uselist=[], |
187 |
+ subset=["bar"], |
188 |
+ expected_result=[] |
189 |
+ ), |
190 |
+ UseReduceTestCase( |
191 |
+ "|| ( ( a b ) ( bar? ( c d ) e f ) )", |
192 |
+ uselist=["bar"], |
193 |
+ subset=["bar"], |
194 |
+ expected_result=['c', 'd'] |
195 |
+ ), |
196 |
+ UseReduceTestCase( |
197 |
+ "( a b ) ( c d bar? ( e f baz? ( g h ) ) )", |
198 |
+ uselist=["bar"], |
199 |
+ subset=["bar"], |
200 |
+ expected_result=['e', 'f'] |
201 |
+ ), |
202 |
+ UseReduceTestCase( |
203 |
+ "( a b ) ( c d bar? ( e f baz? ( g h ) ) )", |
204 |
+ uselist=["bar", "baz"], |
205 |
+ subset=["bar"], |
206 |
+ expected_result=['e', 'f', 'g', 'h'] |
207 |
+ ), |
208 |
+ UseReduceTestCase( |
209 |
+ "( bar? ( a b ) ( bar? ( c d ) ) ) ( e f )", |
210 |
+ uselist=["bar"], |
211 |
+ subset=["bar"], |
212 |
+ expected_result=['a', 'b', 'c', 'd'] |
213 |
+ ), |
214 |
+ UseReduceTestCase( |
215 |
+ "|| ( foo bar? ( baz ) )", |
216 |
+ uselist=["bar"], |
217 |
+ subset=["bar"], |
218 |
+ expected_result=["baz"] |
219 |
+ ), |
220 |
+ UseReduceTestCase( |
221 |
+ "|| ( || ( bar? ( a || ( b c || ( d e ) ) ) ) )", |
222 |
+ uselist=["bar"], |
223 |
+ subset=["bar"], |
224 |
+ expected_result=['a', '||', ['b', 'c', 'd', 'e']] |
225 |
+ ), |
226 |
+ UseReduceTestCase( |
227 |
+ "|| ( || ( bar? ( a || ( ( b c ) ( d e ) ) ) ) )", |
228 |
+ uselist=["bar"], |
229 |
+ subset=["bar"], |
230 |
+ expected_result=['a', '||', [['b', 'c'], ['d', 'e']]] |
231 |
+ ), |
232 |
UseReduceTestCase( |
233 |
"a? ( A ) b? ( B ) !c? ( C ) !d? ( D )", |
234 |
uselist=["a", "b", "c"], |
235 |
|
236 |
diff --git a/lib/portage/tests/resolver/test_with_test_deps.py b/lib/portage/tests/resolver/test_with_test_deps.py |
237 |
index 5bfc6a8a2..d88e3cb6e 100644 |
238 |
--- a/lib/portage/tests/resolver/test_with_test_deps.py |
239 |
+++ b/lib/portage/tests/resolver/test_with_test_deps.py |
240 |
@@ -21,7 +21,27 @@ class WithTestDepsTestCase(TestCase): |
241 |
}, |
242 |
"app-misc/C-0": { |
243 |
"EAPI": "5", |
244 |
- } |
245 |
+ }, |
246 |
+ "app-misc/D-0": { |
247 |
+ "EAPI": "5", |
248 |
+ "IUSE": "test", |
249 |
+ "DEPEND": "test? ( app-misc/E )" |
250 |
+ }, |
251 |
+ "app-misc/E-0": { |
252 |
+ "EAPI": "5", |
253 |
+ "IUSE": "test", |
254 |
+ "DEPEND": "test? ( app-misc/D )" |
255 |
+ }, |
256 |
+ "app-misc/F-0": { |
257 |
+ "EAPI": "5", |
258 |
+ "IUSE": "+test", |
259 |
+ "DEPEND": "test? ( app-misc/G )" |
260 |
+ }, |
261 |
+ "app-misc/G-0": { |
262 |
+ "EAPI": "5", |
263 |
+ "IUSE": "+test", |
264 |
+ "DEPEND": "test? ( app-misc/F )" |
265 |
+ }, |
266 |
} |
267 |
|
268 |
test_cases = ( |
269 |
@@ -32,6 +52,23 @@ class WithTestDepsTestCase(TestCase): |
270 |
success = True, |
271 |
options = { "--onlydeps": True, "--with-test-deps": True }, |
272 |
mergelist = ["app-misc/B-0"]), |
273 |
+ |
274 |
+ # Test that --with-test-deps allows circular dependencies. |
275 |
+ ResolverPlaygroundTestCase( |
276 |
+ ['app-misc/D'], |
277 |
+ success = True, |
278 |
+ options = {'--with-test-deps': True}, |
279 |
+ mergelist = [('app-misc/D-0', 'app-misc/E-0')], |
280 |
+ ambiguous_merge_order=True), |
281 |
+ |
282 |
+ # Test that --with-test-deps does not allow circular dependencies |
283 |
+ # when USE=test is explicitly enabled. |
284 |
+ ResolverPlaygroundTestCase( |
285 |
+ ['app-misc/F'], |
286 |
+ success = False, |
287 |
+ options = {'--with-test-deps': True}, |
288 |
+ circular_dependency_solutions = {'app-misc/G-0': {frozenset({('test', False)})}, 'app-misc/F-0': {frozenset({('test', False)})}}, |
289 |
+ ) |
290 |
) |
291 |
|
292 |
playground = ResolverPlayground(ebuilds=ebuilds, debug=False) |