# rules -- lintian check script -*- perl -*-

# Copyright (C) 2006 Russ Allbery <rra@debian.org>
# Copyright (C) 2005 René van Bevern <rvb@pro-linux.de>
#
# 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 Lintian::rules;
use strict;
use Tags;
use Util;
use Dep;

# The following targets are required per Policy.
my %required = map { $_ => 1 }
    qw(build binary binary-arch binary-indep clean);

sub run {

my $pkg = shift;
my $type = 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 = "";
if(open (IN, '<', "fields/architecture")) {
    chomp($architecture = <IN>)
}
close(IN);

#get build-depends:
my $build_deps = "";
if (open(IN, '<', "fields/build-depends")) {
    local $/ = undef;
    chomp($build_deps .= <IN>);
    close(IN);
}
if (open(IN, '<', "fields/build-depends-indep")) {
    local $/ = undef;
    chomp($build_deps .= <IN>);
    close(IN);
}
$build_deps = Dep::parse($build_deps);


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 = <RULES>;
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;
while (<RULES>) {
    next if /^\s*\#/;
    $includes = 1 if /^ *[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 $.";
    }

    # 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) {
	    $seen{$_}++ if $required{$_};
	}
    } else {
    	#if we have non-empty, non-comment lines, store them for all current targets:
	if (m/^\s+[^#]/) {
	    foreach my $target (@current_targets) {
		$rules_per_target{$target} ||= [];
		push @{$rules_per_target{$target}}, $_;
	    }
	}
    }
}
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};
    }

    #check if we should have seen some dh_ calls:
    if (Dep::implies($build_deps, Dep::parse("debhelper"))) {
        #.desktop files usually imply dh_desktop:
	if (scalar @{[glob("debfiles/*.desktop")]} &&
	    ! grep { /^\s*dh_desktop/ } map { @$_ } values %rules_per_target) {
	    tag "desktop-file-but-no-dh_desktop-call";
	}
    }
}

# Make sure we have no content for binary-arch if we are arch-indep:
if ($architecture eq "all" && scalar @{$rules_per_target{'binary-arch'} || []}) {
    tag "binary-arch-rules-but-pkg-is-arch-indep";
}
}
1;

# vim: syntax=perl ts=8 sw=4
