view autopoop @ 0:d72d5d73b93a

Added Autopoop and mkchangelog.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 22 Apr 2008 14:31:44 +0300
parents
children 5665a846d227
line wrap: on
line source

#!/usr/bin/perl -w
# autopoop v0.57 (C) 2008 Matti 'ccr' Hamalainen
#
# Tries to generate extra.mk.in from known configure.ac and other
# autoconf m4-macros, scanning against Makefiles. Also checks for
# missing / unsubstituted symbols, etc.
#
# Requirements:
# - Recent enough GNU Autoconf (and requirements for that)
# - Perl 5
#
# TODO:
# - see fixmes
# - document this
# - find bugs
# - ???
# - profit
#
use strict;
use Cwd qw(getcwd realpath);

# List of subdirectories to check for additional Autoconf m4 macros
my @m4testdirs = ("m4", "unix");

# Misc. settings
my $makefile = "Makefile";
my $extrasys = "extra.mk";
my $buildsys = "buildsys.mk";
my $tmpfile = "autopoop.tmp";
my $autom4te = "autom4te";

# Things that may be set by user
my %bsdefines = (
  "DESTDIR" => 1,
);

# Special substitutions
my %confdefines = (
#  "VPATH" => "srcdir",
#  "SET_MAKE" => "",
);


### Function prototypes
sub shortpath($);
sub scanfile($$$$$$$$);


