blob: 704552b00dc6cdd8f9a1e54723f326a4eebe700a [file] [log] [blame]
#!/usr/bin/env perl
# Lame script to compare two build logs.
#
# The script intercepts directory changes and compiler invocations and
# compares the compiler invocations for equality. Equality is defined
# as "same options in both invocations ignoring order". So we only test
# a necessary condition.
#
# Both builds must be configured with the same --prefix. Otherwise,
# the value of -DVG_LIBDIR= will differ. They also need to use the same
# compiler.
use Getopt::Long;
use strict;
use warnings;
my $prog_name = "compare-build-logs";
my $usage=<<EOF;
USAGE
$prog_name log1 log2
[--gcc] intercept GCC invocations (this is default)
[--clang] intercept clang invocations
[-v] verbose mode
log-file1
log-file2
EOF
my $compiler = "gcc";
my $verbose = 0;
GetOptions( "gcc" => sub { $compiler = "gcc" },
"clang" => sub { $compiler = "clang" },
"v" => sub { $verbose = 1 },
) || die $usage;
my $num_arg = $#ARGV + 1;
if ($num_arg != 2) {
die $usage;
}
my $log1 = $ARGV[0];
my $log2 = $ARGV[1];
# Hashes: relative filename -> compiler invocation
my %cmd1 = read_log_file($log1);
my %cmd2 = read_log_file($log2);
# Compare the log files
foreach my $file (keys %cmd1) {
if (! $cmd2{$file}) {
print "*** $file missing in $log2\n";
} else {
compare_invocations($file, $cmd1{$file }, $cmd2{$file});
}
}
foreach my $file (keys %cmd2) {
if (! $cmd1{$file}) {
print "*** $file missing in $log1\n";
}
}
exit 0;
# Compare two lines |c1| and |c2| which are compiler invocations for |file|.
# Basically, we look at a compiler invocation as a sequence of blank-separated
# options.
sub compare_invocations {
my ($file, $c1, $c2) = @_;
my ($found1, $found2);
# print "COMPARE $file\n";
# Remove stuff that has embedded spaces
# Remove: `test -f 'whatever.c' || echo './'`
$c1 =~ s|(`[^`]*`)||;
$found1 = $1;
$c2 =~ s|(`[^`]*`)||;
$found2 = $1;
if ($found1 && $found2) {
die if ($found1 ne $found2);
}
# Remove: -o whatever
$c1 =~ s|-o[ ][ ]*([^ ][^ ]*)||;
$found1 = $1;
$c2 =~ s|-o[ ][ ]*([^ ][^ ]*)||;
$found2 = $1;
if ($found1 && $found2) {
die if ($found1 ne $found2);
}
# The remaining lines are considered to be blank-separated options and file
# names. They should be identical in both invocations (but in any order).
my %o1 = ();
my %o2 = ();
foreach my $k (split /\s+/,$c1) {
$o1{$k} = 1;
}
foreach my $k (split /\s+/,$c2) {
$o2{$k} = 1;
}
# foreach my $zz (keys %o1) {
# print "$zz\n";
# }
foreach my $k (keys %o1) {
if (! $o2{$k}) {
print "*** '$k' is missing in compilation of '$file' in $log2\n";
}
}
foreach my $k (keys %o2) {
if (! $o1{$k}) {
print "*** '$k' is missing in compilation of '$file' in $log1\n";
}
}
}
# Read a compiler log file.
# Return hash: relative (to build root) file name -> compiler invocation
sub read_log_file
{
my ($log) = @_;
my $dir = "";
my $root = "";
my %cmd = ();
print "...reading $log\n" if ($verbose);
open(LOG, "$log") || die "cannot open $log\n";
while (my $line = <LOG>) {
chomp $line;
if ($line =~ /^make/) {
if ($line =~ /Entering directory/) {
$dir = $line;
$dir =~ s/^.*`//;
$dir =~ s/'.*//;
if ($root eq "") {
$root = $dir;
# Append a slash if not present
$root = "$root/" if (! ($root =~ /\/$/));
print "...build root is $root\n" if ($verbose);
}
# print "DIR = $dir\n";
next;
}
}
if ($line =~ /^\s*[^\s]*$compiler\s/) {
# If line ends in \ read continuation line.
while ($line =~ /\\$/) {
my $next = <LOG>;
chomp($next);
$line =~ s/\\$//;
$line .= $next;
}
my $file = extract_file($line);
$file = "$dir/$file"; # make absolute
$file =~ s/$root//; # remove build root
# print "FILE $file\n";
$cmd{"$file"} = $line;
}
}
close(LOG);
my $num_invocations = int(keys %cmd);
if ($num_invocations == 0) {
print "*** File $log does not contain any compiler invocations\n";
} else {
print "...found $num_invocations invocations\n" if ($verbose);
}
return %cmd;
}
# Extract a file name from the command line. Assume there is a -o filename
# present. If not, issue a warning.
#
sub extract_file {
my($line) = @_;
# Look for -o executable
if ($line =~ /-o[ ][ ]*([^ ][^ ]*)/) {
return $1;
} else {
print "*** Could not extract file name from $line\n";
return "UNKNOWN";
}
}