summaryrefslogtreecommitdiffstats
path: root/lib/Lintian/Check/Languages
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:42:30 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:42:30 +0000
commit75808db17caf8b960b351e3408e74142f4c85aac (patch)
tree7989e9c09a4240248bf4658a22208a0a52d991c4 /lib/Lintian/Check/Languages
parentInitial commit. (diff)
downloadlintian-75808db17caf8b960b351e3408e74142f4c85aac.tar.xz
lintian-75808db17caf8b960b351e3408e74142f4c85aac.zip
Adding upstream version 2.117.0.upstream/2.117.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/Lintian/Check/Languages')
-rw-r--r--lib/Lintian/Check/Languages/Fortran/Gfortran.pm94
-rw-r--r--lib/Lintian/Check/Languages/Golang/BuiltUsing.pm68
-rw-r--r--lib/Lintian/Check/Languages/Golang/ImportPath.pm56
-rw-r--r--lib/Lintian/Check/Languages/Java.pm315
-rw-r--r--lib/Lintian/Check/Languages/Java/Bytecode.pm58
-rw-r--r--lib/Lintian/Check/Languages/Javascript/Embedded.pm149
-rw-r--r--lib/Lintian/Check/Languages/Javascript/Nodejs.pm262
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/ByteCode/Compiled.pm85
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/ByteCode/Interface.pm63
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/ByteCode/Library.pm58
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Package.pm126
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Path.pm105
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/ByteCode/Plugin.pm56
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/CustomExecutable.pm59
-rw-r--r--lib/Lintian/Check/Languages/Ocaml/Meta.pm67
-rw-r--r--lib/Lintian/Check/Languages/Perl.pm125
-rw-r--r--lib/Lintian/Check/Languages/Perl/Core/Provides.pm83
-rw-r--r--lib/Lintian/Check/Languages/Perl/Perl4/Prerequisites.pm124
-rw-r--r--lib/Lintian/Check/Languages/Perl/Perl5.pm61
-rw-r--r--lib/Lintian/Check/Languages/Perl/Yapp.pm55
-rw-r--r--lib/Lintian/Check/Languages/Php.pm53
-rw-r--r--lib/Lintian/Check/Languages/Php/Composer.pm93
-rw-r--r--lib/Lintian/Check/Languages/Php/Embedded.pm92
-rw-r--r--lib/Lintian/Check/Languages/Php/Pear.pm242
-rw-r--r--lib/Lintian/Check/Languages/Php/Pear/Embedded.pm92
-rw-r--r--lib/Lintian/Check/Languages/Python.pm516
-rw-r--r--lib/Lintian/Check/Languages/Python/BogusPrerequisites.pm88
-rw-r--r--lib/Lintian/Check/Languages/Python/DistOverrides.pm80
-rw-r--r--lib/Lintian/Check/Languages/Python/Distutils.pm77
-rw-r--r--lib/Lintian/Check/Languages/Python/Feedparser.pm54
-rw-r--r--lib/Lintian/Check/Languages/Python/Homepage.pm59
-rw-r--r--lib/Lintian/Check/Languages/Python/Obsolete.pm63
-rw-r--r--lib/Lintian/Check/Languages/Python/Scripts.pm54
-rw-r--r--lib/Lintian/Check/Languages/R.pm74
-rw-r--r--lib/Lintian/Check/Languages/R/Architecture.pm69
-rw-r--r--lib/Lintian/Check/Languages/R/SiteLibrary.pm71
-rw-r--r--lib/Lintian/Check/Languages/Ruby.pm72
-rw-r--r--lib/Lintian/Check/Languages/Rust.pm69
38 files changed, 3987 insertions, 0 deletions
diff --git a/lib/Lintian/Check/Languages/Fortran/Gfortran.pm b/lib/Lintian/Check/Languages/Fortran/Gfortran.pm
new file mode 100644
index 0000000..6479d8a
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Fortran/Gfortran.pm
@@ -0,0 +1,94 @@
+# languages/fortran/gfortran -- lintian check script -*- perl -*-
+
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Fortran::Gfortran;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use Unicode::UTF8 qw(encode_utf8);
+
+const my $NEWLINE => qq{\n};
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # file-info would be great, but files are zipped
+ return
+ unless $item->name =~ m{\.mod$};
+
+ return
+ unless $item->name =~ m{^usr/lib/};
+
+ # do not look at flang, grub or libreoffice modules
+ return
+ if $item->name =~ m{/flang-\d+/}
+ || $item->name =~ m{^usr/lib/grub}
+ || $item->name =~ m{^usr/lib/libreoffice};
+
+ return
+ unless $item->is_file
+ && $item->is_open_ok
+ && $item->file_type =~ /\bgzip compressed\b/;
+
+ my $module_version;
+
+ open(my $fd, '<:gzip', $item->unpacked_path)
+ or die encode_utf8(
+ 'Cannot open gz file ' . $item->unpacked_path . $NEWLINE);
+
+ while (my $line = <$fd>) {
+ next
+ if $line =~ /^\s*$/;
+
+ ($module_version) = ($line =~ /^GFORTRAN module version '(\d+)'/);
+ last;
+ }
+
+ close $fd;
+
+ unless (length $module_version) {
+ $self->pointed_hint('gfortran-module-does-not-declare-version',
+ $item->pointer);
+ return;
+ }
+
+ my $depends = $self->processable->fields->value('Depends');
+ $self->pointed_hint('missing-prerequisite-for-gfortran-module',
+ $item->pointer)
+ unless $depends =~ /\bgfortran-mod-$module_version\b/;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Golang/BuiltUsing.pm b/lib/Lintian/Check/Languages/Golang/BuiltUsing.pm
new file mode 100644
index 0000000..79095d3
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Golang/BuiltUsing.pm
@@ -0,0 +1,68 @@
+# languages/golang/built-using -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2004 Marc Brockschmidt
+# Copyright (C) 2020 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2020-2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Golang::BuiltUsing;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ return
+ unless $self->processable->relation('Build-Depends')
+ ->satisfies('golang-go | golang-any');
+
+ my $control = $self->processable->debian_control;
+
+ for my $installable ($control->installables) {
+ my $installable_fields= $control->installable_fields($installable);
+
+ my $control_item= $self->processable->debian_control->item;
+ my $position = $installable_fields->position('Package');
+
+ $self->pointed_hint(
+ 'missing-built-using-field-for-golang-package',
+ $control_item->pointer($position),
+ "(in section for $installable)"
+ )
+ if $installable_fields->value('Built-Using')
+ !~ m{ \$ [{] misc:Built-Using [}] }x
+ && $installable_fields->value('Architecture') ne 'all';
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Golang/ImportPath.pm b/lib/Lintian/Check/Languages/Golang/ImportPath.pm
new file mode 100644
index 0000000..210696b
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Golang/ImportPath.pm
@@ -0,0 +1,56 @@
+# languages/golang/import-path -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2004 Marc Brockschmidt
+# Copyright (C) 2020 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2020-2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Golang::ImportPath;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ return
+ unless $self->processable->relation('Build-Depends')
+ ->satisfies('golang-go | golang-any');
+
+ my $control = $self->processable->debian_control;
+ my $source_fields = $control->source_fields;
+
+ $self->hint('missing-xs-go-import-path-for-golang-package')
+ unless $source_fields->declares('XS-Go-Import-Path');
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Java.pm b/lib/Lintian/Check/Languages/Java.pm
new file mode 100644
index 0000000..4b26512
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Java.pm
@@ -0,0 +1,315 @@
+# languages/java -- lintian check script -*- perl -*-
+
+# Copyright (C) 2011 Vincent Fourmond
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Java;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use File::Basename;
+use List::SomeUtils qw(any none);
+
+use Lintian::Util qw(normalize_link_target $PKGNAME_REGEX $PKGVERSION_REGEX);
+
+const my $EMPTY => q{};
+const my $HYPHEN => q{-};
+
+const my $ARROW => q{->};
+
+const my $BYTE_CODE_VERSION_OFFSET => 44;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+our $CLASS_REGEX = qr/\.(?:class|cljc?)/;
+
+sub visit_patched_files {
+ my ($self, $item) = @_;
+
+ my $java_info = $item->java_info;
+ return
+ unless scalar keys %{$java_info};
+
+ my $files = $java_info->{files};
+
+ $self->pointed_hint('source-contains-prebuilt-java-object', $item->pointer)
+ if any { m/$CLASS_REGEX$/i } keys %{$files};
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ my $missing_jarwrapper = 0;
+ my $has_public_jars = 0;
+ my $jmajlow = $HYPHEN;
+
+ my $depends = $self->processable->relation('strong')->to_string;
+
+ # Remove all libX-java-doc packages to avoid thinking they are java libs
+ # - note the result may not be a valid dependency listing
+ $depends =~ s/lib[^\s,]+-java-doc//g;
+
+ my @java_lib_depends = ($depends =~ m/\b(lib[^\s,]+-java)\b/g);
+
+ my $JAVA_BYTECODES= $self->data->load('java/constants', qr/\s*=\s*/);
+
+ # We first loop over jar files to find problems
+
+ for my $item (@{$self->processable->installed->sorted_list}) {
+
+ my $java_info = $item->java_info;
+ next
+ unless scalar keys %{$java_info};
+
+ my $files = $java_info->{files};
+ my $manifest = $java_info->{manifest};
+ my $jar_dir = dirname($item->name);
+ my $classes = 0;
+ my $datafiles = 1;
+ my $class_path = $EMPTY;
+ my $bsname = $EMPTY;
+
+ if (exists $java_info->{error}) {
+ $self->pointed_hint('zip-parse-error', $item->pointer,
+ $java_info->{error});
+ next;
+ }
+
+ # The Java Policy says very little about requires for (jars in) JVMs
+ next
+ if $item->name =~ m{^usr/lib/jvm(?:-exports)?/[^/]+/};
+
+ # Ignore Mozilla's jar files, see #635495
+ next
+ if $item->name =~ m{^usr/lib/xul(?:-ext|runner[^/]*+)/};
+
+ if ($item->name =~ m{^usr/share/java/[^/]+\.jar$}) {
+ $has_public_jars = 1;
+
+ # java policy requires package version too; see Bug#976681
+ $self->pointed_hint('bad-jar-name', $item->pointer)
+ unless basename($item->name)
+ =~ /^$PKGNAME_REGEX-$PKGVERSION_REGEX\.jar$/;
+ }
+
+ # check for common code files like .class or .clj (Clojure files)
+ for my $class (grep { m/$CLASS_REGEX$/i } sort keys %{$files}){
+
+ my $module_version = $files->{$class};
+ (my $src = $class) =~ s/\.[^.]+$/\.java/;
+
+ $self->pointed_hint('jar-contains-source', $item->pointer, $src)
+ if %{$files}{$src};
+
+ $classes = 1;
+
+ next
+ if $class =~ m/\.cljc?$/;
+
+ # .class but no major version?
+ next
+ if $module_version eq $HYPHEN;
+
+ if ($module_version
+ < $JAVA_BYTECODES->value('lowest-known-bytecode-version')
+ || $module_version
+ > $JAVA_BYTECODES->value('highest-known-bytecode-version')) {
+
+ # First public major version was 45 (Java1), latest
+ # version is 55 (Java11).
+ $self->pointed_hint('unknown-java-class-version',
+ $item->pointer,$class, $ARROW, $module_version);
+
+ # Skip the rest of this Jar.
+ last;
+ }
+
+ # Collect the "lowest" Class version used. We assume that
+ # mixed class formats implies special compat code for certain
+ # JVM cases.
+ if ($jmajlow eq $HYPHEN) {
+ # first;
+ $jmajlow = $module_version;
+
+ } else {
+ $jmajlow = $module_version
+ if $module_version < $jmajlow;
+ }
+ }
+
+ $datafiles = 0
+ if none { /\.(?:xml|properties|x?html|xhp)$/i } keys %{$files};
+
+ if ($item->is_executable) {
+
+ $self->pointed_hint('executable-jar-without-main-class',
+ $item->pointer)
+ unless $manifest && $manifest->{'Main-Class'};
+
+ # Here, we need to check that the package depends on
+ # jarwrapper.
+ $missing_jarwrapper = 1
+ unless $self->processable->relation('strong')
+ ->satisfies('jarwrapper');
+
+ } elsif ($item->name !~ m{^usr/share/}) {
+
+ $self->pointed_hint('jar-not-in-usr-share', $item->pointer);
+ }
+
+ $class_path = $manifest->{'Class-Path'}//$EMPTY if $manifest;
+ $bsname = $manifest->{'Bundle-SymbolicName'}//$EMPTY if $manifest;
+
+ if ($manifest) {
+ if (!$classes) {
+
+ # Eclipse / OSGi bundles are sometimes source bundles
+ # these do not ship classes but java files and other sources.
+ # Javadoc jars deployed in the Maven repository also do not ship
+ # classes but HTML files, images and CSS files
+ if (
+ (
+ $bsname !~ m/\.source$/
+ && $item->name
+ !~ m{^usr/share/maven-repo/.*-javadoc\.jar}
+ && $item->name !~ m{\.doc(?:\.(?:user|isv))?_[^/]+.jar}
+ && $item->name !~ m{\.source_[^/]+.jar}
+ )
+ || $class_path
+ ) {
+ $self->pointed_hint('codeless-jar', $item->pointer);
+ }
+ }
+
+ } elsif ($classes) {
+ $self->pointed_hint('missing-manifest', $item->pointer);
+ }
+
+ if ($class_path) {
+ # Only run the tests when a classpath is present
+ my @relative;
+ my @paths = split(m/\s++/, $class_path);
+ for my $p (@paths) {
+ if ($p) {
+ # Strip leading ./
+ $p =~ s{^\./+}{}g;
+ if ($p !~ m{^(?:file://)?/} && $p =~ m{/}) {
+ my $target = normalize_link_target($jar_dir, $p);
+ my $tinfo;
+ # Can it be normalized?
+ next unless defined($target);
+ # Relative link to usr/share/java ? Works if
+ # we are depending of a Java library.
+ next
+ if $target =~ m{^usr/share/java/[^/]+.jar$}
+ && @java_lib_depends;
+ $tinfo= $self->processable->installed->lookup($target);
+ # Points to file or link in this package,
+ # which is sometimes easier than
+ # re-writing the classpath.
+ next
+ if defined $tinfo
+ and ($tinfo->is_symlink or $tinfo->is_file);
+ # Relative path with subdirectories.
+ push @relative, $p;
+ }
+ # @todo add an info tag for relative paths, to educate
+ # maintainers ?
+ }
+ }
+
+ $self->pointed_hint('classpath-contains-relative-path',
+ $item->pointer, join(', ', @relative))
+ if @relative;
+ }
+
+ # Trigger a warning when a maven plugin lib is installed in
+ # /usr/share/java/
+ $self->pointed_hint('maven-plugin-in-usr-share-java', $item->pointer)
+ if $has_public_jars
+ && $self->processable->name =~ /^lib.*maven.*plugin.*/
+ && $item->name !~ m{^usr/share/maven-repo/.*\.jar};
+ }
+
+ $self->hint('missing-dep-on-jarwrapper') if $missing_jarwrapper;
+
+ if ($jmajlow ne $HYPHEN) {
+ # Byte code numbers:
+ # 45-49 -> Java1 - Java5 (Always ok)
+ # 50 -> Java6
+ # 51 -> Java7
+ # 52 -> Java8
+ # 53 -> Java9
+ # 54 -> Java10
+ # 55 -> Java11
+ my $bad = 0;
+
+ # If the lowest version used is greater than the requested
+ # limit, then flag it.
+ $bad = 1
+ if $jmajlow > $JAVA_BYTECODES->value('default-bytecode-version');
+
+ # Technically we ought to do some checks with Java6 class
+ # files and dependencies/package types, but for now just skip
+ # that. (See #673276)
+
+ if ($bad) {
+ # Map the Class version to a Java version.
+ my $java_version = $jmajlow - $BYTE_CODE_VERSION_OFFSET;
+
+ $self->hint('incompatible-java-bytecode-format',
+ "Java$java_version version (Class format: $jmajlow)");
+ }
+ }
+
+ if ( !$has_public_jars
+ && !$self->processable->is_transitional
+ && $self->processable->name =~ /^lib[^\s,]+-java$/){
+
+ # Skip this if it installs a symlink in usr/share/java
+ my $java_dir
+ = $self->processable->installed->resolve_path('usr/share/java/');
+
+ my $has_jars = 0;
+ $has_jars = 1
+ if $java_dir
+ && (any { $_->name =~ m{^[^/]+\.jar$} } $java_dir->children);
+
+ $self->hint('javalib-but-no-public-jars')
+ unless $has_jars;
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Java/Bytecode.pm b/lib/Lintian/Check/Languages/Java/Bytecode.pm
new file mode 100644
index 0000000..14566a9
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Java/Bytecode.pm
@@ -0,0 +1,58 @@
+# languages/java/bytecode -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Java::Bytecode;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+const my $MAGIC_BYTE_SIZE => 4;
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # .class (compiled Java files)
+ if ( $item->name =~ /\.class$/
+ && $item->name !~ /(?:WEB-INF|demo|doc|example|sample|test)/) {
+
+ my $magic = $item->magic($MAGIC_BYTE_SIZE);
+
+ $self->pointed_hint('package-installs-java-bytecode', $item->pointer)
+ if $magic eq "\xCA\xFE\xBA\xBE";
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Javascript/Embedded.pm b/lib/Lintian/Check/Languages/Javascript/Embedded.pm
new file mode 100644
index 0000000..9227187
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Javascript/Embedded.pm
@@ -0,0 +1,149 @@
+# languages/javascript/embedded -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Javascript::Embedded;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+my %JS_MAGIC
+ = ('libjs-bootstrap' => qr{ var [ ] (?: Carousel | Typeahead ) }x,);
+
+my $JS_EXT
+ = qr{(?:(?i)[-._]?(?:compiled|lite|min|pack(?:ed)?|prod|umd|yc)?\.(js|css)(?:\.gz)?)$};
+my %JS_FILES = (
+ 'ckeditor' => qr{(?i)/ckeditor} . $JS_EXT,
+ 'fckeditor' => qr{(?i)/fckeditor} . $JS_EXT,
+ 'libjs-async' => qr{(?i)/async} . $JS_EXT,
+ 'libjs-bootstrap' => qr{(?i)/bootstrap(?:-[\d\.]+)?} . $JS_EXT,
+ 'libjs-chai' => qr{(?i)/chai} . $JS_EXT,
+ 'libjs-cropper' => qr{(?i)/cropper(?:\.uncompressed)?} . $JS_EXT,
+ 'libjs-dojo-\w+' => qr{(?i)/(?:dojo|dijit)} . $JS_EXT,
+ 'libjs-excanvas' => qr{(?i)/excanvas(?:-r[0-9]+)?} . $JS_EXT,
+ 'libjs-jac' => qr{(?i)/jsjac} . $JS_EXT,
+ 'libjs-jquery' => qr{(?i)/jquery(?:-[\d\.]+)?} . $JS_EXT,
+ 'libjs-jquery-cookie' => qr{(?i)/jquery\.cookie} . $JS_EXT,
+ 'libjs-jquery-easing' => qr{(?i)/jquery\.easing} . $JS_EXT,
+ 'libjs-jquery-event-drag' => qr{(?i)/jquery\.event\.drap} . $JS_EXT,
+ 'libjs-jquery-event-drop' => qr{(?i)/jquery\.event\.drop} . $JS_EXT,
+ 'libjs-jquery-fancybox' => qr{(?i)/jquery\.fancybox} . $JS_EXT,
+ 'libjs-jquery-form' => qr{(?i)/jquery\.form} . $JS_EXT,
+ 'libjs-jquery-galleriffic' => qr{(?i)/jquery\.galleriffic} . $JS_EXT,
+ 'libjs-jquery-history' => qr{(?i)/jquery\.history} . $JS_EXT,
+ 'libjs-jquery-jfeed' => qr{(?i)/jquery\.jfeed} . $JS_EXT,
+ 'libjs-jquery-jush' => qr{(?i)/jquery\.jush} . $JS_EXT,
+ 'libjs-jquery-livequery' => qr{(?i)/jquery\.livequery} . $JS_EXT,
+ 'libjs-jquery-meiomask' => qr{(?i)/jquery\.meiomask} . $JS_EXT,
+ 'libjs-jquery-metadata' => qr{(?i)/jquery\.metadata} . $JS_EXT,
+ 'libjs-jquery-migrate-1' => qr{(?i)/jquery-migrate(?:-1[\d\.]*)}
+ . $JS_EXT,
+ 'libjs-jquery-mousewheel' => qr{(?i)/jquery\.mousewheel} . $JS_EXT,
+ 'libjs-jquery-opacityrollover' => qr{(?i)/jquery\.opacityrollover}
+ . $JS_EXT,
+ 'libjs-jquery-tablesorter' => qr{(?i)/jquery\.tablesorter} . $JS_EXT,
+ 'libjs-jquery-tipsy' => qr{(?i)/jquery\.tipsy} . $JS_EXT,
+ 'libjs-jquery-treetable' => qr{(?i)/jquery\.treetable} . $JS_EXT,
+ 'libjs-jquery-ui' => qr{(?i)/jquery[\.-](?:-[\d\.]+)?ui}
+ . $JS_EXT,
+ 'libjs-mocha' => qr{(?i)/mocha} . $JS_EXT,
+ 'libjs-mochikit' => qr{(?i)/mochikit} . $JS_EXT,
+ 'libjs-mootools' =>
+qr{(?i)/mootools(?:(?:\.v|-)[\d\.]+)?(?:-(?:(?:core(?:-server)?)|more)(?:-(?:yc|jm|nc))?)?}
+ . $JS_EXT,
+ 'libjs-mustache' => qr{(?i)/mustache} . $JS_EXT,
+# libjs-normalize is provided by node-normalize.css but this is an implementation detail
+ 'libjs-normalize' => qr{(?i)/normalize(?:\.min)?\.css},
+ 'libjs-prototype' => qr{(?i)/prototype(?:-[\d\.]+)?}. $JS_EXT,
+ 'libjs-raphael' => qr{(?i)/raphael(?:[\.-]min)?} . $JS_EXT,
+ 'libjs-scriptaculous' => qr{(?i)/scriptaculous} . $JS_EXT,
+ 'libjs-strophe' => qr{(?i)/strophe} . $JS_EXT,
+ 'libjs-underscore' => qr{(?i)/underscore} . $JS_EXT,
+ 'libjs-yui' => qr{(?i)/(?:yahoo|yui)-(?:dom-event)?}
+ . $JS_EXT,
+ # Disabled due to false positives. Needs a content check adding to verify
+ # that the file being checked is /the/ yahoo.js
+ # 'libjs-yui' => qr{(?i)/yahoo\.js(\.gz)?} . $JS_EXT,
+ 'jsmath' => qr{(?i)/jsMath(?:-fallback-\w+)?}
+ . $JS_EXT,
+ 'node-html5shiv' => qr{(?i)html5shiv(?:-printshiv)?}
+ . $JS_EXT,
+ 'sphinx' =>
+ qr{(?i)/_static/(?:doctools|language_data|searchtools)} . $JS_EXT,
+ 'tinymce' => qr{(?i)/tiny_mce(?:_(?:popup|src))?}
+ . $JS_EXT,
+ 'libjs-lodash' => qr{(?i)lodash} . $JS_EXT,
+ 'node-pako' =>
+ qr{(?i)pako(?:_(:?de|in)flate(?:.es\d+)?)(?:-[\d\.]+)?}. $JS_EXT,
+ 'node-jszip-utils' => qr{(?i)jszip-utils(?:-ie)?(?:-[\d\.]+)?}
+ . $JS_EXT,
+ 'node-jszip' => qr{(?i)jszip(?:-ie)?(?:-[\d\.]+)?} . $JS_EXT,
+ 'libjs-codemirror' => qr{(?i)codemirror} . $JS_EXT,
+ 'libjs-punycode' => qr{(?i)punycode(?:\.es\d+)?} . $JS_EXT,
+# not yet available in unstable
+# 'xinha' => qr{(?i)/(htmlarea|Xinha(Loader|Core))} . $JS_EXT,
+);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ # ignore embedded jQuery libraries for Doxygen (#736360)
+ my $doxygen = $self->processable->installed->resolve_path(
+ $item->dirname . 'doxygen.css');
+ return
+ if $item->basename eq 'jquery.js'
+ && defined $doxygen;
+
+ # embedded javascript
+ for my $provider (keys %JS_FILES) {
+
+ next
+ if $self->processable->name =~ /^$provider$/;
+
+ next
+ unless $item->name =~ /$JS_FILES{$provider}/;
+
+ next
+ if length $JS_MAGIC{$provider}
+ && !length $item->bytes_match($JS_MAGIC{$provider});
+
+ $self->pointed_hint('embedded-javascript-library', $item->pointer,
+ 'please use', $provider);
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Javascript/Nodejs.pm b/lib/Lintian/Check/Languages/Javascript/Nodejs.pm
new file mode 100644
index 0000000..98a5d76
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Javascript/Nodejs.pm
@@ -0,0 +1,262 @@
+# languages/javascript/nodejs -- lintian check script -*- perl -*-
+
+# Copyright (C) 2019-2020, Xavier Guimard <yadd@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Javascript::Nodejs;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use JSON::MaybeXS;
+use List::SomeUtils qw(any none first_value);
+use Path::Tiny;
+use Syntax::Keyword::Try;
+use Unicode::UTF8 qw(encode_utf8);
+
+use Lintian::Relation;
+
+const my $SLASH => q{/};
+const my $DOT => q{.};
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ my $debian_control = $self->processable->debian_control;
+
+ # debian/control check
+ my @testsuites
+ = split(m/\s*,\s*/,$debian_control->source_fields->value('Testsuite'));
+
+ if (any { $_ eq 'autopkgtest-pkg-nodejs' } @testsuites) {
+
+ my $item = $self->processable->patched->resolve_path(
+ 'debian/tests/pkg-js/test');
+ if (defined $item) {
+
+ $self->pointed_hint('pkg-js-autopkgtest-test-is-empty',
+ $item->pointer)
+ if none { /^[^#]*\w/m } $item->bytes;
+
+ } else {
+ $self->hint('pkg-js-autopkgtest-test-is-missing');
+ }
+
+ # Ensure all files referenced in debian/tests/pkg-js/files exist
+ my $files
+ = $self->processable->patched->resolve_path(
+ 'debian/tests/pkg-js/files');
+ if (defined $files) {
+
+ my @patterns = path($files->unpacked_path)->lines;
+
+ # trim leading and trailing whitespace
+ s/^\s+|\s+$//g for @patterns;
+
+ my @notfound = grep { !$self->path_exists($_) } @patterns;
+
+ $self->hint('pkg-js-autopkgtest-file-does-not-exist', $_)
+ for @notfound;
+ }
+ }
+
+ # debian/rules check
+ my $droot = $self->processable->patched->resolve_path('debian/')
+ or return;
+ my $drules = $droot->child('rules')
+ or return;
+
+ return
+ unless $drules->is_open_ok;
+
+ open(my $rules_fd, '<', $drules->unpacked_path)
+ or die encode_utf8('Cannot open ' . $drules->unpacked_path);
+
+ my $command_prefix_pattern = qr/\s+[@+-]?(?:\S+=\S+\s+)*/;
+ my ($seen_nodejs,$override_test,$seen_dh_dynamic);
+ my $bdepends = $self->processable->relation('Build-Depends-All');
+ $seen_nodejs = 1 if $bdepends->satisfies('dh-sequence-nodejs');
+
+ while (my $line = <$rules_fd>) {
+
+ # reconstitute splitted lines
+ while ($line =~ s/\\$// && defined(my $cont = <$rules_fd>)) {
+ $line .= $cont;
+ }
+
+ # skip comments
+ next
+ if $line =~ /^\s*\#/;
+
+ if ($line =~ m{^(?:$command_prefix_pattern)dh\s+}) {
+ $seen_dh_dynamic = 1
+ if $line =~ /\$[({]\w/;
+
+ while ($line =~ /\s--with(?:=|\s+)(['"]?)(\S+)\1/g) {
+ my @addons = split(m{,}, $2);
+ $seen_nodejs = 1
+ if any { $_ eq 'nodejs' } @addons;
+ }
+
+ } elsif ($line =~ /^([^:]*override_dh_[^:]*):/) {
+ $override_test = 1
+ if $1 eq 'auto_test';
+ }
+ }
+
+ if( $seen_nodejs
+ && !$override_test
+ && !$seen_dh_dynamic) {
+
+ # pkg-js-tools search build test in the following order
+ my @candidates = qw{debian/nodejs/test debian/tests/pkg-js/test};
+
+ my $item = first_value { defined }
+ map { $self->processable->patched->resolve_path($_) } @candidates;
+
+ # Ensure test file contains something
+ if (defined $item) {
+ $self->pointed_hint('pkg-js-tools-test-is-empty', $item->pointer)
+ unless any { /^[^#]*\w/m } $item->bytes;
+
+ } else {
+ $self->hint('pkg-js-tools-test-is-missing');
+ }
+ }
+
+ return;
+}
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ if $item->is_dir;
+
+ return
+ if $self->processable->name =~ /-dbg$/;
+
+ # Warn if a file is installed in old nodejs root dir
+ $self->pointed_hint('nodejs-module-installed-in-usr-lib', $item->pointer)
+ if $item->name =~ m{^usr/lib/nodejs/.*};
+
+ # Warn if package is not installed in a subdirectory of nodejs root
+ # directories
+ $self->pointed_hint('node-package-install-in-nodejs-rootdir',
+ $item->pointer)
+ if $item->name
+ =~ m{^usr/(?:share|lib(?:/[^/]+)?)/nodejs/(?:package\.json|[^/]*\.js)$};
+
+ # Now we have to open package.json
+ return
+ unless $item->is_open_ok;
+
+ # Return an error if a package-lock.json or a yanr.lock file is installed
+ $self->pointed_hint('nodejs-lock-file', $item->pointer)
+ if $item->name
+ =~ m{^usr/(?:share|lib(?:/[^/]+)?)/nodejs/([^/]+)(.*/)(package-lock\.json|yarn\.lock)$};
+
+ # Look only nodejs package.json files
+ return
+ unless $item->name
+ =~ m{^usr/(?:share|lib(?:/[^/]+)?)/nodejs/([^\@/]+|\@[^/]+/[^/]+)(.*/)package\.json$};
+
+ # First regexp arg: directory in /**/nodejs or @foo/bar when dir starts
+ # with '@', following npm registry policy
+ my $dirname = $1;
+ # Second regex arg: subpath in /**/nodejs/module/ (eg: node_modules/foo)
+ my $subpath = $2;
+
+ my $declared = $self->processable->name;
+ my $version = $self->processable->fields->value('Version');
+ $declared .= "( = $version)"
+ if length $version;
+ $version ||= '0-1';
+
+ my $provides
+ = $self->processable->relation('Provides')->logical_and($declared);
+
+ my $content = $item->bytes;
+
+ # Look only valid package.json files
+ my $pac;
+ try {
+ $pac = decode_json($content);
+ die
+ unless length $pac->{name};
+ } catch {
+ return;
+ }
+
+ # Store node module name & version (classification)
+ $self->pointed_hint('nodejs-module', $item->pointer, $pac->{name},
+ $pac->{version} // 'undef');
+
+ # Warn if version is 0.0.0-development
+ $self->pointed_hint('nodejs-missing-version-override',
+ $item->pointer, $pac->{name}, $pac->{version})
+ if $pac->{version} and $pac->{version} =~ /^0\.0\.0-dev/;
+
+ # Warn if module name is not equal to nodejs directory
+ if ($subpath eq $SLASH && $dirname ne $pac->{name}) {
+ $self->pointed_hint('nodejs-module-installed-in-bad-directory',
+ $item->pointer, $pac->{name}, $dirname);
+
+ } else {
+ # Else verify that module is declared at least in Provides: field
+ my $name = 'node-' . lc($pac->{name});
+ # Normalize name following Debian policy
+ # (replace invalid characters by "-")
+ $name =~ s{[/_\@]}{-}g;
+ $name =~ s/-+/-/g;
+
+ $self->pointed_hint('nodejs-module-not-declared', $item->pointer,$name)
+ if $subpath eq $SLASH
+ && !$provides->satisfies($name);
+ }
+
+ return;
+}
+
+sub path_exists {
+ my ($self, $expression) = @_;
+
+ # replace asterisks with proper regex wildcard
+ $expression =~ s{ [*] }{[^/]*}gsx;
+
+ return 1
+ if any { m{^ $expression /? $}sx }
+ @{$self->processable->patched->sorted_list};
+
+ return 0;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/ByteCode/Compiled.pm b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Compiled.pm
new file mode 100644
index 0000000..f916d68
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Compiled.pm
@@ -0,0 +1,85 @@
+# languages/ocaml/byte-code/compiled -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::ByteCode::Compiled;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+has provided_o => (
+ is => 'rw',
+ lazy => 1,
+ default => sub {
+ my ($self) = @_;
+
+ my %provided_o;
+
+ for my $item (@{$self->processable->installed->sorted_list}) {
+
+ for my $count (keys %{$item->ar_info}) {
+
+ my $member = $item->ar_info->{$count}{name};
+ next
+ unless length $member;
+
+ # dirname ends in a slash
+ my $virtual_path = $item->dirname . $member;
+
+ # Note: a .o may be legitimately in several different .a
+ $provided_o{$virtual_path} = $item->name;
+ }
+ }
+
+ return \%provided_o;
+ }
+);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ my $no_extension = $item->basename;
+ $no_extension =~ s{ [.] [^.]+ $}{}x;
+
+ # The .cmx counterpart: for each .cmx file, there must be a
+ # matching .o file, which can be there by itself, or embedded in a
+ # .a file in the same directory
+ # dirname ends with a slash
+ $self->pointed_hint('ocaml-dangling-cmx', $item->pointer)
+ if $item->name =~ m{ [.]cmx $}x
+ && !$item->parent_dir->child($no_extension . '.o')
+ && !exists $self->provided_o->{$item->dirname . $no_extension . '.o'};
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/ByteCode/Interface.pm b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Interface.pm
new file mode 100644
index 0000000..8edeab1
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Interface.pm
@@ -0,0 +1,63 @@
+# languages/ocaml/byte-code/interface -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::ByteCode::Interface;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+const my $LAST_ITEM => -1;
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ my $no_extension = $item->basename;
+ $no_extension =~ s{ [.] [^.]+ $}{}x;
+
+ # for dune
+ my $interface_name = (split(/__/, $no_extension))[$LAST_ITEM];
+
+ # $somename.cmi should be shipped with $somename.mli or $somename.ml
+ $self->pointed_hint('ocaml-dangling-cmi', $item->pointer)
+ if $item->name =~ m{ [.]cmi $}x
+ && !$item->parent_dir->child($interface_name . '.mli')
+ && !$item->parent_dir->child(lc($interface_name) . '.mli')
+ && !$item->parent_dir->child($interface_name . '.ml')
+ && !$item->parent_dir->child(lc($interface_name) . '.ml');
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/ByteCode/Library.pm b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Library.pm
new file mode 100644
index 0000000..965f134
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Library.pm
@@ -0,0 +1,58 @@
+# languages/ocaml/byte-code/library -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::ByteCode::Library;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ my $no_extension = $item->basename;
+ $no_extension =~ s{ [.] [^.]+ $}{}x;
+
+ # For each .cmxa file, there must be a matching .a file (#528367)
+ $self->pointed_hint('ocaml-dangling-cmxa', $item->pointer)
+ if $item->name =~ m{ [.]cmxa $}x
+ && !$item->parent_dir->child($no_extension . '.a');
+
+ # $somename.cmo should usually not be shipped with $somename.cma
+ $self->pointed_hint('ocaml-stray-cmo', $item->pointer)
+ if $item->name =~ m{ [.]cma $}x
+ && $item->parent_dir->child($no_extension . '.cmo');
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Package.pm b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Package.pm
new file mode 100644
index 0000000..767f6b0
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Package.pm
@@ -0,0 +1,126 @@
+# languages/ocaml/byte-code/misplaced/package -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::ByteCode::Misplaced::Package;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use List::SomeUtils qw(first_value);
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+const my $EMPTY => q{};
+const my $SLASH => q{/};
+
+has development_files => (is => 'rw', default => sub { [] });
+
+has is_dev_package => (
+ is => 'rw',
+ lazy => 1,
+ default => sub {
+ my ($self) = @_;
+
+ # is it a development package?
+ return 1
+ if (
+ $self->processable->name =~ m{
+ (?: -dev
+ |\A camlp[45](?:-extra)?
+ |\A ocaml (?:
+ -nox
+ |-interp
+ |-compiler-libs
+ )?
+ )\Z}xsm
+ );
+
+ return 0;
+ }
+);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # .cma, .cmo and .cmxs are excluded because they can be plugins
+ push(@{$self->development_files}, $item->name)
+ if $item->name =~ m{ [.] cm (?: i | xa? ) $}x;
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ my $count = scalar @{$self->development_files};
+ my $plural = ($count == 1) ? $EMPTY : 's';
+
+ my $prefix = longest_common_prefix(@{$self->development_files});
+
+ # strip trailing slash
+ $prefix =~ s{ / $}{}x
+ unless $prefix eq $SLASH;
+
+ # non-dev packages should not ship .cmi, .cmx or .cmxa files
+ $self->hint('ocaml-dev-file-in-nondev-package',
+ "$count file$plural in $prefix")
+ if $count > 0
+ && !$self->is_dev_package;
+
+ return;
+}
+
+sub longest_common_prefix {
+ my (@paths) = @_;
+
+ my %prefixes;
+
+ for my $path (@paths) {
+
+ my $truncated = $path;
+
+ # first operation drops the file name
+ while ($truncated =~ s{ / [^/]* $}{}x) {
+ ++$prefixes{$truncated};
+ }
+ }
+
+ my @by_descending_length = reverse sort keys %prefixes;
+
+ my $common = first_value { $prefixes{$_} == @paths } @by_descending_length;
+
+ $common ||= $SLASH;
+
+ return $common;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Path.pm b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Path.pm
new file mode 100644
index 0000000..68e4f4f
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Misplaced/Path.pm
@@ -0,0 +1,105 @@
+# languages/ocaml/byte-code/misplaced/path -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::ByteCode::Misplaced::Path;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use List::SomeUtils qw(first_value);
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+const my $EMPTY => q{};
+const my $SLASH => q{/};
+
+has misplaced_files => (is => 'rw', default => sub { [] });
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # development files outside /usr/lib/ocaml (.cmi, .cmx, .cmxa)
+ return
+ if $item->name =~ m{^ usr/lib/ocaml/ }x;
+
+ # .cma, .cmo and .cmxs are excluded because they can be plugins
+ push(@{$self->misplaced_files}, $item->name)
+ if $item->name =~ m{ [.] cm (?: i | xa? ) $}x;
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ my $count = scalar @{$self->misplaced_files};
+ my $plural = ($count == 1) ? $EMPTY : 's';
+
+ my $prefix = longest_common_prefix(@{$self->misplaced_files});
+
+ # strip trailing slash
+ $prefix =~ s{ / $}{}x
+ unless $prefix eq $SLASH;
+
+ $self->hint(
+ 'ocaml-dev-file-not-in-usr-lib-ocaml',
+ "$count file$plural in $prefix"
+ )if $count > 0;
+
+ return;
+}
+
+sub longest_common_prefix {
+ my (@paths) = @_;
+
+ my %prefixes;
+
+ for my $path (@paths) {
+
+ my $truncated = $path;
+
+ # first operation drops the file name
+ while ($truncated =~ s{ / [^/]* $}{}x) {
+ ++$prefixes{$truncated};
+ }
+ }
+
+ my @by_descending_length = reverse sort keys %prefixes;
+
+ my $common = first_value { $prefixes{$_} == @paths } @by_descending_length;
+
+ $common ||= $SLASH;
+
+ return $common;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/ByteCode/Plugin.pm b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Plugin.pm
new file mode 100644
index 0000000..ae14f6b
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/ByteCode/Plugin.pm
@@ -0,0 +1,56 @@
+# languages/ocaml/byte-code/plugin -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::ByteCode::Plugin;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ my $no_extension = $item->basename;
+ $no_extension =~ s{ [.] [^.]+ $}{}x;
+
+ # For each .cmxs file, there must be a matching .cma or .cmo file
+ # (at least, in library packages)
+ $self->pointed_hint('ocaml-dangling-cmxs', $item->pointer)
+ if $item->name =~ m{ [.]cmxs $}x
+ && !$item->parent_dir->child($no_extension . '.cma')
+ && !$item->parent_dir->child($no_extension . '.cmo')
+ && $self->processable->name =~ /^lib/;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/CustomExecutable.pm b/lib/Lintian/Check/Languages/Ocaml/CustomExecutable.pm
new file mode 100644
index 0000000..8ebad48
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/CustomExecutable.pm
@@ -0,0 +1,59 @@
+# languages/ocaml/custom-executable -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 2012 Kees Cook
+# Copyright (C) 2017-2020 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::CustomExecutable;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ return
+ unless $item->file_type =~ /^ [^,]* \b ELF \b /x;
+
+ # Check for OCaml custom executables (#498138)
+ $self->pointed_hint('ocaml-custom-executable', $item->pointer)
+ if $item->file_type =~ m{ \b not [ ] stripped \b }x
+ && $item->file_type =~ m{ \b executable \b }x
+ && $item->strings =~ m{^ Caml1999X0 [0-9] [0-9] $}mx;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ocaml/Meta.pm b/lib/Lintian/Check/Languages/Ocaml/Meta.pm
new file mode 100644
index 0000000..0a9976b
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ocaml/Meta.pm
@@ -0,0 +1,67 @@
+# languages/ocaml/meta -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2009 Stephane Glondu
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ocaml::Meta;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+has has_meta => (is => 'rw', default => 0);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->name =~ m{^ usr/lib/ocaml/ }x;
+
+ # does the package provide a META file?
+ $self->has_meta(1)
+ if $item->name =~ m{ / META (?: [.] | $ ) }x;
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ my $prerequisites = $self->processable->relation('all');
+
+ # If there is a META file, ocaml-findlib should at least be suggested.
+ $self->hint('ocaml-meta-without-suggesting-findlib')
+ if $self->has_meta
+ && !$prerequisites->satisfies('ocaml-findlib:any');
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Perl.pm b/lib/Lintian/Check/Languages/Perl.pm
new file mode 100644
index 0000000..c68af47
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Perl.pm
@@ -0,0 +1,125 @@
+# languages/perl -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Perl;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Unicode::UTF8 qw(encode_utf8);
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+has perl_sources_in_lib => (is => 'rw', default => sub { [] });
+has has_perl_binaries => (is => 'rw', default => 0);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # perllocal.pod
+ $self->pointed_hint('package-installs-perllocal-pod', $item->pointer)
+ if $item->name =~ m{^usr/lib/perl.*/perllocal.pod$};
+
+ # .packlist files
+ if ($item->name =~ m{^usr/lib/perl.*/.packlist$}) {
+ $self->pointed_hint('package-installs-packlist', $item->pointer);
+
+ }elsif ($item->name =~ m{^usr/lib/(?:[^/]+/)?perl5/.*\.p[lm]$}) {
+ push @{$self->perl_sources_in_lib}, $item;
+
+ }elsif ($item->name =~ m{^usr/lib/(?:[^/]+/)?perl5/.*\.(?:bs|so)$}) {
+ $self->has_perl_binaries(1);
+ }
+
+ # perl modules
+ if ($item->name =~ m{^usr/(?:share|lib)/perl/\S}) {
+
+ # check if it's the "perl" package itself
+ $self->pointed_hint('perl-module-in-core-directory', $item->pointer)
+ unless $self->processable->source_name eq 'perl';
+ }
+
+ # perl modules using old libraries
+ # we do the same check on perl scripts in checks/scripts
+ my $dep = $self->processable->relation('strong');
+ if ( $item->is_file
+ && $item->name =~ /\.pm$/
+ && !$dep->satisfies('libperl4-corelibs-perl | perl (<< 5.12.3-7)')) {
+
+ open(my $fd, '<', $item->unpacked_path)
+ or die encode_utf8('Cannot open ' . $item->unpacked_path);
+
+ my $position = 1;
+ while (my $line = <$fd>) {
+ if (
+ $line =~ m{ (?:do|require)\s+['"] # do/require
+
+ # Huge list of perl4 modules...
+ (abbrev|assert|bigfloat|bigint|bigrat
+ |cacheout|complete|ctime|dotsh|exceptions
+ |fastcwd|find|finddepth|flush|getcwd|getopt
+ |getopts|hostname|importenv|look|newgetopt
+ |open2|open3|pwd|shellwords|stat|syslog
+ |tainted|termcap|timelocal|validate)
+ # ... so they end with ".pl" rather than ".pm"
+ \.pl['"]
+ }xsm
+ ) {
+ my $module = $1;
+
+ $self->pointed_hint('perl-module-uses-perl4-libs-without-dep',
+ $item->pointer($position), "$module.pl");
+ }
+
+ } continue {
+ ++$position;
+ }
+
+ close $fd;
+ }
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ unless ($self->has_perl_binaries) {
+
+ $self->pointed_hint('package-installs-nonbinary-perl-in-usr-lib-perl5',
+ $_->pointer)
+ for @{$self->perl_sources_in_lib};
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Perl/Core/Provides.pm b/lib/Lintian/Check/Languages/Perl/Core/Provides.pm
new file mode 100644
index 0000000..b0a3923
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Perl/Core/Provides.pm
@@ -0,0 +1,83 @@
+# languages/perl/core/provides -- lintian check script (rewrite) -*- perl -*-
+#
+# Copyright (C) 2004 Marc Brockschmidt
+# Copyright (C) 2021 Felix Lechner
+#
+# Parts of the code were taken from the old check script, which
+# was Copyright (C) 1998 Richard Braakman (also licensed under the
+# GPL 2 or higher)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Perl::Core::Provides;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Dpkg::Version qw(version_check);
+
+use Lintian::Relation::Version qw(versions_compare);
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub always {
+ my ($self) = @_;
+
+ my $fields = $self->processable->fields;
+
+ return
+ unless $fields->declares('Version');
+
+ my $version = $fields->unfolded_value('Version');
+
+ my $dversion = Dpkg::Version->new($version);
+ return
+ unless $dversion->is_valid;
+
+ my ($epoch, $upstream, $debian)
+ = ($dversion->epoch, $dversion->version, $dversion->revision);
+
+ my $PERL_CORE_PROVIDES= $self->data->load('fields/perl-provides', '\s+');
+
+ my $name = $fields->value('Package');
+
+ return
+ unless $PERL_CORE_PROVIDES->recognizes($name);
+
+ my $core_version = $PERL_CORE_PROVIDES->value($name);
+
+ my $no_revision = "$epoch:$upstream";
+ return
+ unless version_check($no_revision);
+
+ $self->hint('package-superseded-by-perl', "with $core_version")
+ if versions_compare($core_version, '>=', $no_revision);
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Perl/Perl4/Prerequisites.pm b/lib/Lintian/Check/Languages/Perl/Perl4/Prerequisites.pm
new file mode 100644
index 0000000..fb5e9be
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Perl/Perl4/Prerequisites.pm
@@ -0,0 +1,124 @@
+# languages/perl/perl4/prerequisites -- lintian check script -*- perl -*-
+#
+# Copyright (C) 1998 Richard Braakman
+# Copyright (C) 2002 Josip Rodin
+# Copyright (C) 2016-2019 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Perl::Perl4::Prerequisites;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use File::Basename;
+use Unicode::UTF8 qw(encode_utf8);
+
+use Lintian::Relation;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+# check for obsolete perl libraries
+const my $PERL4_PREREQUISITES =>
+ 'libperl4-corelibs-perl:any | perl:any (<< 5.12.3-7)';
+
+has satisfies_perl4_prerequisites => (
+ is => 'rw',
+ lazy => 1,
+ default => sub {
+ my ($self) = @_;
+
+ return $self->processable->relation('strong')
+ ->satisfies($PERL4_PREREQUISITES);
+ }
+);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # Consider /usr/src/ scripts as "documentation"
+ # - packages containing /usr/src/ tend to be "-source" .debs
+ # and usually come with overrides
+ # no checks necessary at all for scripts in /usr/share/doc/
+ # unless they are examples
+ return
+ if ($item->name =~ m{^usr/share/doc/} || $item->name =~ m{^usr/src/})
+ && $item->name !~ m{^usr/share/doc/[^/]+/examples/};
+
+ return
+ unless length $item->interpreter;
+
+ my $basename = basename($item->interpreter);
+ return
+ unless $basename eq 'perl';
+
+ return
+ unless $item->is_open_ok;
+
+ open(my $fd, '<', $item->unpacked_path)
+ or die encode_utf8('Cannot open ' . $item->unpacked_path);
+
+ my $position = 1;
+ while (my $line = <$fd>) {
+ if (
+ $line =~m{ (?:do|require)\s+['"] # do/require
+
+ # Huge list of perl4 modules...
+ (abbrev|assert|bigfloat|bigint|bigrat
+ |cacheout|complete|ctime|dotsh|exceptions
+ |fastcwd|find|finddepth|flush|getcwd|getopt
+ |getopts|hostname|importenv|look|newgetopt
+ |open2|open3|pwd|shellwords|stat|syslog
+ |tainted|termcap|timelocal|validate)
+ # ... so they end with ".pl" rather than ".pm"
+ \.pl['"]
+ }xsm
+ ) {
+
+ my $module = "$1.pl";
+
+ my $pointer = $item->pointer($position);
+
+ $self->pointed_hint(
+ 'script-uses-perl4-libs-without-dep',$pointer,
+ "(does not satisfy $PERL4_PREREQUISITES)",$module
+ ) unless $self->satisfies_perl4_prerequisites;
+
+ }
+
+ } continue {
+ ++$position;
+ }
+
+ close $fd;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Perl/Perl5.pm b/lib/Lintian/Check/Languages/Perl/Perl5.pm
new file mode 100644
index 0000000..8b138ab
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Perl/Perl5.pm
@@ -0,0 +1,61 @@
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 1999 Joey Hess
+# Copyright (C) 2000 Sean 'Shaleh' Perry
+# Copyright (C) 2002 Josip Rodin
+# Copyright (C) 2007 Russ Allbery
+# Copyright (C) 2013-2018 Bastien ROUCARIES
+# Copyright (C) 2017-2020 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2020-2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Perl::Perl5;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_patched_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ # Find mentioning of usr/lib/perl5 inside the packaging
+ $self->pointed_hint('mentions-deprecated-usr-lib-perl5-directory',
+ $item->pointer)
+ if $item->basename ne 'changelog'
+ && $item->name =~ m{^ debian/ }sx
+ && $item->name !~ m{^ debian/patches/ }sx
+ && $item->name !~ m{^ debian/ (?:.+\.)? install $}sx
+ && $item->bytes =~ m{^ [^#]* usr/lib/perl5 }msx;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Perl/Yapp.pm b/lib/Lintian/Check/Languages/Perl/Yapp.pm
new file mode 100644
index 0000000..adf3605
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Perl/Yapp.pm
@@ -0,0 +1,55 @@
+# languages/perl/yapp -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2019 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Perl::Yapp;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_patched_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->name =~ /\.pm$/;
+
+ my $bytes = $item->bytes;
+ return
+ unless $bytes;
+
+ $self->pointed_hint('source-contains-prebuilt-yapp-parser', $item->pointer)
+ if $bytes
+ =~ /^#\s+This file was generated using Parse::Yapp version [\d.]+/m;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Php.pm b/lib/Lintian/Check/Languages/Php.pm
new file mode 100644
index 0000000..948a7a3
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Php.pm
@@ -0,0 +1,53 @@
+# languages/php -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Php;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # /etc/php/*/mods-available/*.ini
+ if ( $item->is_file
+ && $item->name =~ m{^etc/php/.*/mods-available/.+\.ini$}) {
+
+ $self->pointed_hint('obsolete-comments-style-in-php-ini',
+ $item->pointer)
+ if $item->decoded_utf8 =~ /^\s*#/m;
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Php/Composer.pm b/lib/Lintian/Check/Languages/Php/Composer.pm
new file mode 100644
index 0000000..142c1e8
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Php/Composer.pm
@@ -0,0 +1,93 @@
+# languages/php/composer -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Php::Composer;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Lintian::Relation;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ my $control = $self->processable->debian_control;
+ my $source_fields = $control->source_fields;
+
+ for my $field (
+ qw(Build-Depends Build-Depends-Indep
+ Build-Conflicts Build-Conflicts-Indep)
+ ) {
+ next
+ unless $source_fields->declares($field);
+
+ my $position = $source_fields->position($field);
+ my $pointer = $control->item->pointer($position);
+
+ my $raw = $source_fields->value($field);
+ my $relation = Lintian::Relation->new->load($raw);
+
+ my $condition = 'composer:any';
+
+ $self->pointed_hint('composer-prerequisite', $pointer, $field,
+ '(in source paragraph)')
+ if $relation->satisfies($condition);
+ }
+
+ for my $installable ($control->installables) {
+ my $installable_fields = $control->installable_fields($installable);
+
+ for my $field (
+ qw(Pre-Depends Depends Recommends Suggests Breaks
+ Conflicts Provides Replaces Enhances)
+ ) {
+ next
+ unless $installable_fields->declares($field);
+
+ my $position = $installable_fields->position($field);
+ my $pointer = $control->item->pointer($position);
+
+ my $relation
+ = $self->processable->binary_relation($installable, $field);
+
+ my $condition = 'composer:any';
+
+ $self->pointed_hint('composer-prerequisite', $pointer, $field,
+ "(in section for $installable)")
+ if $relation->satisfies($condition);
+ }
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Php/Embedded.pm b/lib/Lintian/Check/Languages/Php/Embedded.pm
new file mode 100644
index 0000000..2287f09
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Php/Embedded.pm
@@ -0,0 +1,92 @@
+# languages/php/embedded -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Php::Embedded;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+my $PHP_EXT = qr{(?i)\.(?:php|inc|dtd)$};
+my %PHP_FILES = (
+ 'libphp-adodb' => qr{(?i)/adodb\.inc\.php$},
+ 'smarty3?' => qr{(?i)/Smarty(?:_Compiler)?\.class\.php$},
+ 'libphp-phpmailer' => qr{(?i)/class\.phpmailer(\.(?:php|inc))+$},
+ 'phpsysinfo' =>
+qr{(?i)/phpsysinfo\.dtd|/class\.(?:Linux|(?:Open|Net|Free|)BSD)\.inc\.php$},
+ 'php-openid' => qr{/Auth/(?:OpenID|Yadis/Yadis)\.php$},
+ 'libphp-snoopy' => qr{(?i)/Snoopy\.class\.(?:php|inc)$},
+ 'php-markdown' => qr{(?i)/markdown\.php$},
+ 'php-geshi' => qr{(?i)/geshi\.php$},
+ 'libphp-pclzip' =>qr{(?i)/(?:class[.-])?pclzip\.(?:inc|lib)?\.php$},
+ 'libphp-phplayersmenu' => qr{(?i)/.*layersmenu.*/(lib/)?PHPLIB\.php$},
+ 'libphp-phpsniff' => qr{(?i)/phpSniff\.(?:class|core)\.php$},
+ 'libphp-jabber' => qr{(?i)/(?:class\.)?jabber\.php$},
+ 'libphp-simplepie' =>
+ qr{(?i)/(?:class[\.-])?simplepie(?:\.(?:php|inc))+$},
+ 'libphp-jpgraph' => qr{(?i)/jpgraph\.php$},
+ 'php-fpdf' => qr{(?i)/fpdf\.php$},
+ 'php-getid3' => qr{(?i)/getid3\.(?:lib\.)?(?:\.(?:php|inc))+$},
+ 'php-php-gettext' => qr{(?i)/(?<!pomo/)streams\.php$},
+ 'libphp-magpierss' => qr{(?i)/rss_parse\.(?:php|inc)$},
+ 'php-simpletest' => qr{(?i)/unit_tester\.php$},
+ 'libsparkline-php' => qr{(?i)/Sparkline\.php$},
+ 'libnusoap-php' => qr{(?i)/(?:class\.)?nusoap\.(?:php|inc)$},
+ 'php-htmlpurifier' => qr{(?i)/HTMLPurifier\.php$},
+ # not yet available in unstable:,
+ # 'libphp-ixr' => qr{(?i)/IXR_Library(?:\.inc|\.php)+$},
+ # 'libphp-kses' => qr{(?i)/(?:class\.)?kses\.php$},
+);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ # embedded PHP
+ for my $provider (keys %PHP_FILES) {
+
+ next
+ if $self->processable->name =~ /^$provider$/;
+
+ next
+ unless $item->name =~ /$PHP_FILES{$provider}/;
+
+ $self->pointed_hint('embedded-php-library', $item->pointer,
+ 'please use',$provider);
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Php/Pear.pm b/lib/Lintian/Check/Languages/Php/Pear.pm
new file mode 100644
index 0000000..b73b268
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Php/Pear.pm
@@ -0,0 +1,242 @@
+# langauges/php/pear -- lintian check script -*- perl -*-
+
+# Copyright (C) 2013 Mathieu Parent <math.parent@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Php::Pear;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use List::SomeUtils qw(none);
+use Unicode::UTF8 qw(encode_utf8);
+
+const my $DOLLAR => q{$};
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ # Don't check package if it doesn't contain a .php file
+ if (none { $_->basename =~ m/\.php$/i && !$_->is_dir }
+ @{$self->processable->patched->sorted_list}){
+ return;
+ }
+
+ my $build_depends = $self->processable->relation('Build-Depends');
+ my $package_type = 'unknown';
+
+ # PEAR or PECL package
+ my $package_xml = $self->processable->patched->lookup('package.xml');
+ my $package2_xml = $self->processable->patched->lookup('package2.xml');
+
+ my $debian_control = $self->processable->debian_control;
+
+ if (defined($package_xml) || defined($package2_xml)) {
+ # Checking source builddep
+ if (!$build_depends->satisfies('pkg-php-tools')) {
+ $self->hint('pear-package-without-pkg-php-tools-builddep');
+
+ } else {
+ # Checking first binary relations
+ my @binaries = $debian_control->installables;
+ my $binary = $binaries[0];
+
+ my $depends
+ = $self->processable->binary_relation($binary, 'Depends');
+ my $recommends
+ = $self->processable->binary_relation($binary, 'Recommends');
+ my $breaks= $self->processable->binary_relation($binary, 'Breaks');
+
+ $self->hint('pear-package-but-missing-dependency', 'Depends')
+ unless $depends->satisfies($DOLLAR . '{phppear:Debian-Depends}');
+
+ $self->hint('pear-package-but-missing-dependency','Recommends')
+ unless $recommends->satisfies(
+ $DOLLAR . '{phppear:Debian-Recommends}');
+
+ $self->hint('pear-package-but-missing-dependency', 'Breaks')
+ unless $breaks->satisfies($DOLLAR . '{phppear:Debian-Breaks}');
+
+ # checking description
+ my $description
+ = $debian_control->installable_fields($binary)
+ ->untrimmed_value('Description');
+
+ $self->hint(
+ 'pear-package-not-using-substvar',
+ $DOLLAR . '{phppear:summary}'
+ )if $description !~ /\$\{phppear:summary\}/;
+
+ $self->hint(
+ 'pear-package-not-using-substvar',
+ $DOLLAR . '{phppear:description}'
+ )if $description !~ /\$\{phppear:description\}/;
+
+ if (defined $package_xml && $package_xml->is_regular_file) {
+
+ # Wild guess package type as in
+ # PEAR_PackageFile_v2::getPackageType()
+ open(my $package_xml_fd, '<', $package_xml->unpacked_path)
+ or die encode_utf8(
+ 'Cannot open ' . $package_xml->unpacked_path);
+
+ while (my $line = <$package_xml_fd>) {
+ if (
+ $line =~ m{\A \s* <
+ (php|extsrc|extbin|zendextsrc|zendextbin)
+ release \s* /? > }xsm
+ ) {
+ $package_type = $1;
+ last;
+ }
+ if ($line =~ /^\s*<bundle\s*\/?>/){
+ $package_type = 'bundle';
+ last;
+ }
+ }
+
+ close $package_xml_fd;
+
+ if ($package_type eq 'extsrc') { # PECL package
+ if (!$build_depends->satisfies('php-dev')) {
+
+ $self->pointed_hint(
+ 'pecl-package-requires-build-dependency',
+ $package_xml->pointer,'php-dev');
+ }
+
+ if (!$build_depends->satisfies('dh-php')) {
+ $self->pointed_hint(
+ 'pecl-package-requires-build-dependency',
+ $package_xml->pointer,'dh-php');
+ }
+ }
+ }
+ }
+ }
+
+ # PEAR channel
+ my $channel_xml = $self->processable->patched->lookup('channel.xml');
+ $self->pointed_hint('pear-channel-without-pkg-php-tools-builddep',
+ $channel_xml->pointer)
+ if defined $channel_xml
+ && !$build_depends->satisfies('pkg-php-tools');
+
+ # Composer package
+ my $composer_json = $self->processable->patched->lookup('composer.json');
+ $self->pointed_hint('composer-package-without-pkg-php-tools-builddep',
+ $composer_json->pointer)
+ if defined $composer_json
+ && !($build_depends->satisfies('pkg-php-tools')
+ || $build_depends->satisfies('dh-sequence-phpcomposer'))
+ && !defined $package_xml
+ && !defined $package2_xml;
+
+ # Check rules
+ if (
+ $build_depends->satisfies('pkg-php-tools')
+ && ( defined $package_xml
+ || defined $package2_xml
+ || defined $channel_xml
+ || defined $composer_json)
+ ) {
+ my $rules = $self->processable->patched->resolve_path('debian/rules');
+ if (defined $rules && $rules->is_open_ok) {
+
+ my $has_buildsystem_phppear = 0;
+ my $has_addon_phppear = 0;
+ my $has_addon_phpcomposer= 0;
+ my $has_addon_php = 0;
+
+ open(my $rules_fd, '<', $rules->unpacked_path)
+ or die encode_utf8('Cannot open ' . $rules->unpacked_path);
+
+ while (my $line = <$rules_fd>) {
+
+ while ($line =~ s/\\$// && defined(my $cont = <$rules_fd>)) {
+ $line .= $cont;
+ }
+
+ next
+ if $line =~ /^\s*\#/;
+
+ $has_buildsystem_phppear = 1
+ if $line
+ =~ /^\t\s*dh\s.*--buildsystem(?:=|\s+)(?:\S+,)*phppear(?:,\S+)*\s/;
+
+ $has_addon_phppear = 1
+ if $line
+ =~ /^\t\s*dh\s.*--with(?:=|\s+)(?:\S+,)*phppear(?:,\S+)*\s/;
+
+ $has_addon_phpcomposer = 1
+ if $line
+ =~ /^\t\s*dh\s.*--with(?:=|\s+)(?:\S+,)*phpcomposer(?:,\S+)*\s/;
+
+ $has_addon_php = 1
+ if $line
+ =~ /^\t\s*dh\s.*--with(?:=|\s+)(?:\S+,)*php(?:,\S+)*\s/;
+ }
+
+ close $rules_fd;
+
+ if ( defined $package_xml
+ || defined $package2_xml
+ || defined $channel_xml) {
+
+ $self->pointed_hint('missing-pkg-php-tools-buildsystem',
+ $rules->pointer, 'phppear')
+ unless $has_buildsystem_phppear;
+
+ $self->pointed_hint('missing-pkg-php-tools-addon',
+ $rules->pointer, 'phppear')
+ unless $has_addon_phppear;
+
+ $self->pointed_hint('missing-pkg-php-tools-addon',
+ $rules->pointer, 'php')
+ if $package_type eq 'extsrc'
+ && !$has_addon_php;
+ }
+
+ if ( !defined $package_xml
+ && !defined $package2_xml
+ && defined $composer_json) {
+
+ $self->pointed_hint('missing-pkg-php-tools-addon',
+ $rules->pointer, 'phpcomposer')
+ unless $has_addon_phpcomposer;
+ }
+ }
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Php/Pear/Embedded.pm b/lib/Lintian/Check/Languages/Php/Pear/Embedded.pm
new file mode 100644
index 0000000..dfb1268
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Php/Pear/Embedded.pm
@@ -0,0 +1,92 @@
+# languages/php/pear/embedded -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Php::Pear::Embedded;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+my $PEAR_MAGIC = qr{pear[/.]};
+my $PEAR_EXT = qr{(?i)\.php$};
+my %PEAR_FILES = (
+ 'php-auth' => qr{/Auth} . $PEAR_EXT,
+ 'php-auth-http' => qr{/Auth/HTTP} . $PEAR_EXT,
+ 'php-benchmark' => qr{/Benchmark/(?:Timer|Profiler|Iterate)}
+ . $PEAR_EXT,
+ 'php-http' => qr{(?<!/Auth)/HTTP} . $PEAR_EXT,
+ 'php-cache' => qr{/Cache} . $PEAR_EXT,
+ 'php-cache-lite' => qr{/Cache/Lite} . $PEAR_EXT,
+ 'php-compat' => qr{/Compat} . $PEAR_EXT,
+ 'php-config' => qr{/Config} . $PEAR_EXT,
+ 'php-crypt-cbc' => qr{/CBC} . $PEAR_EXT,
+ 'php-date' => qr{/Date} . $PEAR_EXT,
+ 'php-db' => qr{(?<!/Container)/DB} . $PEAR_EXT,
+ 'php-file' => qr{(?<!/Container)/File} . $PEAR_EXT,
+ 'php-log' =>
+ qr{(?:/Log/(?:file|error_log|null|syslog|sql\w*)|/Log)} . $PEAR_EXT,
+ 'php-mail' => qr{/Mail} . $PEAR_EXT,
+ 'php-mail-mime' => qr{(?i)/mime(Part)?} . $PEAR_EXT,
+ 'php-mail-mimedecode' => qr{/mimeDecode} . $PEAR_EXT,
+ 'php-net-ftp' => qr{/FTP} . $PEAR_EXT,
+ 'php-net-imap' => qr{(?<!/Container)/IMAP} . $PEAR_EXT,
+ 'php-net-ldap' => qr{(?<!/Container)/LDAP} . $PEAR_EXT,
+ 'php-net-smtp' => qr{/SMTP} . $PEAR_EXT,
+ 'php-net-socket' => qr{(?<!/FTP)/Socket} . $PEAR_EXT,
+);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ # embedded PEAR
+ for my $provider (keys %PEAR_FILES) {
+
+ next
+ if $self->processable->name =~ /^$provider$/;
+
+ next
+ unless $item->name =~ /$PEAR_FILES{$provider}/;
+
+ next
+ unless length $item->bytes_match($PEAR_MAGIC);
+
+ $self->pointed_hint('embedded-pear-module', $item->pointer,
+ 'please use',$provider);
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python.pm b/lib/Lintian/Check/Languages/Python.pm
new file mode 100644
index 0000000..089fce4
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python.pm
@@ -0,0 +1,516 @@
+# languages/python -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2016 Chris Lamb
+# Copyright (C) 2020 Louis-Philippe Veronneau <pollo@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use List::SomeUtils qw(any none);
+use Unicode::UTF8 qw(encode_utf8);
+
+use Lintian::Relation;
+use Lintian::Relation::Version qw(versions_lte);
+
+const my $EMPTY => q{};
+const my $ARROW => q{ -> };
+const my $DOLLAR => q{$};
+
+const my $PYTHON3_MAJOR => 3;
+const my $PYTHON2_MIGRATION_MAJOR => 2;
+const my $PYTHON2_MIGRATION_MINOR => 6;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+my @FIELDS = qw(Depends Pre-Depends Recommends Suggests);
+my @IGNORE = qw(-dev$ -docs?$ -common$ -tools$);
+my @PYTHON2 = qw(python2:any python2.7:any python2-dev:any);
+my @PYTHON3 = qw(python3:any python3-dev:any);
+
+my %DJANGO_PACKAGES = (
+ '^python3-django-' => 'python3-django',
+ '^python2?-django-' => 'python-django',
+);
+
+my %REQUIRED_DEPENDS = (
+ 'python2' => 'python2-minimal:any | python2:any',
+ 'python3' => 'python3-minimal:any | python3:any',
+);
+
+my %MISMATCHED_SUBSTVARS = (
+ '^python3-.+' => $DOLLAR . '{python:Depends}',
+ '^python2?-.+' => $DOLLAR . '{python3:Depends}',
+);
+
+has ALLOWED_PYTHON_FILES => (
+ is => 'rw',
+ lazy => 1,
+ default => sub {
+ my ($self) = @_;
+
+ return $self->data->load('files/allowed-python-files');
+ }
+);
+has GENERIC_PYTHON_MODULES => (
+ is => 'rw',
+ lazy => 1,
+ default => sub {
+ my ($self) = @_;
+
+ return $self->data->load('files/generic-python-modules');
+ }
+);
+
+my @VERSION_FIELDS = qw(X-Python-Version XS-Python-Version X-Python3-Version);
+
+has correct_location => (is => 'rw', default => sub { {} });
+
+sub source {
+ my ($self) = @_;
+
+ my @installable_names = $self->processable->debian_control->installables;
+ for my $installable_name (@installable_names) {
+ # Python 2 modules
+ if ($installable_name =~ /^python2?-(.*)$/) {
+ my $suffix = $1;
+
+ next
+ if any { $installable_name =~ /$_/ } @IGNORE;
+
+ next
+ if any { $_ eq "python3-${suffix}" } @installable_names;
+
+ # Don't trigger if we ship any Python 3 module
+ next
+ if any {
+ $self->processable->binary_relation($_, 'all')
+ ->satisfies($DOLLAR . '{python3:Depends}')
+ }@installable_names;
+
+ $self->hint('python-foo-but-no-python3-foo', $installable_name);
+ }
+ }
+
+ my $build_all = $self->processable->relation('Build-Depends-All');
+ $self->hint('build-depends-on-python-sphinx-only')
+ if $build_all->satisfies('python-sphinx')
+ && !$build_all->satisfies('python3-sphinx');
+
+ $self->hint(
+ 'alternatively-build-depends-on-python-sphinx-and-python3-sphinx')
+ if $self->processable->fields->value('Build-Depends')
+ =~ /\bpython-sphinx\s+\|\s+python3-sphinx\b/;
+
+ my $debian_control = $self->processable->debian_control;
+
+ # Mismatched substvars
+ for my $regex (keys %MISMATCHED_SUBSTVARS) {
+ my $substvar = $MISMATCHED_SUBSTVARS{$regex};
+
+ for my $installable_name ($debian_control->installables) {
+
+ next
+ if any { $installable_name =~ /$_/ } @IGNORE;
+
+ next
+ if $installable_name !~ qr/$regex/;
+
+ $self->hint('mismatched-python-substvar', $installable_name,
+ $substvar)
+ if $self->processable->binary_relation($installable_name, 'all')
+ ->satisfies($substvar);
+ }
+ }
+
+ my $VERSIONS = $self->data->load('python/versions', qr/\s*=\s*/);
+
+ for my $field (@VERSION_FIELDS) {
+
+ next
+ unless $debian_control->source_fields->declares($field);
+
+ my $pyversion= $debian_control->source_fields->value($field);
+
+ my @valid = (
+ ['\d+\.\d+', '\d+\.\d+'],['\d+\.\d+'],
+ ['\>=\s*\d+\.\d+', '\<\<\s*\d+\.\d+'],['\>=\s*\d+\.\d+'],
+ ['current', '\>=\s*\d+\.\d+'],['current'],
+ ['all']
+ );
+
+ my @pyversion = split(/\s*,\s*/, $pyversion);
+
+ if ($pyversion =~ m/^current/) {
+ $self->hint('python-version-current-is-deprecated', $field);
+ }
+
+ if (@pyversion > 2) {
+ if (any { !/^\d+\.\d+$/ } @pyversion) {
+ $self->hint('malformed-python-version', $field, $pyversion);
+ }
+ } else {
+ my $okay = 0;
+ for my $rule (@valid) {
+ if (
+ $pyversion[0] =~ /^$rule->[0]$/
+ && (
+ (
+ $pyversion[1]
+ && $rule->[1]
+ && $pyversion[1] =~ /^$rule->[1]$/
+ )
+ || (!$pyversion[1] && !$rule->[1])
+ )
+ ) {
+ $okay = 1;
+ last;
+ }
+ }
+ $self->hint('malformed-python-version', $field, $pyversion)
+ unless $okay;
+ }
+
+ if ($pyversion =~ /\b(([23])\.\d+)$/) {
+ my ($v, $major) = ($1, $2);
+ my $old = $VERSIONS->value("old-python$major");
+ my $ancient = $VERSIONS->value("ancient-python$major");
+
+ if (versions_lte($v, $ancient)) {
+ $self->hint('ancient-python-version-field', $field, $v);
+ } elsif (versions_lte($v, $old)) {
+ $self->hint('old-python-version-field', $field, $v);
+ }
+ }
+ }
+
+ $self->hint('source-package-encodes-python-version')
+ if $self->processable->name =~ m/^python\d-/
+ && $self->processable->name ne 'python3-defaults';
+
+ my $build_depends = Lintian::Relation->new;
+ $build_depends->load_norestriction(
+ $self->processable->fields->value('Build-Depends'));
+
+ my $pyproject= $self->processable->patched->resolve_path('pyproject.toml');
+ if (defined $pyproject && $pyproject->is_open_ok) {
+
+ my %PYPROJECT_PREREQUISITES = (
+ 'poetry.core.masonry.api' => 'python3-poetry-core:any',
+ 'flit_core.buildapi' => 'flit:any',
+ 'setuptools.build_meta' => 'python3-setuptools:any',
+ 'pdm.pep517.api' => 'python3-pdm-pep517:any',
+ 'hatchling.build' => 'python3-hatchling:any',
+ 'mesonpy' => 'python3-mesonpy:any',
+ 'sipbuild.api' => 'python3-sipbuild:any'
+ );
+
+ open(my $fd, '<', $pyproject->unpacked_path)
+ or die encode_utf8('Cannot open ' . $pyproject->unpacked_path);
+
+ my $position = 1;
+ while (my $line = <$fd>) {
+
+ my $pointer = $pyproject->pointer($position);
+
+ # In theory, TOML only uses double quotes. In practice, that's not
+ # true and only matching for double quotes introduce false negatives
+ if ($line =~ m{^ \s* build-backend \s* = \s* "([^"]+)" }x
+ || $line =~ m{^ \s* build-backend \s* = \s* '([^"]+)' }x) {
+
+ my $backend = $1;
+
+ $self->pointed_hint('uses-poetry-cli', $pointer)
+ if $backend eq 'poetry.core.masonry.api'
+ && $build_depends->satisfies('python3-poetry:any')
+ && !$build_depends->satisfies('python3-poetry-core:any');
+
+ $self->pointed_hint('uses-pdm-cli', $pointer)
+ if $backend eq 'pdm.pep517.api'
+ && $build_depends->satisfies('python3-pdm:any')
+ && !$build_depends->satisfies('python3-pdm-pep517:any');
+
+ if (exists $PYPROJECT_PREREQUISITES{$backend}) {
+
+ my $prerequisites = $PYPROJECT_PREREQUISITES{$backend}
+ . ', pybuild-plugin-pyproject:any';
+
+ $self->pointed_hint(
+ 'missing-prerequisite-for-pyproject-backend',
+ $pointer, $backend,"(does not satisfy $prerequisites)")
+ if !$build_all->satisfies($prerequisites);
+ }
+ }
+
+ } continue {
+ ++$position;
+ }
+
+ close $fd;
+ }
+
+ return;
+}
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # .pyc/.pyo (compiled Python files)
+ # skip any file installed inside a __pycache__ directory
+ # - we have a separate check for that directory.
+ $self->pointed_hint('package-installs-python-bytecode', $item->pointer)
+ if $item->name =~ /\.py[co]$/
+ && $item->name !~ m{/__pycache__/};
+
+ # __pycache__ (directory for pyc/pyo files)
+ $self->pointed_hint('package-installs-python-pycache-dir', $item->pointer)
+ if $item->is_dir
+ && $item->name =~ m{/__pycache__/};
+
+ if ( $item->is_file
+ && $item->name
+ =~ m{^usr/lib/debug/usr/lib/pyshared/(python\d?(?:\.\d+))/(.+)$}) {
+
+ my $correct = "usr/lib/debug/usr/lib/pymodules/$1/$2";
+ $self->pointed_hint('python-debug-in-wrong-location',
+ $item->pointer, "better: $correct");
+ }
+
+ # .egg (Python egg files)
+ $self->pointed_hint('package-installs-python-egg', $item->pointer)
+ if $item->name =~ /\.egg$/
+ && ( $item->name =~ m{^usr/lib/python\d+(?:\.\d+/)}
+ || $item->name =~ m{^usr/lib/pyshared}
+ || $item->name =~ m{^usr/share/});
+
+ # /usr/lib/site-python
+ $self->pointed_hint('file-in-usr-lib-site-python', $item->pointer)
+ if $item->name =~ m{^usr/lib/site-python/\S};
+
+ # pythonX.Y extensions
+ if ( $item->name =~ m{^usr/lib/python\d\.\d/\S}
+ && $item->name !~ m{^usr/lib/python\d\.\d/(?:site|dist)-packages/}){
+
+ $self->pointed_hint('third-party-package-in-python-dir',$item->pointer)
+ unless $self->processable->source_name =~ m/^python(?:\d\.\d)?$/
+ || $self->processable->source_name =~ m{\A python\d?-
+ (?:stdlib-extensions|profiler|old-doctools) \Z}xsm;
+ }
+
+ # ---------------- Python file locations
+ # - The Python people kindly provided the following table.
+ # good:
+ # /usr/lib/python2.5/site-packages/
+ # /usr/lib/python2.6/dist-packages/
+ # /usr/lib/python2.7/dist-packages/
+ # /usr/lib/python3/dist-packages/
+ #
+ # bad:
+ # /usr/lib/python2.5/dist-packages/
+ # /usr/lib/python2.6/site-packages/
+ # /usr/lib/python2.7/site-packages/
+ # /usr/lib/python3.*/*-packages/
+ if (
+ $item->name =~ m{\A
+ (usr/lib/debug/)?
+ usr/lib/python(\d+(?:\.\d+)?)/
+ ((?:site|dist)-packages)/(.+)
+ \Z}xsm
+ ){
+ my ($debug, $pyver, $actual_package_dir, $relative) = ($1, $2, $3, $4);
+ $debug //= $EMPTY;
+
+ my ($pmaj, $pmin) = split(m{\.}, $pyver, 2);
+ $pmin //= 0;
+
+ next
+ if $pmaj < $PYTHON2_MIGRATION_MAJOR;
+
+ my ($module_name) = ($relative =~ m{^([^/]+)});
+
+ my $actual_python_libpath = "usr/lib/python$pyver/";
+ my $specified_python_libpath = "usr/lib/python$pmaj/";
+
+ # for python 2.X, folder was python2.X and not python2
+ $specified_python_libpath = $actual_python_libpath
+ if $pmaj < $PYTHON3_MAJOR;
+
+ my $specified_package_dir = 'dist-packages';
+
+ # python 2.4 and 2.5
+ $specified_package_dir = 'site-packages'
+ if $pmaj == $PYTHON2_MIGRATION_MAJOR
+ && $pmin < $PYTHON2_MIGRATION_MINOR;
+
+ my $actual_module_path
+ = $debug. $actual_python_libpath. "$actual_package_dir/$module_name";
+ my $specified_module_path
+ = $debug
+ . $specified_python_libpath
+ . "$specified_package_dir/$module_name";
+
+ $self->correct_location->{$actual_module_path} = $specified_module_path
+ unless $actual_module_path eq $specified_module_path;
+
+ for my $regex ($self->GENERIC_PYTHON_MODULES->all) {
+ $self->pointed_hint('python-module-has-overly-generic-name',
+ $item->pointer, "($1)")
+ if $relative =~ m{^($regex)(?:\.py|/__init__\.py)$}i;
+ }
+
+ $self->pointed_hint('unknown-file-in-python-module-directory',
+ $item->pointer)
+ if $item->is_file
+ && $relative eq $item->basename # "top-level"
+ &&!$self->ALLOWED_PYTHON_FILES->matches_any($item->basename, 'i');
+ }
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ $self->hint(
+ 'python-module-in-wrong-location',
+ $_ . $ARROW . $self->correct_location->{$_}
+ )for keys %{$self->correct_location};
+
+ my $deps
+ = $self->processable->relation('all')
+ ->logical_and($self->processable->relation('Provides'),
+ $self->processable->name);
+
+ my @entries
+ = $self->processable->changelog
+ ? @{$self->processable->changelog->entries}
+ : ();
+
+ # Check for missing dependencies
+ if ($self->processable->name !~ /-dbg$/) {
+ for my $item (@{$self->processable->installed->sorted_list}) {
+
+ if ( $item->is_file
+ && $item->name
+ =~ m{^usr/lib/(?<version>python[23])[\d.]*/(?:site|dist)-packages}
+ && !$deps->satisfies($REQUIRED_DEPENDS{$+{version}})) {
+
+ $self->hint('python-package-missing-depends-on-python');
+
+ last;
+ }
+ }
+ }
+
+ # Check for duplicate dependencies
+ for my $field (@FIELDS) {
+ my $dep = $self->processable->relation($field);
+ FIELD: for my $py2 (@PYTHON2) {
+ for my $py3 (@PYTHON3) {
+
+ if ($dep->satisfies($py2) && $dep->satisfies($py3)) {
+ $self->hint('depends-on-python2-and-python3',
+ $field, "(satisfies $py2, $py3)");
+ last FIELD;
+ }
+ }
+ }
+ }
+
+ my $pkg = $self->processable->name;
+
+ # Python 2 modules
+ $self->hint('new-package-should-not-package-python2-module',
+ $self->processable->name)
+ if $self->processable->name =~ / ^ python2? - /msx
+ && (none { $pkg =~ m{ $_ }x } @IGNORE)
+ && @entries == 1
+ && $entries[0]->Changes
+ !~ / \b python [ ]? 2 (?:[.]x)? [ ] (?:variant|version) \b /imsx
+ && $entries[0]->Changes !~ / \Q$pkg\E /msx;
+
+ # Python applications
+ if ($self->processable->name !~ /^python[23]?-/
+ && (none { $_ eq $self->processable->name } @PYTHON2)) {
+ for my $field (@FIELDS) {
+ for my $dep (@PYTHON2) {
+
+ $self->hint(
+ 'dependency-on-python-version-marked-for-end-of-life',
+ $field, "(satisfies $dep)")
+ if $self->processable->relation($field)->satisfies($dep);
+ }
+ }
+ }
+
+ # Django modules
+ for my $regex (keys %DJANGO_PACKAGES) {
+ my $basepkg = $DJANGO_PACKAGES{$regex};
+
+ next
+ if $self->processable->name !~ /$regex/;
+
+ next
+ if any { $self->processable->name =~ /$_/ } @IGNORE;
+
+ $self->hint('django-package-does-not-depend-on-django', $basepkg)
+ unless $self->processable->relation('strong')->satisfies($basepkg);
+ }
+
+ if (
+ $self->processable->name =~ /^python([23]?)-/
+ && (none { $self->processable->name =~ /$_/ } @IGNORE)
+ ) {
+ my $version = $1 || '2'; # Assume python-foo is a Python 2.x package
+ my @prefixes = ($version eq '2') ? 'python3' : qw(python python2);
+
+ for my $field (@FIELDS) {
+ for my $prefix (@prefixes) {
+
+ my $visit = sub {
+ my $rel = $_;
+ return if any { $rel =~ /$_/ } @IGNORE;
+ $self->hint(
+'python-package-depends-on-package-from-other-python-variant',
+ "$field: $rel"
+ ) if /^$prefix-/;
+ };
+
+ $self->processable->relation($field)
+ ->visit($visit, Lintian::Relation::VISIT_PRED_NAME);
+ }
+ }
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/BogusPrerequisites.pm b/lib/Lintian/Check/Languages/Python/BogusPrerequisites.pm
new file mode 100644
index 0000000..fe2df7f
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/BogusPrerequisites.pm
@@ -0,0 +1,88 @@
+# languages/python/bogus-prerequisites -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::BogusPrerequisites;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub installable {
+ my ($self) = @_;
+
+ $self->what_is_python($self->processable->source_name,
+ qw{Depends Pre-Depends Recommends});
+
+ return;
+}
+
+sub source {
+ my ($self) = @_;
+
+ $self->what_is_python($self->processable->name,
+ qw{Build-Depends Build-Depends-Indep Build-Depends-Arch});
+
+ return;
+}
+
+sub what_is_python {
+ my ($self, $source, @fields) = @_;
+
+ # see Bug#973011
+ my @WHAT_IS_PYTHON = qw(
+ python-is-python2:any
+ python-dev-is-python2:any
+ python-is-python3:any
+ python-dev-is-python3:any
+ );
+
+ my %BOGUS_PREREQUISITES;
+
+ unless ($source eq 'what-is-python') {
+
+ for my $unwanted (@WHAT_IS_PYTHON) {
+
+ $BOGUS_PREREQUISITES{$unwanted}
+ = [grep {$self->processable->relation($_)->satisfies($unwanted)}
+ @fields];
+ }
+ }
+
+ for my $unwanted (keys %BOGUS_PREREQUISITES) {
+
+ $self->hint('bogus-python-prerequisite', $_, "(satisfies $unwanted)")
+ for @{$BOGUS_PREREQUISITES{$unwanted}};
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/DistOverrides.pm b/lib/Lintian/Check/Languages/Python/DistOverrides.pm
new file mode 100644
index 0000000..2dadeb6
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/DistOverrides.pm
@@ -0,0 +1,80 @@
+# languages/python/dist-overrides -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::DistOverrides;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use List::SomeUtils qw(uniq);
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+const my $SPACE => q{ };
+
+sub source {
+ my ($self) = @_;
+
+ my $override_file
+ = $self->processable->patched->resolve_path('debian/py3dist-overrides');
+ return
+ unless defined $override_file;
+
+ my $contents = $override_file->decoded_utf8;
+ return
+ unless length $contents;
+
+ # strip comments
+ $contents =~ s/^\s*\#.*$//mg;
+
+ # strip empty lines
+ $contents =~ s/^\s*$//mg;
+
+ # trim leading spaces
+ $contents =~ s/^\s*//mg;
+
+ my @lines = split(/\n/, $contents);
+
+ # get first component from each line
+ my @identifiers
+ = grep { defined } map { (split($SPACE, $_, 2))[0] } @lines;
+
+ my %count;
+ $count{$_}++ for @identifiers;
+
+ my @duplicates = grep { $count{$_} > 1 } uniq @identifiers;
+
+ $self->hint('duplicate-p3dist-override', $_) for @duplicates;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/Distutils.pm b/lib/Lintian/Check/Languages/Python/Distutils.pm
new file mode 100644
index 0000000..cbc30ce
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/Distutils.pm
@@ -0,0 +1,77 @@
+# languages/python/distutils -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2022 Louis-Philippe VĂ©ronneau <pollo@debian.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::Distutils;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+my $PYTHON3_DEPEND
+ = 'python3:any | python3-dev:any | python3-all:any | python3-all-dev:any';
+
+sub visit_patched_files {
+ my ($self, $item) = @_;
+
+ my $build_all = $self->processable->relation('Build-Depends-All');
+
+ # Skip if the package doesn't depend on python
+ return
+ unless $build_all->satisfies($PYTHON3_DEPEND);
+
+ # Skip if it's not a python file
+ return
+ unless $item->name =~ /\.py$/;
+
+ # Skip if we can't open the file
+ return
+ unless $item->is_open_ok;
+
+ open(my $fd, '<', $item->unpacked_path)
+ or die encode_utf8('Cannot open ' . $item->unpacked_path);
+
+ my $position = 1;
+ while (my $line = <$fd>) {
+
+ my $pointer = $item->pointer($position);
+
+ $self->pointed_hint('uses-python-distutils', $pointer)
+ if $line =~ m{^from distutils} || $line =~ m{^import distutils};
+ } continue {
+ ++$position;
+ }
+
+ close $fd;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/Feedparser.pm b/lib/Lintian/Check/Languages/Python/Feedparser.pm
new file mode 100644
index 0000000..da716e7
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/Feedparser.pm
@@ -0,0 +1,54 @@
+# languages/python/feedparser -- lintian check script -*- perl -*-
+
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::Feedparser;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ # embedded Feedparser library
+ $self->pointed_hint('embedded-feedparser-library', $item->pointer)
+ if $item->name =~ m{ / feedparser[.]py $}x
+ && $item->bytes =~ /Universal feed parser/
+ && $self->processable->source_name ne 'feedparser';
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/Homepage.pm b/lib/Lintian/Check/Languages/Python/Homepage.pm
new file mode 100644
index 0000000..18a0470
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/Homepage.pm
@@ -0,0 +1,59 @@
+# languages/python/homepage -- lintian check script (rewrite) -*- perl -*-
+#
+# Copyright (C) 2021 Felix Lechner
+#
+# Parts of the code were taken from the old check script, which
+# was Copyright (C) 1998 Richard Braakman (also licensed under the
+# GPL 2 or higher)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::Homepage;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ my $fields = $self->processable->fields;
+ if ($fields->declares('Homepage')) {
+
+ my $homepage = $fields->value('Homepage');
+
+ # see Bug#981932
+ $self->hint('pypi-homepage', $homepage)
+ if $homepage
+ =~ m{^http s? :// (?:www [.])? pypi (:?[.] python)? [.] org/}isx;
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/Obsolete.pm b/lib/Lintian/Check/Languages/Python/Obsolete.pm
new file mode 100644
index 0000000..e810faa
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/Obsolete.pm
@@ -0,0 +1,63 @@
+# languages/python/obsolete -- lintian check script -*- perl -*-
+#
+# Copyright (C) 1999 Joey Hess
+# Copyright (C) 2000 Sean 'Shaleh' Perry
+# Copyright (C) 2002 Josip Rodin
+# Copyright (C) 2007 Russ Allbery
+# Copyright (C) 2013-2018 Bastien ROUCARIES
+# Copyright (C) 2017-2020 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2020 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::Obsolete;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ my $pycompat= $self->processable->patched->resolve_path('debian/pycompat');
+
+ $self->pointed_hint('debian-pycompat-is-obsolete', $pycompat->pointer)
+ if defined $pycompat
+ && $pycompat->is_file;
+
+ my $pyversions
+ = $self->processable->patched->resolve_path('debian/pyversions');
+
+ $self->pointed_hint('debian-pyversions-is-obsolete', $pyversions->pointer)
+ if defined $pyversions
+ && $pyversions->is_file;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Python/Scripts.pm b/lib/Lintian/Check/Languages/Python/Scripts.pm
new file mode 100644
index 0000000..988b915
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Python/Scripts.pm
@@ -0,0 +1,54 @@
+# languages/python/scripts -- lintian check script -*- perl -*-
+#
+# Copyright (C) 2016 Chris Lamb
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Python::Scripts;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->name =~ m{(?:usr/)?bin/[^/]+};
+
+ return
+ unless $item->is_script;
+
+ $self->pointed_hint('script-uses-unversioned-python-in-shebang',
+ $item->pointer)
+ if $item->interpreter =~ m{^(?:/usr/bin/)?python$};
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/R.pm b/lib/Lintian/Check/Languages/R.pm
new file mode 100644
index 0000000..daa8462
--- /dev/null
+++ b/lib/Lintian/Check/Languages/R.pm
@@ -0,0 +1,74 @@
+# Copyright (C) 1998 Christian Schwarz and Richard Braakman
+# Copyright (C) 1999 Joey Hess
+# Copyright (C) 2000 Sean 'Shaleh' Perry
+# Copyright (C) 2002 Josip Rodin
+# Copyright (C) 2007 Russ Allbery
+# Copyright (C) 2013-2018 Bastien ROUCARIES
+# Copyright (C) 2017-2020 Chris Lamb <lamby@debian.org>
+# Copyright (C) 2020-2021 Felix Lechner
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::R;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+
+const my $RDATA_MAGIC_LENGTH => 4;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub visit_patched_files {
+ my ($self, $item) = @_;
+
+ return
+ unless $item->is_file;
+
+ # Ensure we have a README.source for R data files
+ if ( $item->basename =~ /\.(?:rda|Rda|rdata|Rdata|RData)$/
+ && $item->is_open_ok
+ && $item->file_type =~ /gzip compressed data/
+ && !$self->processable->patched->resolve_path('debian/README.source')){
+
+ open(my $fd, '<:gzip', $item->unpacked_path)
+ or die encode_utf8('Cannot open ' . $item->unpacked_path);
+
+ read($fd, my $magic, $RDATA_MAGIC_LENGTH)
+ or die encode_utf8('Cannot read from ' . $item->unpacked_path);
+
+ close($fd);
+
+ $self->pointed_hint('r-data-without-readme-source', $item->pointer)
+ if $magic eq 'RDX2';
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/R/Architecture.pm b/lib/Lintian/Check/Languages/R/Architecture.pm
new file mode 100644
index 0000000..3ee0bd2
--- /dev/null
+++ b/lib/Lintian/Check/Languages/R/Architecture.pm
@@ -0,0 +1,69 @@
+# languages/r/architecture -- lintian check script (rewrite) -*- perl -*-
+#
+# Copyright (C) 2004 Marc Brockschmidt
+# Copyright (C) 2021 Felix Lechner
+#
+# Parts of the code were taken from the old check script, which
+# was Copyright (C) 1998 Richard Braakman (also licensed under the
+# GPL 2 or higher)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::R::Architecture;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+has have_r_files => (is => 'rw', default => 0);
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ return
+ if $item->is_dir;
+
+ $self->have_r_files(1)
+ if $item->name =~ m{^usr/lib/R/.*/DESCRIPTION$}
+ && $item->decoded_utf8 =~ /^NeedsCompilation: no/m;
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ $self->hint('r-package-not-arch-all')
+ if $self->processable->name =~ /^r-(?:cran|bioc|other)-/
+ && $self->have_r_files
+ && $self->processable->fields->value('Architecture') ne 'all';
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/R/SiteLibrary.pm b/lib/Lintian/Check/Languages/R/SiteLibrary.pm
new file mode 100644
index 0000000..1ac6ac9
--- /dev/null
+++ b/lib/Lintian/Check/Languages/R/SiteLibrary.pm
@@ -0,0 +1,71 @@
+# languages/r/site-library -- lintian check script -*- perl -*-
+
+# Copyright (C) 2020 Dylan Aissi
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::R::SiteLibrary;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Lintian::Relation;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+has r_site_libraries => (is => 'rw', default => sub { [] });
+
+sub visit_installed_files {
+ my ($self, $item) = @_;
+
+ # R site libraries
+ if ($item->name =~ m{^usr/lib/R/site-library/(.*)/DESCRIPTION$}) {
+ push(@{$self->r_site_libraries}, $1);
+ }
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ $self->hint('ships-r-site-library', $_) for @{$self->r_site_libraries};
+
+ return
+ unless @{$self->r_site_libraries};
+
+ my $depends = $self->processable->relation('strong');
+
+ # no version allowed for virtual package; no alternatives
+ $self->hint('requires-r-api')
+ unless $depends->matches(qr/^r-api-[\w\d+-.]+$/,
+ Lintian::Relation::VISIT_OR_CLAUSE_FULL);
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Ruby.pm b/lib/Lintian/Check/Languages/Ruby.pm
new file mode 100644
index 0000000..563f740
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Ruby.pm
@@ -0,0 +1,72 @@
+# languages/ruby -- lintian check script (rewrite) -*- perl -*-
+#
+# Copyright (C) 2021 Felix Lechner
+#
+# Parts of the code were taken from the old check script, which
+# was Copyright (C) 1998 Richard Braakman (also licensed under the
+# GPL 2 or higher)
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Ruby;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ my $fields = $self->processable->fields;
+ if ($fields->declares('Homepage')) {
+
+ my $homepage = $fields->value('Homepage');
+
+ # rubygems itself is okay; see Bug#981935
+ $self->hint('rubygem-homepage', $homepage)
+ if $homepage
+ =~ m{^http s? :// (?:www [.])? rubygems [.] org/gems/}isx;
+ }
+
+ return;
+}
+
+sub binary {
+ my ($self) = @_;
+
+ my @prerequisites
+ = $self->processable->fields->trimmed_list('Depends', qr/,/);
+
+ my @ruby_interpreter = grep { / \b ruby-interpreter \b /x } @prerequisites;
+
+ $self->hint('ruby-interpreter-is-deprecated', $_)for @ruby_interpreter;
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et
diff --git a/lib/Lintian/Check/Languages/Rust.pm b/lib/Lintian/Check/Languages/Rust.pm
new file mode 100644
index 0000000..140134f
--- /dev/null
+++ b/lib/Lintian/Check/Languages/Rust.pm
@@ -0,0 +1,69 @@
+# languages/rust -- lintian check script -*- perl -*-
+
+# Copyright (C) 2020 Sylvestre Ledru
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, you can find it on the World Wide
+# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free
+# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
+# MA 02110-1301, USA.
+
+package Lintian::Check::Languages::Rust;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+sub source {
+ my ($self) = @_;
+
+ my $debian_control = $self->processable->debian_control;
+ for my $installable ($debian_control->installables) {
+
+ my $fields = $debian_control->installable_fields($installable);
+ my $extended = $fields->text('Description');
+
+ # drop synopsis
+ $extended =~ s/^ [^\n]* \n //sx;
+
+ $self->hint('rust-boilerplate', $installable)
+ if $extended
+ =~ /^ \QThis package contains the following binaries built from the Rust crate\E /isx;
+ }
+
+ return;
+}
+
+sub installable {
+ my ($self) = @_;
+
+ $self->hint('empty-rust-library-declares-provides')
+ if $self->processable->name =~ /^librust-/
+ && $self->processable->not_just_docs
+ && length $self->processable->fields->value('Provides');
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et