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