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

PerlCheck: check versioned virtual perl dependencies #597

Merged
merged 1 commit into from
Jul 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"