commit: 59cfd9b5e53577b59fd513b1cdd9e0cda184ed13 Author: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org> AuthorDate: Sun Jul 2 19:24:58 2023 +0000 Commit: Arthur Zamarin <arthurzam <AT> gentoo <DOT> org> CommitDate: Sat Jul 15 08:36:00 2023 +0000 URL: https://gitweb.gentoo.org/proj/pkgcore/pkgcheck.git/commit/?id=59cfd9b5
PerlCheck: check versioned virtual perl dependencies Resolves: https://github.com/pkgcore/pkgcheck/issues/593 Signed-off-by: Arthur Zamarin <arthurzam <AT> gentoo.org> src/pkgcheck/checks/perl.py | 66 ++++++++++++++++++++++++++++++++++++--------- tests/checks/test_perl.py | 36 +++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/src/pkgcheck/checks/perl.py b/src/pkgcheck/checks/perl.py index f2c3bd25..2774f990 100644 --- a/src/pkgcheck/checks/perl.py +++ b/src/pkgcheck/checks/perl.py @@ -2,8 +2,11 @@ import multiprocessing 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 @@ -22,6 +25,24 @@ class MismatchedPerlVersion(results.VersionResult, results.Warning): 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.""" @@ -70,12 +91,13 @@ class _PerlConnection: 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) @@ -86,12 +108,32 @@ class PerlCheck(OptionalCheck): # 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) diff --git a/tests/checks/test_perl.py b/tests/checks/test_perl.py index 8de1a795..b2f76eab 100644 --- a/tests/checks/test_perl.py +++ b/tests/checks/test_perl.py @@ -73,3 +73,39 @@ class TestPerlCheck(misc.ReportTestCase): 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"
