Skip to content

Commit 59cfd9b

Browse files
committed
PerlCheck: check versioned virtual perl dependencies
Resolves: #593 Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
1 parent 2b9b8e7 commit 59cfd9b

File tree

2 files changed

+90
-12
lines changed

2 files changed

+90
-12
lines changed

src/pkgcheck/checks/perl.py

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,11 @@
22
import re
33
import subprocess
44

5+
from pkgcore.ebuild.atom import atom as atom_cls
56
from pkgcore.restrictions import packages, values
7+
from pkgcore.package.errors import MetadataException
68
from snakeoil.osutils import pjoin
9+
from snakeoil.sequences import iflatten_instance
710

811
from .. import const, results, sources
912
from . import OptionalCheck, SkipCheck
@@ -22,6 +25,24 @@ def desc(self):
2225
return f"DIST_VERSION={self.dist_version} normalizes to {self.normalized}"
2326

2427

28+
class MissingVersionedVirtualPerlDependency(results.VersionResult, results.Warning):
29+
"""Missing version restriction for virtual perl dependency.
30+
31+
The virtuals ``virtual/perl-*`` stand for packages that have releases both
32+
as part of ``dev-lang/perl`` and standalone in ``perl-core/*``. Apart from
33+
rare special cases, if you require "any" version of such a virtual, this
34+
will always be fulfilled by ``dev-lang/perl``.
35+
"""
36+
37+
def __init__(self, atom, **kwargs):
38+
super().__init__(**kwargs)
39+
self.atom = atom
40+
41+
@property
42+
def desc(self):
43+
return f"missing version restriction for virtual perl: {self.atom!r}"
44+
45+
2546
class _PerlException(Exception):
2647
"""Generic error during perl script initialization."""
2748

@@ -70,12 +91,13 @@ def __del__(self):
7091
class PerlCheck(OptionalCheck):
7192
"""Perl ebuild related checks."""
7293

73-
_restricted_source = (
74-
sources.RestrictionRepoSource,
75-
(packages.PackageRestriction("inherited", values.ContainmentMatch2("perl-module")),),
94+
_source = sources.EbuildFileRepoSource
95+
known_results = frozenset(
96+
{
97+
MismatchedPerlVersion,
98+
MissingVersionedVirtualPerlDependency,
99+
}
76100
)
77-
_source = (sources.EbuildFileRepoSource, (), (("source", _restricted_source),))
78-
known_results = frozenset([MismatchedPerlVersion])
79101

80102
def __init__(self, *args):
81103
super().__init__(*args)
@@ -86,12 +108,32 @@ def __init__(self, *args):
86108
# it easier to disable this check if required perl deps are missing.
87109
try:
88110
self.perl = _PerlConnection(self.options)
89-
except _PerlException as e:
90-
raise SkipCheck(self, str(e))
111+
except _PerlException as exc:
112+
raise SkipCheck(self, str(exc))
91113

92114
def feed(self, pkg):
93-
if mo := self.dist_version_re.search("".join(pkg.lines)):
94-
dist_version = mo.group("dist_version")
95-
normalized = self.perl.normalize(dist_version)
96-
if normalized != pkg.version:
97-
yield MismatchedPerlVersion(dist_version, normalized, pkg=pkg)
115+
if "perl-module" in pkg.inherited:
116+
if mo := self.dist_version_re.search("".join(pkg.lines)):
117+
dist_version = mo.group("dist_version")
118+
normalized = self.perl.normalize(dist_version)
119+
if normalized != pkg.version:
120+
yield MismatchedPerlVersion(dist_version, normalized, pkg=pkg)
121+
122+
missing_virtual_perl = set()
123+
for attr in (x.lower() for x in pkg.eapi.dep_keys):
124+
try:
125+
deps = getattr(pkg, attr)
126+
except MetadataException:
127+
continue
128+
for atom in iflatten_instance(deps, (atom_cls,)):
129+
if (
130+
not atom.op
131+
and atom.key.startswith("virtual/perl-")
132+
and pkg.key != "dev-lang/perl"
133+
and pkg.category != "perl-core"
134+
and not pkg.key.startswith("virtual/perl-")
135+
):
136+
missing_virtual_perl.add(str(atom))
137+
138+
for atom in sorted(missing_virtual_perl):
139+
yield MissingVersionedVirtualPerlDependency(str(atom), pkg=pkg)

tests/checks/test_perl.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,39 @@ def test_no_perl_deps(self):
7373
for verbosity in (0, 1):
7474
with pytest.raises(SkipCheck, match="failed to run perl script"):
7575
self.mk_check(verbosity=verbosity)
76+
77+
@pytest.mark.parametrize(
78+
"cpv", ("dev-lang/perl-0", "perl-core/pkgcheck-0", "virtual/perl-pkgcheck-0")
79+
)
80+
def test_missing_op_virtual_good_packages(self, cpv):
81+
pkg = misc.FakePkg(cpv, data={"RDEPEND": "virtual/perl-pkgcheck"})
82+
self.assertNoReport(self.mk_check(), pkg)
83+
84+
@pytest.mark.parametrize(
85+
"rdepend",
86+
(
87+
">=virtual/perl-pkgcheck-0",
88+
"<virtual/perl-pkgcheck-0",
89+
"virtual/pkgcheck",
90+
"dev-cpp/perl-pkgcheck-0",
91+
),
92+
)
93+
def test_missing_op_virtual_good_deps(self, rdepend):
94+
pkg = misc.FakePkg("dev-cpp/perl-pkgcheck-0", data={"RDEPEND": rdepend})
95+
self.assertNoReport(self.mk_check(), pkg)
96+
97+
def test_missing_op_virtual_bad(self):
98+
pkg = misc.FakePkg(
99+
"dev-cpp/perl-pkgcheck-0",
100+
data={
101+
"RDEPEND": "virtual/perl-pkgcore virtual/perl-pkgcheck",
102+
"DEPEND": "virtual/perl-pkgcheck",
103+
"BDEPEND": ">=virtual/perl-pkgcore-0",
104+
},
105+
)
106+
reports = self.assertReports(self.mk_check(), pkg)
107+
assert len(reports) == 2
108+
for r in reports:
109+
assert isinstance(r, perl.MissingVersionedVirtualPerlDependency)
110+
assert reports[0].atom == "virtual/perl-pkgcheck"
111+
assert reports[1].atom == "virtual/perl-pkgcore"

0 commit comments

Comments
 (0)