public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "Zac Medico" <zmedico@gentoo.org>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/tests/resolver/, lib/portage/dep/, lib/_emerge/, ...
Date: Mon, 23 Dec 2019 22:47:48 +0000 (UTC)	[thread overview]
Message-ID: <1577139795.85f0dd173ab75bcc39c3616b5a3a967bdc88cf73.zmedico@gentoo> (raw)

commit:     85f0dd173ab75bcc39c3616b5a3a967bdc88cf73
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Fri Dec 20 06:58:58 2019 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Mon Dec 23 22:23:15 2019 +0000
URL:        https://gitweb.gentoo.org/proj/portage.git/commit/?id=85f0dd17

emerge --with-test-deps: allow circular deps

When USE=test is not enabled, allow circular test dependencies
by treating them like PDEPEND. When USE=test is enabled, circular
dependencies are still not allowed, as shown in unit tests.

Suggested-by: Michał Górny <mgorny <AT> gentoo.org>
Bug: https://bugs.gentoo.org/703348
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>

 lib/_emerge/depgraph.py                           | 19 ++++--
 lib/portage/dep/__init__.py                       | 44 +++++++++++++-
 lib/portage/tests/dep/test_use_reduce.py          | 72 ++++++++++++++++++++++-
 lib/portage/tests/resolver/test_with_test_deps.py | 39 +++++++++++-
 4 files changed, 166 insertions(+), 8 deletions(-)

diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py
index 1a5448c8f..83631fe70 100644
--- a/lib/_emerge/depgraph.py
+++ b/lib/_emerge/depgraph.py
@@ -3325,10 +3325,6 @@ class depgraph(object):
 			pkg.iuse.is_valid_flag("test") and \
 			self._is_argument(pkg)
 
-		if with_test_deps:
-			use_enabled = set(use_enabled)
-			use_enabled.add("test")
-
 		if not pkg.built and \
 			"--buildpkgonly" in self._frozen_config.myopts and \
 			"deep" not in self._dynamic_config.myparams:
@@ -3430,6 +3426,21 @@ class depgraph(object):
 						noiselevel=-1, level=logging.DEBUG)
 
 				try:
+					if (with_test_deps and 'test' not in use_enabled and
+						pkg.iuse.is_valid_flag('test')):
+						test_deps = portage.dep.use_reduce(dep_string,
+							uselist=use_enabled | {'test'},
+							is_valid_flag=pkg.iuse.is_valid_flag,
+							opconvert=True, token_class=Atom,
+							eapi=pkg.eapi,
+							subset={'test'})
+
+						if test_deps and not self._add_pkg_dep_string(
+							pkg, dep_root, self._priority(runtime_post=True),
+							test_deps,
+							allow_unsatisfied):
+							return 0
+
 					dep_string = portage.dep.use_reduce(dep_string,
 						uselist=use_enabled,
 						is_valid_flag=pkg.iuse.is_valid_flag,

diff --git a/lib/portage/dep/__init__.py b/lib/portage/dep/__init__.py
index f08f6ba4c..72988357a 100644
--- a/lib/portage/dep/__init__.py
+++ b/lib/portage/dep/__init__.py
@@ -405,7 +405,8 @@ def paren_enclose(mylist, unevaluated_atom=False, opconvert=False):
 	return " ".join(mystrparts)
 
 def use_reduce(depstr, uselist=(), masklist=(), matchall=False, excludeall=(), is_src_uri=False, \
-	eapi=None, opconvert=False, flat=False, is_valid_flag=None, token_class=None, matchnone=False):
+	eapi=None, opconvert=False, flat=False, is_valid_flag=None, token_class=None, matchnone=False,
+	subset=None):
 	"""
 	Takes a dep string and reduces the use? conditionals out, leaving an array
 	with subarrays. All redundant brackets are removed.
@@ -434,6 +435,8 @@ def use_reduce(depstr, uselist=(), masklist=(), matchall=False, excludeall=(), i
 	@type token_class: Class
 	@param matchnone: Treat all conditionals as inactive. Used by digestgen(). 
 	@type matchnone: Bool
+	@param subset: Select a subset of dependencies conditional on the given flags
+	@type subset: Sequence
 	@rtype: List
 	@return: The use reduced depend array
 	"""
