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