commit: 1d856747ada48f8d32c033091b1156cc655efed3
Author: Zac Medico <zmedico <AT> gentoo <DOT> org>
AuthorDate: Wed Dec 6 06:05:46 2023 +0000
Commit: Zac Medico <zmedico <AT> gentoo <DOT> org>
CommitDate: Wed Dec 6 20:23:14 2023 +0000
URL: https://gitweb.gentoo.org/proj/portage.git/commit/?id=1d856747
DepPriority{Normal,Satisfied}Range: weaken _ignore_runtime for cross root
When the dependency parent is for a cross root (ROOT != /) package,
weaken _ignore_runtime in order to tolerate runtime cycles that are
less problematic for cross root packages.
The included test case fails with this error without the fix:
* Error: circular dependencies:
(dev-libs/gmp-6.3.0:0/10.4::test_repo, binary scheduled for merge to
'/tmp/tmp25nwdjn7/cross_root/') depends on
(sys-devel/gcc-13.2.1_p20230826:0/0::test_repo, binary scheduled for merge to
'/tmp/tmp25nwdjn7/cross_root/') (runtime)
(dev-libs/gmp-6.3.0:0/10.4::test_repo, binary scheduled for merge to
'/tmp/tmp25nwdjn7/cross_root/') (runtime_slot_op)
It might be possible to break this cycle
by applying the following change:
- dev-libs/gmp-6.3.0 (Change USE: -cxx)
Bug: https://bugs.gentoo.org/919174
Signed-off-by: Zac Medico <zmedico <AT> gentoo.org>
lib/_emerge/DepPriority.py | 4 +-
lib/_emerge/DepPriorityNormalRange.py | 4 +-
lib/_emerge/DepPrioritySatisfiedRange.py | 1 +
lib/_emerge/UnmergeDepPriority.py | 3 +-
lib/_emerge/depgraph.py | 46 ++++--
lib/portage/tests/resolver/meson.build | 1 +
.../tests/resolver/test_cross_dep_priority.py | 164 +++++++++++++++++++++
7 files changed, 208 insertions(+), 15 deletions(-)
diff --git a/lib/_emerge/DepPriority.py b/lib/_emerge/DepPriority.py
index 99d38477e2..8d282b937a 100644
--- a/lib/_emerge/DepPriority.py
+++ b/lib/_emerge/DepPriority.py
@@ -1,11 +1,11 @@
-# Copyright 1999-2013 Gentoo Foundation
+# Copyright 1999-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
from _emerge.AbstractDepPriority import AbstractDepPriority
class DepPriority(AbstractDepPriority):
- __slots__ = ("satisfied", "optional", "ignored")
+ __slots__ = ("cross", "ignored", "optional", "satisfied")
def __int__(self):
"""
diff --git a/lib/_emerge/DepPriorityNormalRange.py
b/lib/_emerge/DepPriorityNormalRange.py
index d7e4381b47..cb0e6c26b1 100644
--- a/lib/_emerge/DepPriorityNormalRange.py
+++ b/lib/_emerge/DepPriorityNormalRange.py
@@ -1,4 +1,4 @@
-# Copyright 1999-2011 Gentoo Foundation
+# Copyright 1999-2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2
from _emerge.DepPriority import DepPriority
@@ -41,7 +41,7 @@ class DepPriorityNormalRange:
# to adjust this appropriately. But only build time dependencies
# are optional right now, so it's not an issue as-is.
return bool(
- not priority.runtime_slot_op
+ not (priority.runtime_slot_op and not priority.cross)
and (priority.optional or not priority.buildtime)
)
diff --git a/lib/_emerge/DepPrioritySatisfiedRange.py
b/lib/_emerge/DepPrioritySatisfiedRange.py
index 0d42e7613d..b3bc90c2ff 100644
--- a/lib/_emerge/DepPrioritySatisfiedRange.py
+++ b/lib/_emerge/DepPrioritySatisfiedRange.py
@@ -96,6 +96,7 @@ class DepPrioritySatisfiedRange:
(
(not priority.runtime_slot_op)
or (priority.satisfied and priority.runtime_slot_op)
+ or priority.cross
)
and (priority.satisfied or priority.optional or not
priority.buildtime)
)
diff --git a/lib/_emerge/UnmergeDepPriority.py
b/lib/_emerge/UnmergeDepPriority.py
index ff81eff46f..d818bad1b8 100644
--- a/lib/_emerge/UnmergeDepPriority.py
+++ b/lib/_emerge/UnmergeDepPriority.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
from _emerge.AbstractDepPriority import AbstractDepPriority
@@ -6,6 +6,7 @@ from _emerge.AbstractDepPriority import AbstractDepPriority
class UnmergeDepPriority(AbstractDepPriority):
__slots__ = (
+ "cross",
"ignored",
"optional",
"satisfied",
diff --git a/lib/_emerge/depgraph.py b/lib/_emerge/depgraph.py
index 9b09701021..59c78c7354 100644
--- a/lib/_emerge/depgraph.py
+++ b/lib/_emerge/depgraph.py
@@ -3630,7 +3630,7 @@ class depgraph:
blocker=False,
depth=depth,
parent=pkg,
- priority=self._priority(runtime=True),
+ priority=self._priority(cross=self._cross(pkg.root),
runtime=True),
root=pkg.root,
)
if not self._add_dep(dep, allow_unsatisfied=allow_unsatisfied):
@@ -3968,17 +3968,26 @@ class depgraph:
# _dep_disjunctive_stack first, so that choices for build-time
# deps influence choices for run-time deps (bug 639346).
deps = (
- (myroot, edepend["RDEPEND"], self._priority(runtime=True)),
+ (
+ myroot,
+ edepend["RDEPEND"],
+ self._priority(cross=self._cross(pkg.root), runtime=True),
+ ),
(
self._frozen_config._running_root.root,
edepend["IDEPEND"],
- self._priority(runtime=True),
+ self._priority(cross=self._cross(pkg.root), runtime=True),
+ ),
+ (
+ myroot,
+ edepend["PDEPEND"],
+ self._priority(cross=self._cross(pkg.root), runtime_post=True),
),
- (myroot, edepend["PDEPEND"], self._priority(runtime_post=True)),
(
depend_root,
edepend["DEPEND"],
self._priority(
+ cross=self._cross(pkg.root),
buildtime=True,
optional=(pkg.built or ignore_depend_deps),
ignored=ignore_depend_deps,
@@ -3988,6 +3997,7 @@ class depgraph:
self._frozen_config._running_root.root,
edepend["BDEPEND"],
self._priority(
+ cross=self._cross(pkg.root),
buildtime=True,
optional=(pkg.built or ignore_bdepend_deps),
ignored=ignore_bdepend_deps,
@@ -4036,7 +4046,9 @@ class depgraph:
self._queue_disjunctive_deps(
pkg,
dep_root,
- self._priority(runtime_post=True),
+ self._priority(
+ cross=self._cross(pkg.root),
runtime_post=True
+ ),
test_deps,
)
)
@@ -4044,7 +4056,9 @@ class depgraph:
if test_deps and not self._add_pkg_dep_string(
pkg,
dep_root,
- self._priority(runtime_post=True),
+ self._priority(
+ cross=self._cross(pkg.root), runtime_post=True
+ ),
test_deps,
allow_unsatisfied,
):
@@ -4359,7 +4373,10 @@ class depgraph:
return 0
for atom, child in self._minimize_children(
- pkg, self._priority(runtime=True), root_config, atoms
+ pkg,
+ self._priority(cross=self._cross(pkg.root), runtime=True),
+ root_config,
+ atoms,
):
# If this was a specially generated virtual atom
# from dep_check, map it back to the original, in
@@ -4369,7 +4386,7 @@ class depgraph:
atom = getattr(atom, "_orig_atom", atom)
# This is a GLEP 37 virtual, so its deps are all runtime.
- mypriority = self._priority(runtime=True)
+ mypriority = self._priority(cross=self._cross(pkg.root),
runtime=True)
if not atom.blocker:
inst_pkgs = [
inst_pkg
@@ -4616,6 +4633,13 @@ class depgraph:
priority_constructor = DepPriority
return priority_constructor(**kwargs)
+ def _cross(self, eroot):
+ """
+ Returns True if the ROOT for the given EROOT is not /,
+ or EROOT is cross-prefix.
+ """
+ return eroot != self._frozen_config._running_root.root
+
def _dep_expand(self, root_config, atom_without_category):
"""
@param root_config: a root config instance
@@ -5788,7 +5812,9 @@ class depgraph:
node_priority = priority.copy()
else:
# virtuals only have runtime deps
- node_priority = self._priority(runtime=True)
+ node_priority = self._priority(
+ cross=self._cross(node_parent.root), runtime=True
+ )
k = Dependency(
atom=parent_atom,
@@ -5874,7 +5900,7 @@ class depgraph:
pkg._metadata.get("RDEPEND", ""),
myuse=self._pkg_use_enabled(pkg),
parent=pkg,
- priority=self._priority(runtime=True),
+ priority=self._priority(cross=self._cross(pkg.root),
runtime=True),
)
except InvalidDependString as e:
if not pkg.installed:
diff --git a/lib/portage/tests/resolver/meson.build
b/lib/portage/tests/resolver/meson.build
index 770027ac47..77c65a511e 100644
--- a/lib/portage/tests/resolver/meson.build
+++ b/lib/portage/tests/resolver/meson.build
@@ -21,6 +21,7 @@ py.install_sources(
'test_circular_dependencies.py',
'test_complete_graph.py',
'test_complete_if_new_subslot_without_revbump.py',
+ 'test_cross_dep_priority.py',
'test_depclean.py',
'test_depclean_order.py',
'test_depclean_slot_unavailable.py',
diff --git a/lib/portage/tests/resolver/test_cross_dep_priority.py
b/lib/portage/tests/resolver/test_cross_dep_priority.py
new file mode 100644
index 0000000000..10f2eb36e2
--- /dev/null
+++ b/lib/portage/tests/resolver/test_cross_dep_priority.py
@@ -0,0 +1,164 @@
+# Copyright 2023 Gentoo Authors
+# Distributed under the terms of the GNU General Public License v2
+
+import shutil
+import subprocess
+import os
+
+import portage
+from portage.tests import TestCase
+from portage.tests.resolver.ResolverPlayground import (
+ ResolverPlayground,
+ ResolverPlaygroundTestCase,
+)
+
+
+class CrossDepPriorityTestCase(TestCase):
+ def testCrossDepPriority(self):
+ """
+ Test bug 919174, where cross-root merge to an empty root
+ failed due to circular dependencies.
+ """
+ ebuilds = {
+ "dev-lang/python-3.11.6": {
+ "EAPI": "8",
+ "DEPEND": "sys-apps/util-linux:=",
+ "RDEPEND": "sys-apps/util-linux:=",
+ },
+ "sys-apps/util-linux-2.38.1-r2": {
+ "EAPI": "8",
+ "DEPEND": "selinux? ( >=sys-libs/libselinux-2.2.2-r4 )",
+ "RDEPEND": "selinux? ( >=sys-libs/libselinux-2.2.2-r4 )",
+ "IUSE": "selinux",
+ },
+ "sys-libs/libselinux-3.5-r1": {
+ "EAPI": "8",
+ "DEPEND": "python? ( dev-lang/python )",
+ "RDEPEND": "python? ( dev-lang/python )",
+ "IUSE": "python",
+ },
+ "dev-libs/gmp-6.3.0": {
+ "EAPI": "8",
+ "SLOT": "0/10.4",
+ "DEPEND": "cxx? ( sys-devel/gcc )",
+ "RDEPEND": "cxx? ( sys-devel/gcc )",
+ "IUSE": "cxx",
+ },
+ "sys-devel/gcc-13.2.1_p20230826": {
+ "EAPI": "8",
+ "DEPEND": ">=dev-libs/gmp-4.3.2:0=",
+ "RDEPEND": ">=dev-libs/gmp-4.3.2:0=",
+ },
+ }
+
+ installed = {
+ "dev-lang/python-3.11.6": {
+ "EAPI": "8",
+ "KEYWORDS": "x86",
+ "DEPEND": "sys-apps/util-linux:0/0=",
+ "RDEPEND": "sys-apps/util-linux:0/0=",
+ },
+ "sys-apps/util-linux-2.38.1-r2": {
+ "EAPI": "8",
+ "KEYWORDS": "x86",
+ "DEPEND": "selinux? ( >=sys-libs/libselinux-2.2.2-r4 )",
+ "RDEPEND": "selinux? ( >=sys-libs/libselinux-2.2.2-r4 )",
+ "IUSE": "selinux",
+ "USE": "selinux",
+ },
+ "sys-libs/libselinux-3.5-r1": {
+ "EAPI": "8",
+ "KEYWORDS": "x86",
+ "DEPEND": "python? ( dev-lang/python )",
+ "RDEPEND": "python? ( dev-lang/python )",
+ "IUSE": "python",
+ "USE": "python",
+ },
+ "dev-libs/gmp-6.3.0": {
+ "EAPI": "8",
+ "KEYWORDS": "x86",
+ "SLOT": "0/10.4",
+ "DEPEND": "cxx? ( sys-devel/gcc )",
+ "RDEPEND": "cxx? ( sys-devel/gcc )",
+ "IUSE": "cxx",
+ "USE": "cxx",
+ },
+ "sys-devel/gcc-13.2.1_p20230826": {
+ "EAPI": "8",
+ "KEYWORDS": "x86",
+ "DEPEND": ">=dev-libs/gmp-4.3.2:0/10.4=",
+ "RDEPEND": ">=dev-libs/gmp-4.3.2:0/10.4=",
+ },
+ }
+
+ world = [
+ "sys-apps/util-linux",
+ "sys-devel/gcc",
+ ]
+
+ user_config = {
+ "make.conf": ('USE="cxx python selinux"',),
+ }
+
+ test_cases = (
+ ResolverPlaygroundTestCase(
+ ["@world"],
+ options={"--emptytree": True},
+ success=True,
+ mergelist=[
+ "dev-libs/gmp-6.3.0",
+ "sys-devel/gcc-13.2.1_p20230826",
+ "sys-apps/util-linux-2.38.1-r2",
+ "dev-lang/python-3.11.6",
+ "sys-libs/libselinux-3.5-r1",
+ ],
+ ),
+ )
+
+ playground = ResolverPlayground(
+ ebuilds=ebuilds,
+ installed=installed,
+ world=world,
+ user_config=user_config,
+ )
+ try:
+ for test_case in test_cases:
+ playground.run_TestCase(test_case)
+ self.assertEqual(test_case.test_success, True,
test_case.fail_msg)
+
+ # Since ResolverPlayground does not internally support
+ # cross-root, test with emerge.
+ cross_root = os.path.join(playground.settings["EPREFIX"],
"cross_root")
+ world_file = os.path.join(
+ cross_root,
+ playground.settings["EPREFIX"].lstrip(os.sep),
+ portage.const.WORLD_FILE,
+ )
+ os.makedirs(os.path.dirname(world_file))
+ shutil.copy(
+ os.path.join(playground.settings["EPREFIX"],
portage.const.WORLD_FILE),
+ world_file,
+ )
+ result = subprocess.run(
+ [
+ "emerge",
+ f"--root={cross_root}",
+ "--pretend",
+ "--verbose",
+ "--usepkgonly",
+ "--quickpkg-direct=y",
+ "@world",
+ ],
+ env=playground.settings.environ(),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT,
+ )
+ output = result.stdout.decode(errors="replace")
+ try:
+ self.assertTrue("5 packages (5 new, 5 binaries)" in output)
+ self.assertEqual(result.returncode, os.EX_OK)
+ except Exception:
+ print(output)
+ raise
+ finally:
+ playground.cleanup()