@@ -491,6 +494,45 @@ def use_reduce(depstr, uselist=(), masklist=(), matchall=False, excludeall=(), i
 		return (flag in uselist and not is_negated) or \
 			(flag not in uselist and is_negated)
 
+	if subset:
+		def select_subset(dep_struct, disjunction, selected):
+			result = []
+			stack = list(dep_struct)
+			stack.reverse()
+			while stack:
+				token = stack.pop()
+				try:
+					conditional = token.endswith('?')
+				except AttributeError:
+					if disjunction:
+						children = select_subset(token, False, selected)
+						if children:
+							result.append(children)
+					else:
+						result.extend(select_subset(token, False, selected))
+				else:
+					if conditional:
+						children = stack.pop()
+						if is_active(token):
+							if disjunction:
+								children = select_subset(children, False, selected or token[:-1] in subset)
+								if children:
+									result.append(children)
+							else:
+								result.extend(select_subset(children, False, selected or token[:-1] in subset))
+					elif token == '||':
+						children = select_subset(stack.pop(), True, selected)
+						if children:
+							if disjunction:
+								result.extend(children)
+							else:
+								result.append(token)
+								result.append(children)
+					elif selected:
+						result.append(token)
+			return result
+		depstr = paren_enclose(select_subset(paren_reduce(depstr, _deprecation_warn=False), False, False))
+
 	def missing_white_space_check(token, pos):
 		"""
 		Used to generate good error messages for invalid tokens.

diff --git a/lib/portage/tests/dep/test_use_reduce.py b/lib/portage/tests/dep/test_use_reduce.py
index 4f65567cf..d9ee5a309 100644
--- a/lib/portage/tests/dep/test_use_reduce.py
+++ b/lib/portage/tests/dep/test_use_reduce.py
@@ -9,7 +9,7 @@ class UseReduceTestCase(object):
 	def __init__(self, deparray, uselist=[], masklist=[],
 	             matchall=0, excludeall=[], is_src_uri=False,
 	             eapi='0', opconvert=False, flat=False, expected_result=None,
-	             is_valid_flag=None, token_class=None):
+	             is_valid_flag=None, token_class=None, subset=None):
 		self.deparray = deparray
 		self.uselist = uselist
 		self.masklist = masklist
@@ -21,13 +21,15 @@ class UseReduceTestCase(object):
 		self.flat = flat
 		self.is_valid_flag = is_valid_flag
 		self.token_class = token_class
+		self.subset = subset
 		self.expected_result = expected_result
 
 	def run(self):
 		try:
 			return use_reduce(self.deparray, self.uselist, self.masklist,
 				self.matchall, self.excludeall, self.is_src_uri, self.eapi,
-				self.opconvert, self.flat, self.is_valid_flag, self.token_class)
+				self.opconvert, self.flat, self.is_valid_flag, self.token_class,
+				subset=self.subset)
 		except InvalidDependString as e:
 			raise InvalidDependString("%s: %s" % (e, self.deparray))
 
@@ -50,6 +52,72 @@ class UseReduce(TestCase):
 				uselist=["a", "b", "c", "d"],
 				expected_result=["A", "B"]
 				),
+			UseReduceTestCase(
+				"a? ( A ) b? ( B ) !c? ( C ) !d? ( D )",
+				uselist=["a", "b", "c", "d"],
+				subset=["b"],
+				expected_result=["B"]
+				),
+			UseReduceTestCase(
+				"bar? ( || ( foo bar? ( baz ) ) )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['||', ['foo', 'baz']]
+				),
+			UseReduceTestCase(
+				"bar? ( foo bar? ( baz ) foo )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['foo', 'baz', 'foo']
+				),
+			UseReduceTestCase(
+				"|| ( ( a b ) ( c d ) )",
+				uselist=[],
+				subset=["bar"],
+				expected_result=[]
+				),
+			UseReduceTestCase(
+				"|| ( ( a b ) ( bar? ( c d ) e f ) )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['c', 'd']
+				),
+			UseReduceTestCase(
+				"( a b ) ( c d bar? ( e f baz? ( g h ) ) )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['e', 'f']
+				),
+			UseReduceTestCase(
+				"( a b ) ( c d bar? ( e f baz? ( g h ) ) )",
+				uselist=["bar", "baz"],
+				subset=["bar"],
+				expected_result=['e', 'f', 'g', 'h']
+				),
+			UseReduceTestCase(
+				"( bar? ( a b ) ( bar? ( c d ) ) ) ( e f )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['a', 'b', 'c', 'd']
+				),
+			UseReduceTestCase(
+				"|| ( foo bar? ( baz ) )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=["baz"]
+				),
+			UseReduceTestCase(
+				"|| ( || ( bar? ( a || ( b c || ( d e ) ) ) ) )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['a', '||', ['b', 'c', 'd', 'e']]
+				),
+			UseReduceTestCase(
+				"|| ( || ( bar? ( a || ( ( b c ) ( d e ) ) ) ) )",
+				uselist=["bar"],
+				subset=["bar"],
+				expected_result=['a', '||', [['b', 'c'], ['d', 'e']]]
+				),
 			UseReduceTestCase(
 				"a? ( A ) b? ( B ) !c? ( C ) !d? ( D )",
 				uselist=["a", "b", "c"],

diff --git a/lib/portage/tests/resolver/test_with_test_deps.py b/lib/portage/tests/resolver/test_with_test_deps.py
index 5bfc6a8a2..d88e3cb6e 100644
--- a/lib/portage/tests/resolver/test_with_test_deps.py
+++ b/lib/portage/tests/resolver/test_with_test_deps.py
@@ -21,7 +21,27 @@ class WithTestDepsTestCase(TestCase):
 			},
 			"app-misc/C-0": {
 				"EAPI": "5",
-			}
+			},
+			"app-misc/D-0": {
+				"EAPI": "5",
+				"IUSE": "test",
+				"DEPEND": "test? ( app-misc/E )"
+			},
+			"app-misc/E-0": {
+				"EAPI": "5",
+				"IUSE": "test",
+				"DEPEND": "test? ( app-misc/D )"
+			},
+			"app-misc/F-0": {
+				"EAPI": "5",
+				"IUSE": "+test",
+				"DEPEND": "test? ( app-misc/G )"
+			},
+			"app-misc/G-0": {
+				"EAPI": "5",
+				"IUSE": "+test",
+				"DEPEND": "test? ( app-misc/F )"
+			},
 		}
 
 		test_cases = (
@@ -32,6 +52,23 @@ class WithTestDepsTestCase(TestCase):
 				success = True,
 				options = { "--onlydeps": True, "--with-test-deps": True },
 				mergelist = ["app-misc/B-0"]),
+
+			# Test that --with-test-deps allows circular dependencies.
+			ResolverPlaygroundTestCase(
+				['app-misc/D'],
+				success = True,
+				options = {'--with-test-deps': True},
+				mergelist = [('app-misc/D-0', 'app-misc/E-0')],
+				ambiguous_merge_order=True),
+
+			# Test that --with-test-deps does not allow circular dependencies
+			# when USE=test is explicitly enabled.
+			ResolverPlaygroundTestCase(
+				['app-misc/F'],
+				success = False,
+				options = {'--with-test-deps': True},
+				circular_dependency_solutions = {'app-misc/G-0': {frozenset({('test', False)})}, 'app-misc/F-0': {frozenset({('test', False)})}},
+			)
 		)
 
 		playground = ResolverPlayground(ebuilds=ebuilds, debug=False)


                 reply	other threads:[~2019-12-23 22:47 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1577139795.85f0dd173ab75bcc39c3616b5a3a967bdc88cf73.zmedico@gentoo \
    --to=zmedico@gentoo.org \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox