From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from lists.gentoo.org (pigeon.gentoo.org [208.92.234.80]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by finch.gentoo.org (Postfix) with ESMTPS id 2004C158015 for ; Tue, 26 Dec 2023 21:05:48 +0000 (UTC) Received: from pigeon.gentoo.org (localhost [127.0.0.1]) by pigeon.gentoo.org (Postfix) with SMTP id 615062BC014; Tue, 26 Dec 2023 21:05:47 +0000 (UTC) Received: from smtp.gentoo.org (woodpecker.gentoo.org [IPv6:2001:470:ea4a:1:5054:ff:fec7:86e4]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by pigeon.gentoo.org (Postfix) with ESMTPS id 4071C2BC014 for ; Tue, 26 Dec 2023 21:05:47 +0000 (UTC) Received: from oystercatcher.gentoo.org (oystercatcher.gentoo.org [148.251.78.52]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by smtp.gentoo.org (Postfix) with ESMTPS id 5E54F33FEF1 for ; Tue, 26 Dec 2023 21:05:46 +0000 (UTC) Received: from localhost.localdomain (localhost [IPv6:::1]) by oystercatcher.gentoo.org (Postfix) with ESMTP id EF574ED2 for ; Tue, 26 Dec 2023 21:05:44 +0000 (UTC) From: "Zac Medico" To: gentoo-commits@lists.gentoo.org Content-Transfer-Encoding: 8bit Content-type: text/plain; charset=UTF-8 Reply-To: gentoo-dev@lists.gentoo.org, "Zac Medico" Message-ID: <1703624665.64b16b76611e14ff0b38b762486f073039f21a05.zmedico@gentoo> Subject: [gentoo-commits] proj/portage:master commit in: lib/portage/tests/resolver/, lib/_emerge/ X-VCS-Repository: proj/portage X-VCS-Files: lib/_emerge/AbstractDepPriority.py lib/_emerge/UnmergeDepPriority.py lib/_emerge/actions.py lib/_emerge/depgraph.py lib/portage/tests/resolver/test_depclean_order.py X-VCS-Directories: lib/_emerge/ lib/portage/tests/resolver/ X-VCS-Committer: zmedico X-VCS-Committer-Name: Zac Medico X-VCS-Revision: 64b16b76611e14ff0b38b762486f073039f21a05 X-VCS-Branch: master Date: Tue, 26 Dec 2023 21:05:44 +0000 (UTC) Precedence: bulk List-Post: List-Help: List-Unsubscribe: List-Subscribe: List-Id: Gentoo Linux mail X-BeenThere: gentoo-commits@lists.gentoo.org X-Auto-Response-Suppress: DR, RN, NRN, OOF, AutoReply X-Archives-Salt: 0b4b5d9d-354c-4c39-a8b8-32632398b843 X-Archives-Hash: 01d2d6b714fff47978930db56a377abf commit: 64b16b76611e14ff0b38b762486f073039f21a05 Author: Zac Medico gentoo org> AuthorDate: Mon Dec 25 02:53:57 2023 +0000 Commit: Zac Medico gentoo org> CommitDate: Tue Dec 26 21:04:25 2023 +0000 URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=64b16b76 depclean: Strengthen IDEPEND in unmerge order Increase priority of IDEPEND so that it is stronger than RDEPEND in unmerge order calculations. This causes IDEPEND to be unmerged afterwards when packages are involved in RDEPEND cycles. Bug: https://bugs.gentoo.org/916135 Signed-off-by: Zac Medico gentoo.org> lib/_emerge/AbstractDepPriority.py | 3 +- lib/_emerge/UnmergeDepPriority.py | 35 +++++++------ lib/_emerge/actions.py | 5 +- lib/_emerge/depgraph.py | 4 +- lib/portage/tests/resolver/test_depclean_order.py | 63 +++++++++++++++++++++++ 5 files changed, 92 insertions(+), 18 deletions(-) diff --git a/lib/_emerge/AbstractDepPriority.py b/lib/_emerge/AbstractDepPriority.py index a9616c1094..3af262cd79 100644 --- a/lib/_emerge/AbstractDepPriority.py +++ b/lib/_emerge/AbstractDepPriority.py @@ -1,4 +1,4 @@ -# Copyright 1999-2013 Gentoo Foundation +# Copyright 1999-2023 Gentoo Authors # Distributed under the terms of the GNU General Public License v2 import copy @@ -9,6 +9,7 @@ class AbstractDepPriority(SlotObject): __slots__ = ( "buildtime", "buildtime_slot_op", + "installtime", "runtime", "runtime_post", "runtime_slot_op", diff --git a/lib/_emerge/UnmergeDepPriority.py b/lib/_emerge/UnmergeDepPriority.py index d818bad1b8..b14f8b84eb 100644 --- a/lib/_emerge/UnmergeDepPriority.py +++ b/lib/_emerge/UnmergeDepPriority.py @@ -12,18 +12,19 @@ class UnmergeDepPriority(AbstractDepPriority): "satisfied", ) """ - Combination of properties Priority Category - - runtime_slot_op 0 HARD - runtime -1 HARD - runtime_post -2 HARD - buildtime -3 SOFT - (none of the above) -3 SOFT - """ + Combination of properties Priority Category + + installtime 0 HARD + runtime_slot_op -1 HARD + runtime -2 HARD + runtime_post -3 HARD + buildtime -4 SOFT + (none of the above) -4 SOFT + """ MAX = 0 - SOFT = -3 - MIN = -3 + SOFT = -4 + MIN = -4 def __init__(self, **kwargs): AbstractDepPriority.__init__(self, **kwargs) @@ -31,19 +32,23 @@ class UnmergeDepPriority(AbstractDepPriority): self.optional = True def __int__(self): - if self.runtime_slot_op: + if self.installtime: return 0 - if self.runtime: + if self.runtime_slot_op: return -1 - if self.runtime_post: + if self.runtime: return -2 - if self.buildtime: + if self.runtime_post: return -3 - return -3 + if self.buildtime: + return -4 + return -4 def __str__(self): if self.ignored: return "ignored" + if self.installtime: + return "install time" if self.runtime_slot_op: return "hard slot op" myvalue = self.__int__() diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py index 13bb75931c..20f3978f77 100644 --- a/lib/_emerge/actions.py +++ b/lib/_emerge/actions.py @@ -1568,11 +1568,12 @@ def _calc_depclean(settings, trees, ldpath_mtimes, myopts, action, args_set, spi graph = digraph() del cleanlist[:] + installtime = UnmergeDepPriority(installtime=True, runtime=True) runtime = UnmergeDepPriority(runtime=True) runtime_post = UnmergeDepPriority(runtime_post=True) buildtime = UnmergeDepPriority(buildtime=True) priority_map = { - "IDEPEND": runtime, + "IDEPEND": installtime, "RDEPEND": runtime, "PDEPEND": runtime_post, "BDEPEND": buildtime, @@ -1683,6 +1684,8 @@ def _calc_depclean(settings, trees, ldpath_mtimes, myopts, action, args_set, spi break if not nodes: raise AssertionError("no root nodes") + # Sort nodes for deterministic results. + nodes.sort(reverse=True) if ignore_priority is not None: # Some deps have been dropped due to circular dependencies, # so only pop one node in order to minimize the number that diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py index e92c6962ac..6ee4471bbe 100644 --- a/lib/_emerge/depgraph.py +++ b/lib/_emerge/depgraph.py @@ -4007,7 +4007,9 @@ class depgraph: ( self._frozen_config._running_root.root, edepend["IDEPEND"], - self._priority(cross=self._cross(pkg.root), runtime=True), + self._priority( + cross=self._cross(pkg.root), installtime=True, runtime=True + ), ), ( myroot, diff --git a/lib/portage/tests/resolver/test_depclean_order.py b/lib/portage/tests/resolver/test_depclean_order.py index 23b5e755c3..36d60d44e9 100644 --- a/lib/portage/tests/resolver/test_depclean_order.py +++ b/lib/portage/tests/resolver/test_depclean_order.py @@ -109,3 +109,66 @@ class SimpleDepcleanTestCase(TestCase): self.assertEqual(test_case.test_success, True, test_case.fail_msg) finally: playground.cleanup() + + def testCircularDepclean(self): + """ + Test for bug 916135, where an indirect circular dependency caused + the unmerge order to fail to account for IDEPEND. + """ + + ebuilds = { + "dev-util/A-1": {}, + "dev-libs/B-1": { + "EAPI": "8", + "SLOT": "1", + "IDEPEND": "dev-util/A", + "RDEPEND": "dev-libs/B:=", + }, + "dev-libs/B-2": { + "EAPI": "8", + "SLOT": "2", + "IDEPEND": "dev-util/A", + "RDEPEND": "dev-libs/B:=", + }, + "dev-libs/C-1": {}, + } + + installed = { + "dev-util/A-1": {}, + "dev-libs/B-1": { + "EAPI": "8", + "SLOT": "1", + "IDEPEND": "dev-util/A", + "RDEPEND": "dev-libs/B:2/2=", + }, + "dev-libs/B-2": { + "EAPI": "8", + "SLOT": "2", + "IDEPEND": "dev-util/A", + "RDEPEND": "dev-libs/B:1/1=", + }, + "dev-libs/C-1": {}, + } + + world = ("dev-libs/C",) + + test_cases = ( + # Remove dev-libs/B first because it IDEPENDs on dev-util/A + ResolverPlaygroundTestCase( + [], + options={"--depclean": True}, + success=True, + ordered=True, + cleanlist=["dev-libs/B-2", "dev-libs/B-1", "dev-util/A-1"], + ), + ) + + playground = ResolverPlayground( + ebuilds=ebuilds, installed=installed, world=world + ) + try: + for test_case in test_cases: + playground.run_TestCase(test_case) + self.assertEqual(test_case.test_success, True, test_case.fail_msg) + finally: + playground.cleanup()