summaryrefslogtreecommitdiffstats
path: root/lib/Lintian/Check/Fields/PackageRelations.pm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Lintian/Check/Fields/PackageRelations.pm')
-rw-r--r--lib/Lintian/Check/Fields/PackageRelations.pm794
1 files changed, 794 insertions, 0 deletions
diff --git a/lib/Lintian/Check/Fields/PackageRelations.pm b/lib/Lintian/Check/Fields/PackageRelations.pm
new file mode 100644
index 0000000..eeb11c0
--- /dev/null
+++ b/lib/Lintian/Check/Fields/PackageRelations.pm
@@ -0,0 +1,794 @@
+# fields/package-relations -- lintian check script (rewrite) -*- perl -*-
+#
+# Copyright (C) 2004 Marc Brockschmidt
+# Copyright (C) 2019-2020 Chris Lamb <lamby@debian.org>
+#
+# 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::Fields::PackageRelations;
+
+use v5.20;
+use warnings;
+use utf8;
+
+use Const::Fast;
+use Dpkg::Version qw(version_check);
+use List::SomeUtils qw(any);
+
+use Lintian::Relation;
+
+use Moo;
+use namespace::clean;
+
+with 'Lintian::Check';
+
+const my $EMPTY => q{};
+const my $EQUAL => q{=};
+const my $VERTICAL_BAR => q{|};
+
+# Still in the archive but shouldn't be the primary Emacs dependency.
+my @obsolete_emacs_versions = qw(21 22 23);
+my @emacs_flavors = ($EMPTY, qw(-el -gtk -nox -lucid));
+my %known_obsolete_emacs;
+for my $version (@obsolete_emacs_versions) {
+ for my $flavor (@emacs_flavors) {
+
+ my $package = 'emacs' . $version . $flavor;
+ $known_obsolete_emacs{$package} = 1;
+ }
+}
+
+my %known_libstdcs = map { $_ => 1 } qw(
+ libstdc++2.9-glibc2.1
+ libstdc++2.10
+ libstdc++2.10-glibc2.2
+ libstdc++3
+ libstdc++3.0
+ libstdc++4
+ libstdc++5
+ libstdc++6
+ lib64stdc++6
+);
+
+my %known_tcls = map { $_ => 1 } qw(
+ tcl74
+ tcl8.0
+ tcl8.2
+ tcl8.3
+ tcl8.4
+ tcl8.5
+);
+
+my %known_tclxs = map { $_ => 1 } qw(
+ tclx76
+ tclx8.0.4
+ tclx8.2
+ tclx8.3
+ tclx8.4
+);
+
+my %known_tks = map { $_ => 1 } qw(
+ tk40
+ tk8.0
+ tk8.2
+ tk8.3
+ tk8.4
+ tk8.5
+);
+
+my %known_libpngs = map { $_ => 1 } qw(
+ libpng12-0
+ libpng2
+ libpng3
+);
+
+my @known_java_pkg = map { qr/$_/ } (
+ 'default-j(?:re|dk)(?:-headless)?',
+ # java-runtime and javaX-runtime alternatives (virtual)
+ 'java\d*-runtime(?:-headless)?',
+ # openjdk-X and sun-javaX
+ '(openjdk-|sun-java)\d+-j(?:re|dk)(?:-headless)?',
+ 'gcj-(?:\d+\.\d+-)?jre(?:-headless)?', 'gcj-(?:\d+\.\d+-)?jdk', # gcj
+ 'gij',
+ 'java-gcj-compat(?:-dev|-headless)?', # deprecated/transitional packages
+ 'kaffe', 'cacao', 'jamvm',
+ 'classpath', # deprecated packages (removed in Squeeze)
+);
+
+# Python development packages that are used almost always just for building
+# architecture-dependent modules. Used to check for unnecessary build
+# dependencies for architecture-independent source packages.
+our $PYTHON_DEV = join(' | ',
+ qw(python3-dev python3-all-dev),
+ map { "python$_-dev:any" } qw(2.7 3 3.7 3.8 3.9));
+
+sub installable {
+ my ($self) = @_;
+
+ my $pkg = $self->processable->name;
+ my $type = $self->processable->type;
+ my $processable = $self->processable;
+ my $group = $self->group;
+
+ my $KNOWN_ESSENTIAL = $self->data->load('fields/essential');
+ my $KNOWN_TOOLCHAIN = $self->data->load('fields/toolchain');
+ my $KNOWN_METAPACKAGES = $self->data->load('fields/metapackages');
+
+ my $DH_ADDONS = $self->data->debhelper_addons;
+ my %DH_ADDONS_VALUES
+ = map { $_ => 1 } map { $DH_ADDONS->installed_by($_) } $DH_ADDONS->all;
+
+ my $OBSOLETE_PACKAGES
+ = $self->data->load('fields/obsolete-packages',qr/\s*=>\s*/);
+
+ my $VIRTUAL_PACKAGES= $self->data->load('fields/virtual-packages');
+
+ my $javalib = 0;
+ my $replaces = $processable->relation('Replaces');
+ my %nag_once;
+ $javalib = 1 if($pkg =~ m/^lib.*-java$/);
+ for my $field (
+ qw(Depends Pre-Depends Recommends Suggests Conflicts Provides Enhances Replaces Breaks)
+ ) {
+ next
+ unless $processable->fields->declares($field);
+
+ # get data and clean it
+ my $data = $processable->fields->unfolded_value($field);
+ my $javadep = 0;
+
+ my (@seen_libstdcs, @seen_tcls, @seen_tclxs,@seen_tks, @seen_libpngs);
+
+ my $is_dep_field
+ = any { $field eq $_ } qw(Depends Pre-Depends Recommends Suggests);
+
+ $self->hint('alternates-not-allowed', $field)
+ if ($data =~ /\|/ && !$is_dep_field);
+ $self->check_field($field, $data) if $is_dep_field;
+
+ for my $dep (split /\s*,\s*/, $data) {
+ my (@alternatives, @seen_obsolete_packages);
+ push @alternatives, [_split_dep($_), $_]
+ for (split /\s*\|\s*/, $dep);
+
+ if ($is_dep_field) {
+ push @seen_libstdcs, $alternatives[0][0]
+ if defined $known_libstdcs{$alternatives[0][0]};
+ push @seen_tcls, $alternatives[0][0]
+ if defined $known_tcls{$alternatives[0][0]};
+ push @seen_tclxs, $alternatives[0][0]
+ if defined $known_tclxs{$alternatives[0][0]};
+ push @seen_tks, $alternatives[0][0]
+ if defined $known_tks{$alternatives[0][0]};
+ push @seen_libpngs, $alternatives[0][0]
+ if defined $known_libpngs{$alternatives[0][0]};
+ }
+
+ # Only for (Pre-)?Depends.
+ $self->hint('virtual-package-depends-without-real-package-depends',
+ "$field: $alternatives[0][0]")
+ if (
+ $VIRTUAL_PACKAGES->recognizes($alternatives[0][0])
+ && ($field eq 'Depends' || $field eq 'Pre-Depends')
+ && ($pkg ne 'base-files' || $alternatives[0][0] ne 'awk')
+ # ignore phpapi- dependencies as adding an
+ # alternative, real, package breaks its purpose
+ && $alternatives[0][0] !~ m/^phpapi-/
+ );
+
+ # Check defaults for transitions. Here, we only care
+ # that the first alternative is current.
+ $self->hint('depends-on-old-emacs', "$field: $alternatives[0][0]")
+ if ( $is_dep_field
+ && $known_obsolete_emacs{$alternatives[0][0]});
+
+ for my $part_d (@alternatives) {
+ my ($d_pkg, $d_march, $d_version, undef, undef, $rest,
+ $part_d_orig)
+ = @{$part_d};
+
+ $self->hint('invalid-versioned-provides', $part_d_orig)
+ if ( $field eq 'Provides'
+ && $d_version->[0]
+ && $d_version->[0] ne $EQUAL);
+
+ $self->hint('bad-provided-package-name', $d_pkg)
+ if $d_pkg !~ /^[a-z0-9][-+\.a-z0-9]+$/;
+
+ $self->hint('breaks-without-version', $part_d_orig)
+ if ( $field eq 'Breaks'
+ && !$d_version->[0]
+ && !$VIRTUAL_PACKAGES->recognizes($d_pkg)
+ && !$replaces->satisfies($part_d_orig));
+
+ $self->hint('conflicts-with-version', $part_d_orig)
+ if ($field eq 'Conflicts' && $d_version->[0]);
+
+ $self->hint('obsolete-relation-form', "$field: $part_d_orig")
+ if ($d_version && any { $d_version->[0] eq $_ }('<', '>'));
+
+ $self->hint('bad-version-in-relation', "$field: $part_d_orig")
+ if ($d_version->[0] && !version_check($d_version->[1]));
+
+ $self->hint('package-relation-with-self',
+ "$field: $part_d_orig")
+ if ($pkg eq $d_pkg)
+ && (!$d_march)
+ && ( $field ne 'Conflicts'
+ && $field ne 'Replaces'
+ && $field ne 'Provides');
+
+ $self->hint('bad-relation', "$field: $part_d_orig") if $rest;
+
+ push @seen_obsolete_packages, [$part_d_orig, $d_pkg]
+ if ( $OBSOLETE_PACKAGES->recognizes($d_pkg)
+ && $is_dep_field);
+
+ $self->hint('depends-on-metapackage', "$field: $part_d_orig")
+ if ( $KNOWN_METAPACKAGES->recognizes($d_pkg)
+ && !$KNOWN_METAPACKAGES->recognizes($pkg)
+ && !$processable->is_transitional
+ && !$processable->is_meta_package
+ && $is_dep_field);
+
+ # diffutils is a special case since diff was
+ # renamed to diffutils, so a dependency on
+ # diffutils effectively is a versioned one.
+ $self->hint(
+ 'depends-on-essential-package-without-using-version',
+ "$field: $part_d_orig")
+ if ( $KNOWN_ESSENTIAL->recognizes($d_pkg)
+ && !$d_version->[0]
+ && $is_dep_field
+ && $d_pkg ne 'diffutils'
+ && $d_pkg ne 'dash');
+
+ $self->hint('package-depends-on-an-x-font-package',
+ "$field: $part_d_orig")
+ if ( $field =~ /^(?:Pre-)?Depends$/
+ && $d_pkg =~ /^xfont.*/
+ && $d_pkg ne 'xfonts-utils'
+ && $d_pkg ne 'xfonts-encodings');
+
+ $self->hint('depends-on-packaging-dev',$field)
+ if (($field =~ /^(?:Pre-)?Depends$/|| $field eq 'Recommends')
+ && $d_pkg eq 'packaging-dev'
+ && !$processable->is_transitional
+ && !$processable->is_meta_package);
+
+ $self->hint('needless-suggest-recommend-libservlet-java',
+ "$d_pkg")
+ if (($field eq 'Recommends' || $field eq 'Suggests')
+ && $d_pkg =~ m/libservlet[\d\.]+-java/);
+
+ $self->hint('needlessly-depends-on-awk', $field)
+ if ( $d_pkg eq 'awk'
+ && !$d_version->[0]
+ && $is_dep_field
+ && $pkg ne 'base-files');
+
+ $self->hint('depends-on-libdb1-compat', $field)
+ if ( $d_pkg eq 'libdb1-compat'
+ && $pkg !~ /^libc(?:6|6.1|0.3)/
+ && $field =~ /^(?:Pre-)?Depends$/);
+
+ $self->hint('depends-on-python-minimal', $field,)
+ if ( $d_pkg =~ /^python[\d.]*-minimal$/
+ && $is_dep_field
+ && $pkg !~ /^python[\d.]*-minimal$/);
+
+ $self->hint('doc-package-depends-on-main-package', $field)
+ if ("$d_pkg-doc" eq $pkg
+ && $field =~ /^(?:Pre-)?Depends$/);
+
+ $self->hint(
+ 'package-relation-with-perl-modules', "$field: $d_pkg"
+ # matches "perl-modules" (<= 5.20) as well as
+ # perl-modules-5.xx (>> 5.20)
+ )
+ if $d_pkg =~ /^perl-modules/
+ && $field ne 'Replaces'
+ && $processable->source_name ne 'perl';
+
+ $self->hint('depends-exclusively-on-makedev', $field,)
+ if ( $field eq 'Depends'
+ && $d_pkg eq 'makedev'
+ && @alternatives == 1);
+
+ $self->hint('lib-recommends-documentation',
+ "$field: $part_d_orig")
+ if ( $field eq 'Recommends'
+ && $pkg =~ m/^lib/
+ && $pkg !~ m/-(?:dev|docs?|tools|bin)$/
+ && $part_d_orig =~ m/-docs?$/);
+
+ $self->hint('binary-package-depends-on-toolchain-package',
+ "$field: $part_d_orig")
+ if $KNOWN_TOOLCHAIN->recognizes($d_pkg)
+ && $is_dep_field
+ && $pkg !~ /^dh-/
+ && $pkg !~ /-(?:source|src)$/
+ && !$processable->is_transitional
+ && !$processable->is_meta_package
+ && !$DH_ADDONS_VALUES{$pkg};
+
+ # default-jdk-doc must depend on openjdk-X-doc (or
+ # classpath-doc) to be useful; other packages
+ # should depend on default-jdk-doc if they want
+ # the Java Core API.
+ $self->hint('depends-on-specific-java-doc-package',$field)
+ if (
+ $is_dep_field
+ && $pkg ne 'default-jdk-doc'
+ && ( $d_pkg eq 'classpath-doc'
+ || $d_pkg =~ /openjdk-\d+-doc/)
+ );
+
+ if ($javalib && $field eq 'Depends'){
+ foreach my $reg (@known_java_pkg){
+ if($d_pkg =~ m/$reg/){
+ $javadep++;
+ last;
+ }
+
+ }
+ }
+ }
+
+ for my $d (@seen_obsolete_packages) {
+ my ($dep, $pkg_name) = @{$d};
+ my $replacement = $OBSOLETE_PACKAGES->value($pkg_name)
+ // $EMPTY;
+ $replacement = ' => ' . $replacement
+ if $replacement ne $EMPTY;
+ if ($pkg_name eq $alternatives[0][0]
+ or scalar @seen_obsolete_packages== scalar @alternatives) {
+ $self->hint(
+ 'depends-on-obsolete-package',
+ "$field: $dep${replacement}"
+ );
+ } else {
+ $self->hint(
+ 'ored-depends-on-obsolete-package',
+ "$field: $dep${replacement}"
+ );
+ }
+ }
+
+ # Only emit the tag if all the alternatives are
+ # JVM/JRE/JDKs
+ # - assume that <some-lib> | openjdk-X-jre-headless
+ # makes sense for now.
+ if (scalar(@alternatives) == $javadep
+ && !exists $nag_once{'needless-dependency-on-jre'}){
+ $nag_once{'needless-dependency-on-jre'} = 1;
+ $self->hint('needless-dependency-on-jre');
+ }
+ }
+ $self->hint('package-depends-on-multiple-libstdc-versions',
+ @seen_libstdcs)
+ if (scalar @seen_libstdcs > 1);
+ $self->hint('package-depends-on-multiple-tcl-versions', @seen_tcls)
+ if (scalar @seen_tcls > 1);
+ $self->hint('package-depends-on-multiple-tclx-versions', @seen_tclxs)
+ if (scalar @seen_tclxs > 1);
+ $self->hint('package-depends-on-multiple-tk-versions', @seen_tks)
+ if (scalar @seen_tks > 1);
+ $self->hint('package-depends-on-multiple-libpng-versions',
+ @seen_libpngs)
+ if (scalar @seen_libpngs > 1);
+ }
+
+ # If Conflicts or Breaks is set, make sure it's not inconsistent with
+ # the other dependency fields.
+ for my $conflict (qw/Conflicts Breaks/) {
+ next
+ unless $processable->fields->declares($conflict);
+
+ for my $field (qw(Depends Pre-Depends Recommends Suggests)) {
+ next
+ unless $processable->fields->declares($field);
+
+ my $relation = $processable->relation($field);
+ for my $package (split /\s*,\s*/,
+ $processable->fields->value($conflict)) {
+
+ $self->hint('conflicts-with-dependency', $field, $package)
+ if $relation->satisfies($package);
+ }
+ }
+ }
+
+ return;
+}
+
+sub source {
+ my ($self) = @_;
+
+ my $pkg = $self->processable->name;
+ my $type = $self->processable->type;
+ my $processable = $self->processable;
+ my $group = $self->group;
+
+ my $KNOWN_ESSENTIAL = $self->data->load('fields/essential');
+ my $KNOWN_METAPACKAGES = $self->data->load('fields/metapackages');
+ my $NO_BUILD_DEPENDS= $self->data->load('fields/no-build-depends');
+ my $known_build_essential
+ = $self->data->load('fields/build-essential-packages');
+ my $KNOWN_BUILD_PROFILES= $self->data->load('fields/build-profiles');
+
+ my $OBSOLETE_PACKAGES
+ = $self->data->load('fields/obsolete-packages',qr/\s*=>\s*/);
+
+ my $VIRTUAL_PACKAGES= $self->data->load('fields/virtual-packages');
+
+ my @binpkgs = $processable->debian_control->installables;
+
+ #Get number of arch-indep packages:
+ my $arch_indep_packages = 0;
+ my $arch_dep_packages = 0;
+
+ for my $binpkg (@binpkgs) {
+ my $arch = $processable->debian_control->installable_fields($binpkg)
+ ->value('Architecture');
+
+ if ($arch eq 'all') {
+ $arch_indep_packages++;
+ } else {
+ $arch_dep_packages++;
+ }
+ }
+
+ $self->hint('build-depends-indep-without-arch-indep')
+ if ( $processable->fields->declares('Build-Depends-Indep')
+ && $arch_indep_packages == 0);
+
+ $self->hint('build-depends-arch-without-arch-dependent-binary')
+ if ( $processable->fields->declares('Build-Depends-Arch')
+ && $arch_dep_packages == 0);
+
+ my %depend;
+ for my $field (
+ qw(Build-Depends Build-Depends-Indep Build-Depends-Arch Build-Conflicts Build-Conflicts-Indep Build-Conflicts-Arch)
+ ) {
+ if ($processable->fields->declares($field)) {
+
+ my $is_dep_field = any { $field eq $_ }
+ qw(Build-Depends Build-Depends-Indep Build-Depends-Arch);
+
+ # get data and clean it
+ my $data = $processable->fields->unfolded_value($field);
+
+ $self->check_field($field, $data);
+ $depend{$field} = $data;
+
+ for my $dep (split /\s*,\s*/, $data) {
+ my (@alternatives, @seen_obsolete_packages);
+ push @alternatives, [_split_dep($_), $_]
+ for (split /\s*\|\s*/, $dep);
+
+ $self->hint(
+ 'virtual-package-depends-without-real-package-depends',
+ "$field: $alternatives[0][0]")
+ if ( $VIRTUAL_PACKAGES->recognizes($alternatives[0][0])
+ && $is_dep_field);
+
+ for my $part_d (@alternatives) {
+ my ($d_pkg, undef, $d_version, $d_arch, $d_restr,
+ $rest,$part_d_orig)
+ = @{$part_d};
+
+ for my $arch (@{$d_arch->[0]}) {
+ $self->hint('invalid-arch-string-in-source-relation',
+ $arch, "[$field: $part_d_orig]")
+ if $arch eq 'all'
+ || (
+ !$self->data->architectures
+ ->is_release_architecture(
+ $arch)
+ && !$self->data->architectures->is_wildcard($arch)
+ );
+ }
+
+ for my $restrlist (@{$d_restr}) {
+ for my $prof (@{$restrlist}) {
+ $prof =~ s/^!//;
+ $self->hint(
+ 'invalid-profile-name-in-source-relation',
+ "$prof [$field: $part_d_orig]"
+ )
+ unless $KNOWN_BUILD_PROFILES->recognizes($prof)
+ or $prof =~ /^pkg\.[a-z0-9][a-z0-9+.-]+\../;
+ }
+ }
+
+ if ( $d_pkg =~ /^openjdk-\d+-doc$/
+ or $d_pkg eq 'classpath-doc'){
+ $self->hint(
+ 'build-depends-on-specific-java-doc-package',
+ $d_pkg);
+ }
+
+ if ($d_pkg eq 'java-compiler'){
+ $self->hint(
+ 'build-depends-on-an-obsolete-java-package',
+ $d_pkg);
+ }
+
+ if ( $d_pkg =~ /^libdb\d+\.\d+.*-dev$/
+ and $is_dep_field) {
+ $self->hint('build-depends-on-versioned-berkeley-db',
+ "$field:$d_pkg");
+ }
+
+ $self->hint('conflicting-negation-in-source-relation',
+ "$field: $part_d_orig")
+ if ( $d_arch
+ && $d_arch->[1] != 0
+ && $d_arch->[1] ne @{ $d_arch->[0] });
+
+ $self->hint('depends-on-packaging-dev', $field)
+ if ($d_pkg eq 'packaging-dev');
+
+ $self->hint('build-depends-on-build-essential', $field)
+ if ($d_pkg eq 'build-essential');
+
+ $self->hint(
+'build-depends-on-build-essential-package-without-using-version',
+ "$d_pkg [$field: $part_d_orig]"
+ )
+ if ($known_build_essential->recognizes($d_pkg)
+ && !$d_version->[1]);
+
+ $self->hint(
+'build-depends-on-essential-package-without-using-version',
+ "$field: $part_d_orig"
+ )
+ if ( $KNOWN_ESSENTIAL->recognizes($d_pkg)
+ && !$d_version->[0]
+ && $d_pkg ne 'dash');
+ push @seen_obsolete_packages, [$part_d_orig, $d_pkg]
+ if ( $OBSOLETE_PACKAGES->recognizes($d_pkg)
+ && $is_dep_field);
+
+ $self->hint('build-depends-on-metapackage',
+ "$field: $part_d_orig")
+ if ( $KNOWN_METAPACKAGES->recognizes($d_pkg)
+ and $is_dep_field);
+
+ $self->hint('build-depends-on-non-build-package',
+ "$field: $part_d_orig")
+ if ( $NO_BUILD_DEPENDS->recognizes($d_pkg)
+ and $is_dep_field);
+
+ $self->hint('build-depends-on-1-revision',
+ "$field: $part_d_orig")
+ if ( $d_version->[0] eq '>='
+ && $d_version->[1] =~ /-1$/
+ && $is_dep_field);
+
+ $self->hint('bad-relation', "$field: $part_d_orig")
+ if $rest;
+
+ $self->hint('bad-version-in-relation',
+ "$field: $part_d_orig")
+ if ($d_version->[0]
+ && !version_check($d_version->[1]));
+
+ $self->hint(
+ 'package-relation-with-perl-modules',
+ "$field: $part_d_orig"
+ # matches "perl-modules" (<= 5.20) as well as
+ # perl-modules-5.xx (>> 5.20)
+ )
+ if $d_pkg =~ /^perl-modules/
+ && $processable->source_name ne 'perl';
+ }
+
+ my $all_obsolete = 0;
+ $all_obsolete = 1
+ if scalar @seen_obsolete_packages == @alternatives;
+ for my $d (@seen_obsolete_packages) {
+ my ($dep, $pkg_name) = @{$d};
+ my $replacement = $OBSOLETE_PACKAGES->value($pkg_name)
+ // $EMPTY;
+
+ $replacement = ' => ' . $replacement
+ if $replacement ne $EMPTY;
+ if ( $pkg_name eq $alternatives[0][0]
+ or $all_obsolete) {
+ $self->hint('build-depends-on-obsolete-package',
+ "$field: $dep${replacement}");
+ } else {
+ $self->hint('ored-build-depends-on-obsolete-package',
+ "$field: $dep${replacement}");
+ }
+ }
+ }
+ }
+ }
+
+ # Check for redundancies.
+ my @to_check = (
+ ['Build-Depends'],
+ ['Build-Depends', 'Build-Depends-Indep'],
+ ['Build-Depends', 'Build-Depends-Arch']
+ );
+
+ for my $fields (@to_check) {
+ my $relation = Lintian::Relation->new->logical_and(
+ map { $processable->relation($_) }@{$fields});
+
+ for my $redundant_set ($relation->redundancies) {
+
+ $self->hint(
+ 'redundant-build-prerequisites',
+ join(', ', sort @{$redundant_set})
+ );
+ }
+ }
+
+ # Make sure build dependencies and conflicts are consistent.
+ my $build_all = $processable->relation('Build-Depends-All');
+
+ for my $field (
+ qw{Build-Conflicts Build-Conflicts-Indep Build-Conflicts-Arch}) {
+
+ my @conflicts= $processable->fields->trimmed_list($field, qr{\s*,\s*});
+ my @contradictions = grep { $build_all->satisfies($_) } @conflicts;
+
+ my $position = $processable->fields->position($field);
+ my $pointer = $processable->debian_control->item->pointer($position);
+
+ $self->pointed_hint('build-conflicts-with-build-dependency',
+ $pointer, $field, $_)
+ for @contradictions;
+ }
+
+ my (@arch_dep_pkgs, @dbg_pkgs);
+ for my $installable ($group->get_installables) {
+
+ if ($installable->name =~ m/-dbg$/) {
+ push(@dbg_pkgs, $installable);
+
+ } elsif ($installable->fields->value('Architecture') ne 'all'){
+ push(@arch_dep_pkgs, $installable);
+ }
+ }
+
+ my $dstr = join($VERTICAL_BAR, map { quotemeta($_->name) } @arch_dep_pkgs);
+ my $depregex = qr/^(?:$dstr)$/;
+ for my $dbg_proc (@dbg_pkgs) {
+ my $deps = $processable->binary_relation($dbg_proc->name, 'strong');
+ my $missing = 1;
+ $missing = 0
+ if $deps->matches($depregex, Lintian::Relation::VISIT_PRED_NAME);
+ if ($missing && $dbg_proc->is_transitional) {
+ # If it is a transitional package, allow it to depend
+ # on another -dbg instead.
+ $missing = 0
+ if $deps->matches(qr/-dbg \Z/xsm,
+ Lintian::Relation::VISIT_PRED_NAME);
+ }
+ $self->hint('dbg-package-missing-depends', $dbg_proc->name)
+ if $missing;
+ }
+
+ # Check for a python*-dev build dependency in source packages that
+ # build only arch: all packages.
+ if ($arch_dep_packages == 0 and $build_all->satisfies($PYTHON_DEV)) {
+ $self->hint('build-depends-on-python-dev-with-no-arch-any');
+ }
+
+ my $bdepends = $processable->relation('Build-Depends');
+
+ # libmodule-build-perl
+ # matches() instead of satisfies() because of possible OR relation
+ $self->hint('libmodule-build-perl-needs-to-be-in-build-depends')
+ if $processable->relation('Build-Depends-Indep')
+ ->equals('libmodule-build-perl', Lintian::Relation::VISIT_PRED_NAME)
+ && !$bdepends->equals('libmodule-build-perl',
+ Lintian::Relation::VISIT_PRED_NAME);
+
+ # libmodule-build-tiny-perl
+ $self->hint('libmodule-build-tiny-perl-needs-to-be-in-build-depends')
+ if $processable->relation('Build-Depends-Indep')
+ ->satisfies('libmodule-build-tiny-perl')
+ && !$bdepends->satisfies('libmodule-build-tiny-perl:any');
+
+ return;
+}
+
+# splits "foo:bar (>= 1.2.3) [!i386 ia64] <stage1 !nocheck> <cross>" into
+# ( "foo", "bar", [ ">=", "1.2.3" ], [ [ "i386", "ia64" ], 1 ], [ [ "stage1", "!nocheck" ] , [ "cross" ] ], "" )
+# ^^^ ^^
+# count of negated arches, if ! was given ||
+# rest (should always be "" for valid dependencies)
+sub _split_dep {
+ my $dep = shift;
+ my ($pkg, $dmarch, $version, $darch, $restr)
+ = ($EMPTY, $EMPTY, [$EMPTY,$EMPTY], [[], 0], []);
+
+ if ($dep =~ s/^\s*([^<\s\[\(]+)\s*//) {
+ ($pkg, $dmarch) = split(/:/, $1, 2);
+ $dmarch //= $EMPTY; # Ensure it is defined (in case there is no ":")
+ }
+
+ if (length $dep) {
+ if ($dep
+ =~ s/\s* \( \s* (<<|<=|>=|>>|[=<>]) \s* ([^\s(]+) \s* \) \s*//x) {
+ @{$version} = ($1, $2);
+ }
+ if ($dep && $dep =~ s/\s*\[([^\]]+)\]\s*//) {
+ my $t = $1;
+ $darch->[0] = [split /\s+/, $t];
+ my $negated = 0;
+ for my $arch (@{ $darch->[0] }) {
+ $negated++ if $arch =~ s/^!//;
+ }
+ $darch->[1] = $negated;
+ }
+ while ($dep && $dep =~ s/\s*<([^>]+)>\s*//) {
+ my $t = $1;
+ push(@{$restr}, [split /\s+/, $t]);
+ }
+ }
+
+ return ($pkg, $dmarch, $version, $darch, $restr, $dep);
+}
+
+sub check_field {
+ my ($self, $field, $data) = @_;
+
+ my $processable = $self->processable;
+
+ my $has_default_mta
+ = $processable->relation($field)
+ ->equals('default-mta', Lintian::Relation::VISIT_PRED_NAME);
+ my $has_mail_transport_agent = $processable->relation($field)
+ ->equals('mail-transport-agent', Lintian::Relation::VISIT_PRED_NAME);
+
+ $self->hint('default-mta-dependency-not-listed-first',"$field: $data")
+ if $processable->relation($field)
+ ->matches(qr/\|\s+default-mta/, Lintian::Relation::VISIT_OR_CLAUSE_FULL);
+
+ if ($has_default_mta) {
+ $self->hint(
+ 'default-mta-dependency-does-not-specify-mail-transport-agent',
+ "$field: $data")
+ unless $has_mail_transport_agent;
+ } elsif ($has_mail_transport_agent) {
+ $self->hint(
+ 'mail-transport-agent-dependency-does-not-specify-default-mta',
+ "$field: $data")
+ unless $has_default_mta;
+ }
+
+ return;
+}
+
+1;
+
+# Local Variables:
+# indent-tabs-mode: nil
+# cperl-indent-level: 4
+# End:
+# vim: syntax=perl sw=4 sts=4 sr et