diff options
Diffstat (limited to 'lib/Lintian/Check/Languages/Java.pm')
-rw-r--r-- | lib/Lintian/Check/Languages/Java.pm | 315 |
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 |