1 #!/usr/bin/env perl 2 3 #------------------------------------------------------------------- 4 # Check header files and #include directives 5 # 6 # (1) include/*.h must not include pub_core_...h 7 # (2) coregrind/pub_core_xyzzy.h may include pub_tool_xyzzy.h 8 # other coregrind headers may not include pub_tool_xyzzy.h 9 # (3) coregrind/ *.c must not include pub_tool_xyzzy.h 10 # (4) tool *.[ch] files must not include pub_core_...h 11 # (5) include pub_core/tool_clreq.h instead of valgrind.h except in tools' 12 # export headers 13 #------------------------------------------------------------------- 14 15 use strict; 16 use warnings; 17 use File::Basename; 18 use Getopt::Long; 19 20 my $this_script = basename($0); 21 22 # The list of top-level directories is divided into three sets: 23 # 24 # (1) coregrind directories 25 # (2) tool directories 26 # (3) directories to ignore 27 # 28 # If a directory is found that does not belong to any of those sets, the 29 # script will terminate unsuccessfully. 30 31 my %coregrind_dirs = ( 32 "include" => 1, 33 "coregrind" => 1, 34 ); 35 36 my %tool_dirs = ( 37 "none" => 1, 38 "lackey" => 1, 39 "massif" => 1, 40 "memcheck" => 1, 41 "drd" => 1, 42 "helgrind", => 1, 43 "callgrind" => 1, 44 "cachegrind" => 1, 45 "shared" => 1, 46 "exp-bbv" => 1, 47 "exp-dhat" => 1, 48 "exp-sgcheck" => 1 49 ); 50 51 my %dirs_to_ignore = ( 52 ".deps" => 1, 53 ".svn" => 1, 54 ".git" => 1, # allow git mirrors of the svn repo 55 ".in_place" => 1, 56 "Inst" => 1, # the nightly scripts creates this 57 "VEX" => 1, 58 "docs" => 1, 59 "auxprogs" => 1, 60 "autom4te.cache" => 1, 61 "nightly" => 1, 62 "perf" => 1, 63 "tests" => 1, 64 "gdbserver_tests" => 1, 65 "mpi" => 1 66 ); 67 68 my %tool_export_header = ( 69 "drd/drd.h" => 1, 70 "helgrind/helgrind.h" => 1, 71 "memcheck/memcheck.h" => 1, 72 "callgrind/callgrind.h" => 1 73 ); 74 75 my $usage=<<EOF; 76 USAGE 77 78 $this_script 79 80 [--debug] Debugging output 81 82 dir ... Directories to process 83 EOF 84 85 my $debug = 0; 86 my $num_errors = 0; 87 88 &main; 89 90 sub main { 91 GetOptions( "debug" => \$debug ) || die $usage; 92 93 my $argc = $#ARGV + 1; 94 95 if ($argc < 1) { 96 die $usage; 97 } 98 99 foreach my $dir (@ARGV) { 100 process_dir(undef, $dir, 0); 101 } 102 103 my $rc = ($num_errors == 0) ? 0 : 1; 104 exit $rc; 105 } 106 107 sub process_dir { 108 my ($path, $dir, $depth) = @_; 109 my $hdir; 110 111 if ($depth == 0) { 112 # The root directory is always processed 113 } elsif ($depth == 1) { 114 # Toplevel directories 115 return if ($dirs_to_ignore{$dir}); 116 117 if (! $tool_dirs{$dir} && ! $coregrind_dirs{$dir}) { 118 die "Unknown directory '$dir'. Please update $this_script\n"; 119 } 120 } else { 121 # Subdirectories 122 return if ($dirs_to_ignore{$dir}); 123 } 124 125 print "DIR = $dir DEPTH = $depth\n" if ($debug); 126 127 chdir($dir) || die "Cannot chdir '$dir'\n"; 128 129 opendir($hdir, ".") || die "cannot open directory '.'"; 130 131 while (my $file = readdir($hdir)) { 132 next if ($file eq "."); 133 next if ($file eq ".."); 134 135 # Subdirectories 136 if (-d $file) { 137 my $full_path = defined $path ? "$path/$file" : $file; 138 process_dir($full_path, $file, $depth + 1); 139 next; 140 } 141 142 # Regular files; only interested in *.c and *.h 143 next if (! ($file =~ /\.[ch]$/)); 144 my $path_name = defined $path ? "$path/$file" : $file; 145 process_file($path_name); 146 } 147 close($hdir); 148 chdir("..") || die "Cannot chdir '..'\n"; 149 } 150 151 #--------------------------------------------------------------------- 152 # Return 1, if file is located in <valgrind>/include 153 #--------------------------------------------------------------------- 154 sub is_coregrind_export_header { 155 my ($path_name) = @_; 156 157 return ($path_name =~ /^include\//) ? 1 : 0; 158 } 159 160 #--------------------------------------------------------------------- 161 # Return 1, if file is located underneath <valgrind>/coregrind 162 #--------------------------------------------------------------------- 163 sub is_coregrind_file { 164 my ($path_name) = @_; 165 166 return ($path_name =~ /^coregrind\//) ? 1 : 0; 167 } 168 169 #--------------------------------------------------------------------- 170 # Return 1, if file is located underneath <valgrind>/<tool> 171 #--------------------------------------------------------------------- 172 sub is_tool_file { 173 my ($path_name) = @_; 174 175 for my $tool (keys %tool_dirs) { 176 return 1 if ($path_name =~ /^$tool\//); 177 } 178 return 0 179 } 180 181 #--------------------------------------------------------------------- 182 # Return array of files #include'd by file. 183 #--------------------------------------------------------------------- 184 sub get_included_files { 185 my ($path_name) = @_; 186 my @includes = (); 187 my $file = basename($path_name); 188 189 open(FILE, "<$file") || die "Cannot open file '$file'"; 190 191 while (my $line = <FILE>) { 192 if ($line =~ /^\s*#\s*include "([^"]*)"/) { 193 push @includes, $1; 194 } 195 if ($line =~ /^\s*#\s*include <([^>]*)>/) { 196 push @includes, $1; 197 } 198 } 199 close FILE; 200 return @includes; 201 } 202 203 #--------------------------------------------------------------------- 204 # Check a file from <valgrind>/include 205 #--------------------------------------------------------------------- 206 sub check_coregrind_export_header { 207 my ($path_name) = @_; 208 209 foreach my $inc (get_included_files($path_name)) { 210 $inc = basename($inc); 211 # Must not include pub_core_.... 212 if ($inc =~ /pub_core_/) { 213 error("File $path_name must not include $inc\n"); 214 } 215 # Only pub_tool_clreq.h may include valgrind.h 216 if (($inc eq "valgrind.h") && ($path_name ne "include/pub_tool_clreq.h")) { 217 error("File $path_name should include pub_tool_clreq.h instead of $inc\n"); 218 } 219 } 220 } 221 222 #--------------------------------------------------------------------- 223 # Check a file from <valgrind>/coregrind 224 #--------------------------------------------------------------------- 225 sub check_coregrind_file { 226 my ($path_name) = @_; 227 my $file = basename($path_name); 228 229 foreach my $inc (get_included_files($path_name)) { 230 print "\tINCLUDE $inc\n" if ($debug); 231 # Only pub_tool_xyzzy.h may include pub_core_xyzzy.h 232 if ($inc =~ /pub_tool_/) { 233 my $buddy = $inc; 234 $buddy =~ s/pub_tool/pub_core/; 235 if ($file ne $buddy) { 236 error("File $path_name must not include $inc\n"); 237 } 238 } 239 # Must not include valgrind.h 240 if ($inc eq "valgrind.h") { 241 error("File $path_name should include pub_core_clreq.h instead of $inc\n"); 242 } 243 } 244 } 245 246 #--------------------------------------------------------------------- 247 # Check a file from <valgrind>/<tool> 248 #--------------------------------------------------------------------- 249 sub check_tool_file { 250 my ($path_name) = @_; 251 my $file = basename($path_name); 252 253 foreach my $inc (get_included_files($path_name)) { 254 print "\tINCLUDE $inc\n" if ($debug); 255 # Must not include pub_core_... 256 if ($inc =~ /pub_core_/) { 257 error("File $path_name must not include $inc\n"); 258 } 259 # Must not include valgrind.h unless this is an export header 260 if ($inc eq "valgrind.h" && ! $tool_export_header{$path_name}) { 261 error("File $path_name should include pub_tool_clreq.h instead of $inc\n"); 262 } 263 } 264 } 265 266 sub process_file { 267 my ($path_name) = @_; 268 269 print "FILE = $path_name\n" if ($debug); 270 271 if (is_coregrind_export_header($path_name)) { 272 check_coregrind_export_header($path_name); 273 } elsif (is_coregrind_file($path_name)) { 274 check_coregrind_file($path_name); 275 } elsif (is_tool_file($path_name)) { 276 check_tool_file($path_name); 277 } 278 } 279 280 sub error { 281 my ($message) = @_; 282 print STDERR "*** $message"; 283 ++$num_errors; 284 } 285