Gentoo Archives: gentoo-portage-dev

From: Zac Medico <zmedico@g.o>
To: gentoo-portage-dev@l.g.o
Cc: Zac Medico <zmedico@g.o>, "Michał Górny" <mgorny@g.o>
Subject: [gentoo-portage-dev] [PATCH] emerge --with-test-deps: allow circular deps
Date: Fri, 20 Dec 2019 09:49:59
Message-Id: 20191220094921.27666-1-zmedico@gentoo.org
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