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