Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

depgraph: Do not allow slotted deps to be satisfied by wrong slots #1055

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
18 changes: 18 additions & 0 deletions lib/_emerge/depgraph.py
Expand Up @@ -4041,6 +4041,16 @@ def _wrapped_add_pkg_dep_string(
inst_pkg, modified_use=self._pkg_use_enabled(inst_pkg)
)
]
# Do not allow slotted deps to be satisfied by wrong slots.
# Otherwise, slot-operator-dependent packages may rebuild
# before the slotted package they are dependent on.
if child and atom.slot_operator == "=":
thesamesam marked this conversation as resolved.
Show resolved Hide resolved
inst_pkgs = [
inst_pkg
for inst_pkg in inst_pkgs
if inst_pkg.slot == child.slot
and inst_pkg.sub_slot == child.sub_slot
]
if inst_pkgs:
for inst_pkg in inst_pkgs:
if self._pkg_visibility_check(inst_pkg):
Expand Down Expand Up @@ -4160,6 +4170,14 @@ def _wrapped_add_pkg_dep_string(
inst_pkg, modified_use=self._pkg_use_enabled(inst_pkg)
)
]
# Do not allow slotted deps to be satisfied by wrong slots.
if child and atom.slot_operator == "=":
inst_pkgs = [
inst_pkg
for inst_pkg in inst_pkgs
if inst_pkg.slot == child.slot
and inst_pkg.sub_slot == child.sub_slot
]
if inst_pkgs:
for inst_pkg in inst_pkgs:
if self._pkg_visibility_check(inst_pkg):
Expand Down
121 changes: 121 additions & 0 deletions lib/portage/tests/resolver/test_perl_rebuild_bug.py
@@ -0,0 +1,121 @@
# Copyright 2023 Gentoo Authors
# Distributed under the terms of the GNU General Public License v2

from portage.tests import TestCase
from portage.tests.resolver.ResolverPlayground import (
ResolverPlayground,
ResolverPlaygroundTestCase,
)


class PerlRebuildBugTestCase(TestCase):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def testPerlRebuildBug(self):
"""
The infamous Perl rebuild bug.

A non-slotted build-time dependency cycle is created by:
dev-lang/perl -> sys-libs/zlib -> sys-devel/automake -> dev-lang/perl
Everything else depends on this cycle.

Bug in solving for smallest cycle causes slot in RDEPEND of
dev-perl/Locale-gettext to be ignored, so all dependencies other than
perl's >=sys-libs/zlib-1.2.12 are satisfied by already-installed
packages. dev-perl/Locale-gettext and sys-devel/automake become leaves
of the depgraph after satisfied packages are ignored. They become
emerged first. This causes an issue because dev-perl/Locale-gettext is
now built before the slot upgrade of dev-lang/perl.
"""
ebuilds = {
"dev-lang/perl-5.36.0-r2": {
"EAPI": "5",
"DEPEND": ">=sys-libs/zlib-1.2.12",
"RDEPEND": ">=sys-libs/zlib-1.2.12",
"SLOT": "0/5.36",
},
"dev-perl/Locale-gettext-1.70.0-r1": {
"EAPI": "5",
"DEPEND": "dev-lang/perl",
"RDEPEND": "dev-lang/perl:=",
},
"sys-apps/help2man-1.49.3": {
"EAPI": "5",
"DEPEND": "dev-lang/perl dev-perl/Locale-gettext",
"RDEPEND": "dev-lang/perl dev-perl/Locale-gettext",
},
"sys-devel/automake-1.16.5": {
"EAPI": "5",
"DEPEND": "dev-lang/perl",
"RDEPEND": "dev-lang/perl",
},
"sys-libs/zlib-1.2.13-r1": {
"EAPI": "5",
"DEPEND": "sys-devel/automake",
},
}

installed = {
"dev-lang/perl-5.34.0-r3": {
"EAPI": "5",
"DEPEND": "sys-libs/zlib",
"RDEPEND": "sys-libs/zlib",
"SLOT": "0/5.34",
},
"dev-perl/Locale-gettext-1.70.0-r1": {
"EAPI": "5",
"DEPEND": "dev-lang/perl",
"RDEPEND": "dev-lang/perl:0/5.34=",
},
"sys-apps/help2man-1.48.5": {
"EAPI": "5",
"DEPEND": "dev-lang/perl dev-perl/Locale-gettext",
"RDEPEND": "dev-lang/perl dev-perl/Locale-gettext",
},
"sys-devel/automake-1.16.4": {
"EAPI": "5",
"DEPEND": "dev-lang/perl",
"RDEPEND": "dev-lang/perl",
},
"sys-libs/zlib-1.2.11-r4": {
"EAPI": "5",
"DEPEND": "sys-devel/automake",
},
}

world = ["sys-apps/help2man"]

test_cases = (
ResolverPlaygroundTestCase(
["@world"],
options={"--deep": True, "--update": True, "--verbose": True},
success=True,
ambiguous_merge_order=True,
merge_order_assertions=(
(
"dev-lang/perl-5.36.0-r2",
"dev-perl/Locale-gettext-1.70.0-r1",
),
),
mergelist=[
"sys-devel/automake-1.16.5",
"sys-libs/zlib-1.2.13-r1",
"dev-lang/perl-5.36.0-r2",
"dev-perl/Locale-gettext-1.70.0-r1",
"sys-apps/help2man-1.49.3",
],
),
)

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()