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