### Argument handling
my $pcwd = getcwd();
my $argc = ($#ARGV + 1);
print "Autopoop v0.57 (C) 2008 Matti 'ccr' Hamalainen\n";
die("Usage: autopoop <configurefile> <outfile> [autom4te options]\n".
"Example: autopoop configure.ac extra.mk.in\n\n".
"NOTICE! Assumes following files are used:\n".
"$buildsys\[.in\], $extrasys, $makefile\n\n") unless ($argc >= 2);
my $configure = shift;
my $outfile = shift;


### Execute Autoconf 'autom4te' ...
my @args = (@ARGV);
foreach my $dir (@m4testdirs) {
  if (-d $dir) { push(@args, ("-I", $dir)); }
}

push(@args, (
  "--force", "--no-cache", "--melt", "--language=autoconf",
  "--trace=AC_SUBST", "-o", $tmpfile, $configure));

print "- Running $autom4te ".join(" ", @args)." ...\n";
system($autom4te, @args) == 0 or die("Failed to execute $autom4te: $?\n");


### Paf files
$buildsys = realpath($buildsys);
$extrasys = realpath($extrasys);
$outfile = realpath($outfile);


### Process results
open(INFILE,"<".$tmpfile) or die("Could not open '$tmpfile'!\n");
while (<INFILE>) {
  chop;
  my @entries = split(/:/);
  my $key = $entries[3];
  unless ($key =~ /_(FALSE|TRUE)$/) { $confdefines{$key} = $key; }
}
close INFILE;
unlink($tmpfile);


### Scan Makefiles and depencies
my %makesubsts = ();
my %confsubsts = ();
my %makedefines = ();
my %scanned = ($outfile => 1, $extrasys => 1);
scanfile(".", $makefile, \%scanned, \%makesubsts, \%confsubsts, \%makedefines, 1, 1);
scanfile("", $buildsys, \%scanned, \%makesubsts, \%confsubsts, \%makedefines, 1, 0);


### Scan buildsys.mk.in
my %bssubsts = ();
scanfile("", $buildsys.".in", \%scanned, \%bssubsts, \%bssubsts, \%bsdefines, 0, 0);


my %globdefs = ();
foreach my $file (keys %makedefines) {
  foreach my $s (keys %{$makedefines{$file}}) {
    $globdefs{$s} = 1;
  }
}


### Compare results
print "- Checking for used, but possibly undefined Make variables:\n";
foreach my $file (keys %makesubsts) {
  foreach my $s (sort { $a cmp $b } keys %{$makesubsts{$file}}) {
    if (!defined($confdefines{$s}) && !defined($makedefines{$file}{$s}) && !defined($bsdefines{$s})) {
      my $err = 0;
      if (!defined($globdefs{$s})) {
        $err = 1;
      } elsif ($file ne $buildsys && $file ne $outfile) {
        $err = 2;
      }
      if ($err) {
        printf "  %-20s", "'$s'";
        print  " in ".shortpath($file).":".$makesubsts{$file}{$s}." ($err)\n";
      }
    }
  }
}
print qq|Legend:
  (1) = Not defined anywhere (or misdetected).
  (2) = Defined 'somewhere', but unsure if symbol is propagated.

|;


print "- Checking for used, but possibly undefined substitutions:\n";
foreach my $file (keys %confsubsts) {
  foreach my $s (keys %{$confsubsts{$file}}) {
    if (!defined($confdefines{$s})) {
        printf "  %-20s", "'$s'";
        print  " in ".shortpath($file).":".$confsubsts{$file}{$s}."\n";
    }
  }
}
print "\n";


### Output stuff
print "- Outputting '$outfile'\n";
open(OUTFILE, ">", $outfile) or die("Could not open '$outfile'!\n");
foreach my $s (keys %confdefines) {
  if ($confdefines{$s} ne "") {
    print OUTFILE "$s ?= \@".$confdefines{$s}."\@\n";
  } else {
    print OUTFILE "\@".$s."\@\n";
  }
}
close OUTFILE;

print "- Done.\n";


###
### Functions
###
sub shortpath($) {
  if (index($_[0], $pcwd) == 0) {
    return substr($_[0], length($pcwd) + 1);
  } else {
    return $_[0];
  }  
}

sub addhash($$$$$)
{
# if ($usenames) { $substs->{$filename}{$1} = $line; } else { $substs->{$1} = $line; }
  if ($_[0]) {
    $_[1]->{$_[2]}{$_[3]} = $_[4];
  } else {
    $_[1]->{$_[3]} = $_[4];
  }
}

sub scanfile($$$$$$$$) {
  my $mypath = $_[0];
  my $myfile = $_[1];
  
  my $scanned = $_[2];
  my $substs = $_[3];
  my $confsubsts = $_[4];
  my $defines = $_[5];
  
  my $usenames = $_[6];
  my $recurse = $_[7];

  # Form real path and check if we have already scanned the file
  my $filename = realpath($mypath."/".$myfile);
  if (defined($filename)) {
    if (defined($scanned->{$filename})) {
      $scanned->{$filename}++;
      return;
    }
    $scanned->{$filename} = 1;
  } else {
    $filename = $mypath."/".$myfile;
  }

#print "ASDF: $filename\n";

  # Scan file, if found
  if (open(my $SCANFILE, "<", $filename)) {
    print "- Scanning ".shortpath($filename)." ...\n";
    my $line = 0;
    while (<$SCANFILE>) {
      $line++;
      chop;
      if (/^#/) {
        # ignore comment lines
      } else {
      
      while (/[^\$]\$\{([A-Za-z0-9_]+)\}/g) {
        addhash($usenames, $substs, $filename, $1, $line);
      }
      while (/[^\$]\$\(([A-Za-z0-9_]+)\)/g) {
        addhash($usenames, $substs, $filename, $1, $line);
      }
      while (/\@([A-Za-z0-9_]+)\@/g) {
        addhash($usenames, $confsubsts, $filename, $1, $line);
      }
      
      if (/^ifdef\s+([A-Za-z0-9_]+)/g) {
        addhash($usenames, $substs, $filename, $1, $line);
      } elsif (/^([A-Za-z][A-Za-z0-9_]+)\s*[\+]?=/) {
        addhash($usenames, $defines, $filename, $1, $line);
      }
      elsif (/^include (\S+)/) {
        scanfile($mypath, "$1", $scanned, $substs, $confsubsts, $defines, $usenames, $recurse);
      }
      }
    }
    close $SCANFILE;
  }
  
  # Recurse into subdirectories
  if ($recurse) {
    opendir(DIR, $mypath) || return;
    my @dirs = grep { /^[^\.]/ && -d "$mypath/$_" } readdir(DIR);
    closedir(DIR);
    foreach my $dir (@dirs) {
      scanfile($mypath."/".$dir, $myfile, $scanned, $substs, $confsubsts, $defines, $usenames, $recurse);
    }
  }
}