commit:     12f6056da88041f82a9c9dfc23ee0eab39077782
Author:     Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Sun Feb 11 04:12:45 2024 +0000
Commit:     Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Sun Feb 11 04:36:22 2024 +0000
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 <zmedico <AT> 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:

Reply via email to