]> Git Repo - qemu.git/blob - scripts/clean-header-guards.pl
Merge remote-tracking branch 'remotes/ehabkost/tags/x86-pull-request' into staging
[qemu.git] / scripts / clean-header-guards.pl
1 #!/usr/bin/perl -w
2 #
3 # Clean up include guards in headers
4 #
5 # Copyright (C) 2016 Red Hat, Inc.
6 #
7 # Authors:
8 #  Markus Armbruster <[email protected]>
9 #
10 # This work is licensed under the terms of the GNU GPL, version 2 or
11 # (at your option) any later version. See the COPYING file in the
12 # top-level directory.
13 #
14 # Usage: scripts/clean-header-guards.pl [OPTION]... [FILE]...
15 #     -c CC     Use a compiler other than cc
16 #     -n        Suppress actual cleanup
17 #     -v        Show which files are cleaned up, and which are skipped
18 #
19 # Does the following:
20 # - Header files without a recognizable header guard are skipped.
21 # - Clean up any untidy header guards in-place.  Warn if the cleanup
22 #   renames guard symbols, and explain how to find occurences of these
23 #   symbols that may have to be updated manually.
24 # - Warn about duplicate header guard symbols.  To make full use of
25 #   this warning, you should clean up *all* headers in one run.
26 # - Warn when preprocessing a header with its guard symbol defined
27 #   produces anything but whitespace.  The preprocessor is run like
28 #   "cc -E -DGUARD_H -c -P -", and fed the test program on stdin.
29
30 use strict;
31 use Getopt::Std;
32
33 # Stuff we don't want to clean because we import it into our tree:
34 my $exclude = qr,^(disas/libvixl/|include/standard-headers/
35     |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x;
36 # Stuff that is expected to fail the preprocessing test:
37 my $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,;
38
39 my %guarded = ();
40 my %old_guard = ();
41
42 our $opt_c = "cc";
43 our $opt_n = 0;
44 our $opt_v = 0;
45 getopts("c:nv");
46
47 sub skipping {
48     my ($fname, $msg, $line1, $line2) = @_;
49
50     return if !$opt_v or $fname =~ $exclude;
51     print "$fname skipped: $msg\n";
52     print "    $line1" if defined $line1;
53     print "    $line2" if defined $line2;
54 }
55
56 sub gripe {
57     my ($fname, $msg) = @_;
58     return if $fname =~ $exclude;
59     print STDERR "$fname: warning: $msg\n";
60 }
61
62 sub slurp {
63     my ($fname) = @_;
64     local $/;                   # slurp
65     open(my $in, "<", $fname)
66         or die "can't open $fname for reading: $!";
67     return <$in>;
68 }
69
70 sub unslurp {
71     my ($fname, $contents) = @_;
72     open (my $out, ">", $fname)
73         or die "can't open $fname for writing: $!";
74     print $out $contents
75         or die "error writing $fname: $!";
76     close $out
77         or die "error writing $fname: $!";
78 }
79
80 sub fname2guard {
81     my ($fname) = @_;
82     $fname =~ tr/a-z/A-Z/;
83     $fname =~ tr/A-Z0-9/_/cs;
84     return $fname;
85 }
86
87 sub preprocess {
88     my ($fname, $guard) = @_;
89
90     open(my $pipe, "-|", "$opt_c -E -D$guard -c -P - <$fname")
91         or die "can't run $opt_c: $!";
92     while (<$pipe>) {
93         if ($_ =~ /\S/) {
94             gripe($fname, "not blank after preprocessing");
95             last;
96         }
97     }
98     close $pipe
99         or gripe($fname, "preprocessing failed ($opt_c exit status $?)");
100 }
101
102 for my $fname (@ARGV) {
103     my $text = slurp($fname);
104
105     $text =~ m,\A(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*|,msg;
106     my $pre = $&;
107     unless ($text =~ /\G(.*\n)/g) {
108         $text =~ /\G.*/;
109         skipping($fname, "no recognizable header guard", "$&\n");
110         next;
111     }
112     my $line1 = $1;
113     unless ($text =~ /\G(.*\n)/g) {
114         $text =~ /\G.*/;
115         skipping($fname, "no recognizable header guard", "$&\n");
116         next;
117     }
118     my $line2 = $1;
119     my $body = substr($text, pos($text));
120
121     unless ($line1 =~ /^\s*\#\s*(if\s*\!\s*defined(\s*\()?|ifndef)\s*
122                        ([A-Za-z0-9_]+)/x) {
123         skipping($fname, "no recognizable header guard", $line1, $line2);
124         next;
125     }
126     my $guard = $3;
127     unless ($line2 =~ /^\s*\#\s*define\s+([A-Za-z0-9_]+)/) {
128         skipping($fname, "no recognizable header guard", $line1, $line2);
129         next;
130     }
131     my $guard2 = $1;
132     unless ($guard2 eq $guard) {
133         skipping($fname, "mismatched header guard ($guard vs. $guard2) ",
134                  $line1, $line2);
135         next;
136     }
137
138     unless ($body =~ m,\A((.*\n)*)
139                        (\s*\#\s*endif\s*(/\*\s*.*\s*\*/\s*)?\n?)
140                        (\n|\s)*\Z,x) {
141         skipping($fname, "can't find end of header guard");
142         next;
143     }
144     $body = $1;
145     my $line3 = $3;
146     my $endif_comment = $4;
147
148     my $oldg = $guard;
149
150     unless ($fname =~ $exclude) {
151         my @issues = ();
152         $guard =~ tr/a-z/A-Z/
153             and push @issues, "contains lowercase letters";
154         $guard =~ s/^_+//
155             and push @issues, "is a reserved identifier";
156         $guard =~ s/(_H)?_*$/_H/
157             and $& ne "_H" and push @issues, "doesn't end with _H";
158         unless ($guard =~ /^[A-Z][A-Z0-9_]*_H/) {
159             skipping($fname, "can't clean up odd guard symbol $oldg\n",
160                      $line1, $line2);
161             next;
162         }
163
164         my $exp = fname2guard($fname =~ s,.*/,,r);
165         unless ($guard =~ /\Q$exp\E\Z/) {
166             $guard = fname2guard($fname =~ s,^include/,,r);
167             push @issues, "doesn't match the file name";
168         }
169         if (@issues and $opt_v) {
170             print "$fname guard $oldg needs cleanup:\n    ",
171                 join(", ", @issues), "\n";
172         }
173     }
174
175     $old_guard{$guard} = $oldg
176         if $guard ne $oldg;
177
178     if (exists $guarded{$guard}) {
179         gripe($fname, "guard $guard also used by $guarded{$guard}");
180     } else {
181         $guarded{$guard} = $fname;
182     }
183
184     unless ($fname =~ $exclude) {
185         my $newl1 = "#ifndef $guard\n";
186         my $newl2 = "#define $guard\n";
187         my $newl3 = "#endif\n";
188         $newl3 =~ s,\Z, /* $guard */, if defined $endif_comment;
189         if ($line1 ne $newl1 or $line2 ne $newl2 or $line3 ne $newl3) {
190             $pre =~ s/\n*\Z/\n\n/ if $pre =~ /\N/;
191             $body =~ s/\A\n*/\n/;
192             if ($opt_n) {
193                 print "$fname would be cleaned up\n" if $opt_v;
194             } else {
195                 unslurp($fname, "$pre$newl1$newl2$body$newl3");
196                 print "$fname cleaned up\n" if $opt_v;
197             }
198         }
199     }
200
201     preprocess($fname, $opt_n ? $oldg : $guard)
202         unless $fname =~ $exclude or $fname =~ $exclude_cpp;
203 }
204
205 if (%old_guard) {
206     print STDERR "warning: guard symbol renaming may break things\n";
207     for my $guard (sort keys %old_guard) {
208         print STDERR "    $old_guard{$guard} -> $guard\n";
209     }
210     print STDERR "To find uses that may have to be updated try:\n";
211     print STDERR "    git grep -Ew '", join("|", sort values %old_guard),
212         "'\n";
213 }
This page took 0.03583 seconds and 4 git commands to generate.