# rules -- lintian check script -*- perl -*- # Copyright (C) 2006 Russ Allbery # Copyright (C) 2005 René van Bevern # # 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. package Maemian::rules; use strict; use Tags; use Util; # The following targets are required per Policy. my %required = map { $_ => 1 } qw(build binary binary-arch binary-indep clean); # Rules about required debhelper command ordering. Each command is put into a # class and the tag is issued if they're called in the wrong order for the # classes. Unknown commands won't trigger this flag. my %debhelper_order = (dh_makeshlibs => 1, dh_shlibdeps => 2, dh_installdeb => 2, dh_gencontrol => 2, dh_builddeb => 3); sub run { my $pkg = shift; my $type = shift; my $info = shift; # Policy could be read as allowing debian/rules to be a symlink to some other # file, and in a native Debian package it could be a symlink to a file that we # didn't unpack. Warn if it's a symlink (dpkg-source does as well) and skip # all the tests if we then can't read it. if (-l "debfiles/rules") { tag "debian-rules-is-symlink", ""; return 0 unless -f "debfiles/rules"; } #get architecture field: unless (-d "fields") { fail("directory in lintian laboratory for $type package $pkg missing: fields"); } my $architecture = $info->field('architecture') || ''; open(RULES, '<', 'debfiles/rules') or fail("Failed opening rules: $!"); # Check for required #!/usr/bin/make -f opening line. Allow -r or -e; a # strict reading of Policy doesn't allow either, but they seem harmless. my $start = ; tag "debian-rules-not-a-makefile", "" unless $start =~ m%^\#!\s*/usr/bin/make\s+-[re]?f[re]?\s*$%; # Scan debian/rules. We would really like to let make do this for us, but # unfortunately there doesn't seem to be a way to get make to syntax-check and # analyze a makefile without running at least $(shell) commands. # # We skip some of the rule analysis if debian/rules includes any other files, # since to chase all includes we'd have to have all of its build dependencies # installed. my $includes = 0; my %seen; local $_; my @current_targets; my %rules_per_target; my $debhelper_group; while () { next if /^\s*\#/; $includes = 1 if m/^ *[s-]?include\s+/; # Check for DH_COMPAT settings outside of any rule, which are now # deprecated. It's a bit easier structurally to do this here than in # debhelper. if (/^\s*(export\s+)?DH_COMPAT\s*:?=/ && keys(%seen) == 0) { tag "debian-rules-sets-DH_COMPAT", "line $."; } # Check for problems that can occur anywhere in debian/rules. if (/\$[\(\{]PWD[\)\}]/) { tag "debian-rules-uses-pwd", "line $."; } if (/^\t\s*-(?:\$[\(\{]MAKE[\}\)]|make)\s.*(?:dist)?clean/ || /^\t\s*(?:\$[\(\{]MAKE[\}\)]|make)\s(?:.*\s)?-\w*i.*(?:dist)?clean/) { tag "debian-rules-ignores-make-clean-error", "line $."; } if (/$[\(\{]DEB_BUILD_OPTS[\)\}]/) { tag "debian-rules-uses-DEB_BUILD_OPTS", "line $."; } # Listing a rule as a dependency of .PHONY is sufficient to make it # present for the purposes of GNU make and therefore the Policy # requirement. if (/^(?:[^:]+\s)?\.PHONY(?:\s[^:]+)?:(.+)/) { my @targets = split (' ', $1); for (@targets) { $seen{$_}++ if $required{$_}; } } if (/^([^\s:][^:]*):/) { @current_targets = split (' ', $1); for (@current_targets) { if (m/%/) { my $pattern = quotemeta $_; $pattern =~ s/\\%/.*/g; for my $target (keys %required) { $seen{$target}++ if $target =~ m/$pattern/; } } else { $seen{$_}++ if $required{$_}; } } $debhelper_group = 0; } elsif (/^define /) { # We don't want to think the body of the define is part of the # previous rule or we'll get false positives on tags like # binary-arch-rules-but-pkg-is-arch-indep. Treat a define as the # end of the current rule, although that isn't very accurate either. @current_targets = (); } else { # If we have non-empty, non-comment lines, store them for all current # targets and check whether debhelper programs are called in a # reasonable order. if (m/^\s+[^\#]/) { foreach my $target (@current_targets) { $rules_per_target{$target} ||= []; push @{$rules_per_target{$target}}, $_; } if (m/^\s+(dh_\S+)\b/ and $debhelper_order{$1}) { my $command = $1; my $group = $debhelper_order{$command}; if ($group < $debhelper_group) { tag "debian-rules-calls-debhelper-in-odd-order", $command, "(line $.)"; } else { $debhelper_group = $group; } } } } } close RULES; unless ($includes) { # Make sure all the required rules were seen. for my $target (sort keys %required) { tag "debian-rules-missing-required-target", $target unless $seen{$target}; } } # Make sure we have no content for binary-arch if we are arch-indep: $rules_per_target{'binary-arch'} ||= []; if ($architecture eq "all" && scalar @{$rules_per_target{'binary-arch'}}) { my $nonempty = 0; foreach (@{$rules_per_target{'binary-arch'}}) { # dh binary-arch is actually a no-op if there is no # Architecture: any package in the control file unless (m/^\s*dh\s+(?:binary-arch|\$\@)/) { $nonempty = 1; } } tag "binary-arch-rules-but-pkg-is-arch-indep" if $nonempty; } } 1; # Local Variables: # indent-tabs-mode: nil # cperl-indent-level: 4 # End: # vim: syntax=perl sw=4 sts=4 ts=4 et shiftround