blob: f234320d92a967bc688c5f0973a68ab5229a9011 [file] [log] [blame]
#!/usr/bin/env perl
#-------------------------------------------------------------------
# Check header files and #include directives
#
# (1) include/*.h must not include pub_core_...h
# (2) coregrind/pub_core_xyzzy.h may include pub_tool_xyzzy.h
# other coregrind headers may not include pub_tool_xyzzy.h
# (3) coregrind/ *.c must not include pub_tool_xyzzy.h
# (4) tool *.[ch] files must not include pub_core_...h
# (5) include pub_core/tool_clreq.h instead of valgrind.h except in tools'
# export headers
#-------------------------------------------------------------------
use strict;
use warnings;
use File::Basename;
use Getopt::Long;
my $this_script = basename($0);
# The list of top-level directories is divided into three sets:
#
# (1) coregrind directories
# (2) tool directories
# (3) directories to ignore
#
# If a directory is found that does not belong to any of those sets, the
# script will terminate unsuccessfully.
my %coregrind_dirs = (
"include" => 1,
"coregrind" => 1,
);
my %tool_dirs = (
"none" => 1,
"lackey" => 1,
"massif" => 1,
"memcheck" => 1,
"drd" => 1,
"helgrind", => 1,
"callgrind" => 1,
"cachegrind" => 1,
"shared" => 1,
"exp-bbv" => 1,
"exp-dhat" => 1,
"exp-sgcheck" => 1
);
my %dirs_to_ignore = (
".deps" => 1,
".svn" => 1,
".git" => 1, # allow git mirrors of the svn repo
".in_place" => 1,
"Inst" => 1, # the nightly scripts creates this
"VEX" => 1,
"docs" => 1,
"auxprogs" => 1,
"autom4te.cache" => 1,
"nightly" => 1,
"perf" => 1,
"tests" => 1,
"gdbserver_tests" => 1,
"mpi" => 1
);
my %tool_export_header = (
"drd/drd.h" => 1,
"helgrind/helgrind.h" => 1,
"memcheck/memcheck.h" => 1,
"callgrind/callgrind.h" => 1
);
my $usage=<<EOF;
USAGE
$this_script
[--debug] Debugging output
dir ... Directories to process
EOF
my $debug = 0;
my $num_errors = 0;
&main;
sub main {
GetOptions( "debug" => \$debug ) || die $usage;
my $argc = $#ARGV + 1;
if ($argc < 1) {
die $usage;
}
foreach my $dir (@ARGV) {
process_dir(undef, $dir, 0);
}
my $rc = ($num_errors == 0) ? 0 : 1;
exit $rc;
}
sub process_dir {
my ($path, $dir, $depth) = @_;
my $hdir;
if ($depth == 0) {
# The root directory is always processed
} elsif ($depth == 1) {
# Toplevel directories
return if ($dirs_to_ignore{$dir});
if (! $tool_dirs{$dir} && ! $coregrind_dirs{$dir}) {
die "Unknown directory '$dir'. Please update $this_script\n";
}
} else {
# Subdirectories
return if ($dirs_to_ignore{$dir});
}
print "DIR = $dir DEPTH = $depth\n" if ($debug);
chdir($dir) || die "Cannot chdir '$dir'\n";
opendir($hdir, ".") || die "cannot open directory '.'";
while (my $file = readdir($hdir)) {
next if ($file eq ".");
next if ($file eq "..");
# Subdirectories
if (-d $file) {
my $full_path = defined $path ? "$path/$file" : $file;
process_dir($full_path, $file, $depth + 1);
next;
}
# Regular files; only interested in *.c and *.h
next if (! ($file =~ /\.[ch]$/));
my $path_name = defined $path ? "$path/$file" : $file;
process_file($path_name);
}
close($hdir);
chdir("..") || die "Cannot chdir '..'\n";
}
#---------------------------------------------------------------------
# Return 1, if file is located in <valgrind>/include
#---------------------------------------------------------------------
sub is_coregrind_export_header {
my ($path_name) = @_;
return ($path_name =~ /^include\//) ? 1 : 0;
}
#---------------------------------------------------------------------
# Return 1, if file is located underneath <valgrind>/coregrind
#---------------------------------------------------------------------
sub is_coregrind_file {
my ($path_name) = @_;
return ($path_name =~ /^coregrind\//) ? 1 : 0;
}
#---------------------------------------------------------------------
# Return 1, if file is located underneath <valgrind>/<tool>
#---------------------------------------------------------------------
sub is_tool_file {
my ($path_name) = @_;
for my $tool (keys %tool_dirs) {
return 1 if ($path_name =~ /^$tool\//);
}
return 0
}
#---------------------------------------------------------------------
# Return array of files #include'd by file.
#---------------------------------------------------------------------
sub get_included_files {
my ($path_name) = @_;
my @includes = ();
my $file = basename($path_name);
open(FILE, "<$file") || die "Cannot open file '$file'";
while (my $line = <FILE>) {
if ($line =~ /^\s*#\s*include "([^"]*)"/) {
push @includes, $1;
}
if ($line =~ /^\s*#\s*include <([^>]*)>/) {
push @includes, $1;
}
}
close FILE;
return @includes;
}
#---------------------------------------------------------------------
# Check a file from <valgrind>/include
#---------------------------------------------------------------------
sub check_coregrind_export_header {
my ($path_name) = @_;
foreach my $inc (get_included_files($path_name)) {
$inc = basename($inc);
# Must not include pub_core_....
if ($inc =~ /pub_core_/) {
error("File $path_name must not include $inc\n");
}
# Only pub_tool_clreq.h may include valgrind.h
if (($inc eq "valgrind.h") && ($path_name ne "include/pub_tool_clreq.h")) {
error("File $path_name should include pub_tool_clreq.h instead of $inc\n");
}
}
}
#---------------------------------------------------------------------
# Check a file from <valgrind>/coregrind
#---------------------------------------------------------------------
sub check_coregrind_file {
my ($path_name) = @_;
my $file = basename($path_name);
foreach my $inc (get_included_files($path_name)) {
print "\tINCLUDE $inc\n" if ($debug);
# Only pub_tool_xyzzy.h may include pub_core_xyzzy.h
if ($inc =~ /pub_tool_/) {
my $buddy = $inc;
$buddy =~ s/pub_tool/pub_core/;
if ($file ne $buddy) {
error("File $path_name must not include $inc\n");
}
}
# Must not include valgrind.h
if ($inc eq "valgrind.h") {
error("File $path_name should include pub_core_clreq.h instead of $inc\n");
}
}
}
#---------------------------------------------------------------------
# Check a file from <valgrind>/<tool>
#---------------------------------------------------------------------
sub check_tool_file {
my ($path_name) = @_;
my $file = basename($path_name);
foreach my $inc (get_included_files($path_name)) {
print "\tINCLUDE $inc\n" if ($debug);
# Must not include pub_core_...
if ($inc =~ /pub_core_/) {
error("File $path_name must not include $inc\n");
}
# Must not include valgrind.h unless this is an export header
if ($inc eq "valgrind.h" && ! $tool_export_header{$path_name}) {
error("File $path_name should include pub_tool_clreq.h instead of $inc\n");
}
}
}
sub process_file {
my ($path_name) = @_;
print "FILE = $path_name\n" if ($debug);
if (is_coregrind_export_header($path_name)) {
check_coregrind_export_header($path_name);
} elsif (is_coregrind_file($path_name)) {
check_coregrind_file($path_name);
} elsif (is_tool_file($path_name)) {
check_tool_file($path_name);
}
}
sub error {
my ($message) = @_;
print STDERR "*** $message";
++$num_errors;
}