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