summaryrefslogtreecommitdiffstats
path: root/lib/Lintian/Check/Languages/Java.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Lintian/Check/Languages/Java.pm')
-rw-r--r--lib/Lintian/Check/Languages/Java.pm315
1 files changed, 315 insertions, 0 deletions
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