#! /usr/local/bin/perl5
#
# This script can be used to clean up an automatically generated Makefile or
# Makefile.in.  It removes unused variables.  It also tries to remove
# intermediate empty targets (those with no commands).
# Based on the following variables, it can:
#
$noinstall = 1;   # Remove install targets
$nodots = 1;      # Remove .S and .s targets
$nodist = 1;      # Remove distribution targets
$nophony = 1;     # Remove .PHONY: 
$noid = 1;        # Remove ID: target
$notexinfo = 1;   # Remove texinfo targets
$noemptytarg = 0; # Remove empty targets
#
# Still to do: intermediate target/dependency compression should ONLY happen
# for targets in the .PHONY list.
# targets of the form xxx-yyy are often internal targets, and can be removed
# as well.

$debug = 0;       # Turn on heavy debugging info


$read_nextline = 0;

sub remove_command
{
    print "In remove command\n" if ($debug);
    while (<MAKEFILE>) {
        if (/^[^ \t]/) {$read_nextline = 1; print "YE!\n" if ($debug); last;}
    }
}

sub remove_line
{
    local ($look_for_vars) = @_;
    print "In remove line\n" if ($debug);
    while (1) {
        &look_for_var_use if ($look_for_vars);
        $var = $_;
        chop $var;
        if ((chop $var) ne "\\") { last; }
        $_ = <MAKEFILE>;
    };
}

sub scan_makefile 
{
    $val = 1;
    if ($noinstall && (/^install.*:/ || /^uninstall.*:/)) {
        # Remove install targets
        print "install target\n" if ($debug);
        &remove_command;
    } elsif ($nodots && (/^\.[Ss]\.o/ || /^\.[Ss]\.lo/)) {
        # Remove bogus .s.o targets
        &remove_command;
    } elsif ($nodist && /^dist[-a-zA-Z]*:/) {
	# Remove distribution info
        print "dist targets\n" if ($debug);
        &remove_command;
    } elsif ($noid && /^ID: /) {
	&remove_command;
    } elsif ($notexinfo && (/^info-am:/ || /^info:/ || /^dvi-am:/ || /^dvi:/)){
        &remove_command;
    } else {
        $val = 0;
    }
    return $val;
}

#
# Scan the rest of this line and look for variable uses
sub look_for_var_use
{
    $PAT_VAR_USE = '^[^\$]*\$\(([-a-zA-Z0-9-_]*)\)(.*)';
    print "Vars in $_" if ($debug);
    if (/\$\(.*\)/) {
        $line = $_;
        while ( ($var, $Etc) = ($line =~ /$PAT_VAR_USE/o)) { 
            $line = $Etc;
	    print "Found $var\n" if ($debug);
	    # This is really only approximate; we need to construct a
	    # dependency tree and eliminate dead trees.
            $variables{$var} ++; 
        }
    }
}

$PAT_DEP_USE = '^[ \t]*(\S+) (.*)';
#
# Scan the rest of this line and look for other dependencies (including 
# continuation lines)
sub look_for_depends
{				
    my $count, $line;

    $tmp = $_;
    ($targ,$line) = ($tmp =~ /^([^:]*):(.*)/);
    $count = 0;
    print "$targ : $line";
    while ( ($var, $Etc) = ($line =~ /$PAT_DEP_USE/o)) { 
        $line = $Etc;
        print "Found $var\n" if ($debug);
	# This is really only approximate; we need to construct a
	# dependency tree and eliminate dead trees.
	$count++;
        $targets{$var} ++; 
    }
    return $count;
}

sub look_for_commands
{
    $_ = <MAKEFILE>;
    $read_next_line = 0;
    return ($_ ne "\n");
}

sub output_target_list
{
    print "In output targets line $_\n" if ($debug);
    $line = $_;
    ($targ, $line) = ($line =~ /^(\S+):(.*)/);  # Chops off the Newline!
    print "$targ: ";
    $line = "$line\n";
    while (1) {
        chop $line;
	$lastchar = chop $line;
	print "last char = $lastchar\n" if ($debug);
	$line = "$line$lastchar";
	print "FOO:$line\n" if ($debug);
	while ( ($dep, $Etc) = ($line =~ /$PAT_DEP_USE/o) ) {
	    print "dep = $dep/Etc = $Etc\n" if ($debug);
	    if ($dep eq "\\" || $dep eq "\n") { last; }
	    if ($targets{$dep} > 1 || $targets{$dep} == 0) { print "$dep " }
	    $line = $Etc;
	}
        if ($lastchar ne "\\") { last; }
        $line = <MAKEFILE>;
    };
    print "\n";
}

$filename = $ARGV[0];

$PAT_ASSIGN = '^([a-zA-Z_0-9]*)[ \t]*=';
$PAT_TARGET = '^([-a-zA-Z_0-9]*):[ \t]*$';

# Read variable names; keep track of the number of uses
open( MAKEFILE, "<$filename" ) || die "Could not open $filename\n";
$read_nextline = 0;
while ($read_nextline || ($_ = <MAKEFILE>)) {
    print "$_" if ($debug);
    $read_nextline = 0;
    $line = $_;
    if ((($var) = ($line =~ /$PAT_ASSIGN/o)) && $var ne "VPATH" &&
        $var ne "SHELL" ) {
        print "variable assignment\n" if ($debug);
        # Remove variables that are never used
        # But keep special variables: SHELL, VPATH
        $variables{$var} = 0;
        &remove_line(1);
    } elsif (&scan_makefile) {
        ;
    } elsif ( ($var) = ($line =~ /$PAT_TARGET/o) ) {
        # target with no dependencies (eventually, also check
        # to see if they have any commands)
        &look_for_var_use;
        $targets{$var} = 1 + &look_for_commands;
    } else {
        # Is there a variable use here?
        &look_for_var_use;
    }
    print "read_nextline = $read_nextline\n" if ($debug);
}
close( MAKEFILE );

# Now, read the file and remove useless variables
open( MAKEFILE, "<$filename" ); 
$read_nextline = 0;
$last_blank = 0;
while ($read_nextline || ($_ = <MAKEFILE>)) {
    $read_nextline = 0;
    $line = $_;
    if ((($var) = ($line =~ /$PAT_ASSIGN/o)) && $var ne "VPATH" &&
        $var ne "SHELL" ) {
        print "variables{$var} = $variables{$var}\n" if ($debug);
        if ($variables{$var} > 0) { 
            print $_; 
        } else {
	    &remove_line(0);
        }
    } elsif (&scan_makefile) {
        ;
    } elsif ($nophony && /^\.PHONY:/o) {
	# Remove .PHONY: ... 
	&remove_line(0);
    } elsif ($noemptytarg && /^[-_0-9A-Za-z]*:/) {
        # process targets
        &output_target_list;
    } elsif ($_ eq "\n" && $last_blank) {
        ;
    } else {
        $last_blank = ($_ eq "\n");
        print $_;
    }
}

