1#!/usr/bin/env perl 2 3# Lame script to compare two build logs. 4# 5# The script intercepts directory changes and compiler invocations and 6# compares the compiler invocations for equality. Equality is defined 7# as "same options in both invocations ignoring order". So we only test 8# a necessary condition. 9# 10# Both builds must be configured with the same --prefix. Otherwise, 11# the value of -DVG_LIBDIR= will differ. They also need to use the same 12# compiler. 13 14use Getopt::Long; 15use strict; 16use warnings; 17 18my $prog_name = "compare-build-logs"; 19 20my $usage=<<EOF; 21USAGE 22 23 $prog_name log1 log2 24 25 [--gcc] intercept GCC invocations (this is default) 26 27 [--clang] intercept clang invocations 28 29 [-v] verbose mode 30 31 log-file1 32 33 log-file2 34EOF 35 36my $compiler = "gcc"; 37my $verbose = 0; 38 39GetOptions( "gcc" => sub { $compiler = "gcc" }, 40 "clang" => sub { $compiler = "clang" }, 41 "v" => sub { $verbose = 1 }, 42 ) || die $usage; 43 44my $num_arg = $#ARGV + 1; 45 46if ($num_arg != 2) { 47 die $usage; 48} 49 50my $log1 = $ARGV[0]; 51my $log2 = $ARGV[1]; 52 53# Hashes: relative filename -> compiler invocation 54my %cmd1 = read_log_file($log1); 55my %cmd2 = read_log_file($log2); 56 57# Compare the log files 58 59foreach my $file (keys %cmd1) { 60 if (! $cmd2{$file}) { 61 print "*** $file missing in $log2\n"; 62 } else { 63 compare_invocations($file, $cmd1{$file }, $cmd2{$file}); 64 } 65} 66foreach my $file (keys %cmd2) { 67 if (! $cmd1{$file}) { 68 print "*** $file missing in $log1\n"; 69 } 70} 71 72exit 0; 73 74# Compare two lines |c1| and |c2| which are compiler invocations for |file|. 75# Basically, we look at a compiler invocation as a sequence of blank-separated 76# options. 77sub compare_invocations { 78 my ($file, $c1, $c2) = @_; 79 my ($found1, $found2); 80# print "COMPARE $file\n"; 81 82# Remove stuff that has embedded spaces 83 84# Remove: `test -f 'whatever.c' || echo './'` 85 $c1 =~ s|(`[^`]*`)||; 86 $found1 = $1; 87 $c2 =~ s|(`[^`]*`)||; 88 $found2 = $1; 89 if ($found1 && $found2) { 90 die if ($found1 ne $found2); 91 } 92 93# Remove: -o whatever 94 $c1 =~ s|-o[ ][ ]*([^ ][^ ]*)||; 95 $found1 = $1; 96 $c2 =~ s|-o[ ][ ]*([^ ][^ ]*)||; 97 $found2 = $1; 98 if ($found1 && $found2) { 99 die if ($found1 ne $found2); 100 } 101 102# The remaining lines are considered to be blank-separated options and file 103# names. They should be identical in both invocations (but in any order). 104 my %o1 = (); 105 my %o2 = (); 106 foreach my $k (split /\s+/,$c1) { 107 $o1{$k} = 1; 108 } 109 foreach my $k (split /\s+/,$c2) { 110 $o2{$k} = 1; 111 } 112# foreach my $zz (keys %o1) { 113# print "$zz\n"; 114# } 115 foreach my $k (keys %o1) { 116 if (! $o2{$k}) { 117 print "*** '$k' is missing in compilation of '$file' in $log2\n"; 118 } 119 } 120 foreach my $k (keys %o2) { 121 if (! $o1{$k}) { 122 print "*** '$k' is missing in compilation of '$file' in $log1\n"; 123 } 124 } 125} 126 127# Read a compiler log file. 128# Return hash: relative (to build root) file name -> compiler invocation 129sub read_log_file 130{ 131 my ($log) = @_; 132 my $dir = ""; 133 my $root = ""; 134 my %cmd = (); 135 136 print "...reading $log\n" if ($verbose); 137 138 open(LOG, "$log") || die "cannot open $log\n"; 139 while (my $line = <LOG>) { 140 chomp $line; 141 if ($line =~ /^make/) { 142 if ($line =~ /Entering directory/) { 143 $dir = $line; 144 $dir =~ s/^.*`//; 145 $dir =~ s/'.*//; 146 if ($root eq "") { 147 $root = $dir; 148 # Append a slash if not present 149 $root = "$root/" if (! ($root =~ /\/$/)); 150 print "...build root is $root\n" if ($verbose); 151 } 152# print "DIR = $dir\n"; 153 next; 154 } 155 } 156 if ($line =~ /^\s*[^\s]*$compiler\s/) { 157 # If line ends in \ read continuation line. 158 while ($line =~ /\\$/) { 159 my $next = <LOG>; 160 chomp($next); 161 $line =~ s/\\$//; 162 $line .= $next; 163 } 164 165 my $file = extract_file($line); 166 $file = "$dir/$file"; # make absolute 167 $file =~ s/$root//; # remove build root 168# print "FILE $file\n"; 169 $cmd{"$file"} = $line; 170 } 171 } 172 close(LOG); 173 174 my $num_invocations = int(keys %cmd); 175 176 if ($num_invocations == 0) { 177 print "*** File $log does not contain any compiler invocations\n"; 178 } else { 179 print "...found $num_invocations invocations\n" if ($verbose); 180 } 181 return %cmd; 182} 183 184# Extract a file name from the command line. Assume there is a -o filename 185# present. If not, issue a warning. 186# 187sub extract_file { 188 my($line) = @_; 189# Look for -o executable 190 if ($line =~ /-o[ ][ ]*([^ ][^ ]*)/) { 191 return $1; 192 } else { 193 print "*** Could not extract file name from $line\n"; 194 return "UNKNOWN"; 195 } 196} 197