Skip to content

Commit

Permalink
PerlCheck: check versioned virtual perl dependencies
Browse files Browse the repository at this point in the history
Resolves: #593
Signed-off-by: Arthur Zamarin <arthurzam@gentoo.org>
  • Loading branch information
arthurzam committed Jul 15, 2023
1 parent 2b9b8e7 commit 59cfd9b
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 12 deletions.
66 changes: 54 additions & 12 deletions src/pkgcheck/checks/perl.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
import re
import subprocess

from pkgcore.ebuild.atom import atom as atom_cls
from pkgcore.restrictions import packages, values
from pkgcore.package.errors import MetadataException
from snakeoil.osutils import pjoin
from snakeoil.sequences import iflatten_instance

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


class MissingVersionedVirtualPerlDependency(results.VersionResult, results.Warning):
"""Missing version restriction for virtual perl dependency.
The virtuals ``virtual/perl-*`` stand for packages that have releases both
as part of ``dev-lang/perl`` and standalone in ``perl-core/*``. Apart from
rare special cases, if you require "any" version of such a virtual, this
will always be fulfilled by ``dev-lang/perl``.
"""

def __init__(self, atom, **kwargs):
super().__init__(**kwargs)
self.atom = atom

@property
def desc(self):
return f"missing version restriction for virtual perl: {self.atom!r}"


class _PerlException(Exception):
"""Generic error during perl script initialization."""

Expand Down Expand Up @@ -70,12 +91,13 @@ def __del__(self):
class PerlCheck(OptionalCheck):
"""Perl ebuild related checks."""

_restricted_source = (
sources.RestrictionRepoSource,
(packages.PackageRestriction("inherited", values.ContainmentMatch2("perl-module")),),
_source = sources.EbuildFileRepoSource
known_results = frozenset(
{
MismatchedPerlVersion,
MissingVersionedVirtualPerlDependency,
}
)
_source = (sources.EbuildFileRepoSource, (), (("source", _restricted_source),))
known_results = frozenset([MismatchedPerlVersion])

def __init__(self, *args):
super().__init__(*args)
Expand All @@ -86,12 +108,32 @@ def __init__(self, *args):
# it easier to disable this check if required perl deps are missing.
try:
self.perl = _PerlConnection(self.options)
except _PerlException as e:
raise SkipCheck(self, str(e))
except _PerlException as exc:
raise SkipCheck(self, str(exc))

def feed(self, pkg):
if mo := self.dist_version_re.search("".join(pkg.lines)):
dist_version = mo.group("dist_version")
normalized = self.perl.normalize(dist_version)
if normalized != pkg.version:
yield MismatchedPerlVersion(dist_version, normalized, pkg=pkg)
if "perl-module" in pkg.inherited:
if mo := self.dist_version_re.search("".join(pkg.lines)):
dist_version = mo.group("dist_version")
normalized = self.perl.normalize(dist_version)
if normalized != pkg.version:
yield MismatchedPerlVersion(dist_version, normalized, pkg=pkg)

missing_virtual_perl = set()
for attr in (x.lower() for x in pkg.eapi.dep_keys):
try:
deps = getattr(pkg, attr)
except MetadataException:
continue
for atom in iflatten_instance(deps, (atom_cls,)):
if (
not atom.op
and atom.key.startswith("virtual/perl-")
and pkg.key != "dev-lang/perl"
and pkg.category != "perl-core"
and not pkg.key.startswith("virtual/perl-")
):
missing_virtual_perl.add(str(atom))

for atom in sorted(missing_virtual_perl):
yield MissingVersionedVirtualPerlDependency(str(atom), pkg=pkg)
36 changes: 36 additions & 0 deletions tests/checks/test_perl.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,39 @@ def test_no_perl_deps(self):
for verbosity in (0, 1):
with pytest.raises(SkipCheck, match="failed to run perl script"):
self.mk_check(verbosity=verbosity)

@pytest.mark.parametrize(
"cpv", ("dev-lang/perl-0", "perl-core/pkgcheck-0", "virtual/perl-pkgcheck-0")
)
def test_missing_op_virtual_good_packages(self, cpv):
pkg = misc.FakePkg(cpv, data={"RDEPEND": "virtual/perl-pkgcheck"})
self.assertNoReport(self.mk_check(), pkg)

@pytest.mark.parametrize(
"rdepend",
(
">=virtual/perl-pkgcheck-0",
"<virtual/perl-pkgcheck-0",
"virtual/pkgcheck",
"dev-cpp/perl-pkgcheck-0",
),
)
def test_missing_op_virtual_good_deps(self, rdepend):
pkg = misc.FakePkg("dev-cpp/perl-pkgcheck-0", data={"RDEPEND": rdepend})
self.assertNoReport(self.mk_check(), pkg)

def test_missing_op_virtual_bad(self):
pkg = misc.FakePkg(
"dev-cpp/perl-pkgcheck-0",
data={
"RDEPEND": "virtual/perl-pkgcore virtual/perl-pkgcheck",
"DEPEND": "virtual/perl-pkgcheck",
"BDEPEND": ">=virtual/perl-pkgcore-0",
},
)
reports = self.assertReports(self.mk_check(), pkg)
assert len(reports) == 2
for r in reports:
assert isinstance(r, perl.MissingVersionedVirtualPerlDependency)
assert reports[0].atom == "virtual/perl-pkgcheck"
assert reports[1].atom == "virtual/perl-pkgcore"

0 comments on commit 59cfd9b

Please sign in to comment.