commit: 12f6056da88041f82a9c9dfc23ee0eab39077782
Author: Zac Medico gentoo org>
AuthorDate: Sun Feb 11 04:12:45 2024 +
Commit: Zac Medico gentoo org>
CommitDate: Sun Feb 11 04:36:22 2024 +
URL:https://gitweb.gentoo.org/proj/portage.git/commit/?id=12f6056d
_overlap_dnf: deduplicate any-of blocks
Duplicate any-of blocks are eliminated since DNF expansion
of duplicates is nonsensical.
Bug: https://bugs.gentoo.org/891137
Signed-off-by: Zac Medico gentoo.org>
lib/portage/dep/dep_check.py | 17 ++---
lib/portage/tests/dep/test_overlap_dnf.py | 31 +--
2 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/lib/portage/dep/dep_check.py b/lib/portage/dep/dep_check.py
index 5ca0995a87..c361ee59e2 100644
--- a/lib/portage/dep/dep_check.py
+++ b/lib/portage/dep/dep_check.py
@@ -1,4 +1,4 @@
-# Copyright 2010-2020 Gentoo Authors
+# Copyright 2010-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
__all__ = ["dep_check", "dep_eval", "dep_wordreduce", "dep_zapdeps"]
@@ -963,7 +963,8 @@ def _overlap_dnf(dep_struct):
order to minimize the number of packages chosen to satisfy cases like
"|| ( foo bar ) || ( bar baz )" as in bug #632026. Non-overlapping
groups are excluded from the conversion, since DNF leads to exponential
-explosion of the formula.
+explosion of the formula. Duplicate || groups are eliminated since
+DNF expansion of duplicates is nonsensical (bug #891137).
When dep_struct does not contain any overlapping groups, no DNF
conversion will be performed, and dep_struct will be returned as-is.
@@ -1021,7 +1022,17 @@ def _overlap_dnf(dep_struct):
if len(disjunctions) > 1:
overlap = True
# convert overlapping disjunctions to DNF
-result.extend(_dnf_convert(sorted(disjunctions.values(),
key=order_key)))
+dedup_set = set()
+unique_disjunctions = []
+for x in sorted(disjunctions.values(), key=order_key):
+dep_repr = portage.dep.paren_enclose(x, opconvert=True)
+if dep_repr not in dedup_set:
+dedup_set.add(dep_repr)
+unique_disjunctions.append(x)
+if len(unique_disjunctions) > 1:
+result.extend(_dnf_convert(unique_disjunctions))
+else:
+result.extend(unique_disjunctions)
else:
# pass through non-overlapping disjunctions
result.append(disjunctions.popitem()[1])
diff --git a/lib/portage/tests/dep/test_overlap_dnf.py
b/lib/portage/tests/dep/test_overlap_dnf.py
index 77352021a9..7fd1cfe7dd 100644
--- a/lib/portage/tests/dep/test_overlap_dnf.py
+++ b/lib/portage/tests/dep/test_overlap_dnf.py
@@ -1,4 +1,4 @@
-# Copyright 2017-2023 Gentoo Authors
+# Copyright 2017-2024 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
from portage.tests import TestCase
@@ -51,22 +51,41 @@ class OverlapDNFTestCase(TestCase):
class DuplicateOverlapDNFTestCase(TestCase):
def testDuplicateOverlapDNF(self):
"""
-Demonstrate unnecessary DNF expansion for duplicate
-any-of blocks as in bug 891137.
+Demonstrate deduplication of any-of blocks, preventing unnecessary
+DNF expansion for duplicate any-of blocks as in bug 891137.
"""
test_cases = (
+("|| ( cat/A cat/B ) || ( cat/A cat/B )", [["||", "cat/A",
"cat/B"]]),
(
-"|| ( cat/A cat/B ) || ( cat/A cat/B )",
+"|| ( cat/A cat/B ) cat/E || ( cat/C cat/D ) || ( cat/A cat/B
)",
+["cat/E", ["||", "cat/A", "cat/B"], ["||", "cat/C", "cat/D"]],
+),
+(
+"|| ( cat/A cat/B ) cat/D || ( cat/B cat/C ) || ( cat/A cat/B
)",
[
+"cat/D",
[
"||",
-["cat/A", "cat/A"],
["cat/A", "cat/B"],
-["cat/B", "cat/A"],
+["cat/A", "cat/C"],
["cat/B", "cat/B"],
+["cat/B", "cat/C"],
],
],
),
+(
+"|| ( cat/A cat/B ) || ( cat/C cat/D ) || ( ( cat/B cat/E )
cat/F ) || ( cat/A cat/B )",
+[
+[
+"||",
+["cat/A", "cat/B", "cat/E"],
+["cat/A", "cat/F"],
+["cat/B", "cat/B", "cat/E"],
+["cat/B", "cat/F"],
+],
+["||", "cat/C", "cat/D"],
+],
+),
)
for dep_str, result in test_cases: