]> git.proxmox.com Git - mirror_qemu.git/blame - scripts/clean-header-guards.pl
QEMU_PACKED: Remove gcc_struct attribute in Windows non x86 targets
[mirror_qemu.git] / scripts / clean-header-guards.pl
CommitLineData
b7d5a9c2 1#!/usr/bin/env perl
2dbc4ebc
MA
2#
3# Clean up include guards in headers
4#
5# Copyright (C) 2016 Red Hat, Inc.
6#
7# Authors:
8# Markus Armbruster <armbru@redhat.com>
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
30use strict;
b7d5a9c2 31use warnings;
2dbc4ebc
MA
32use Getopt::Std;
33
34# Stuff we don't want to clean because we import it into our tree:
35my $exclude = qr,^(disas/libvixl/|include/standard-headers/
36 |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x;
37# Stuff that is expected to fail the preprocessing test:
38my $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,;
39
40my %guarded = ();
41my %old_guard = ();
42
43our $opt_c = "cc";
44our $opt_n = 0;
45our $opt_v = 0;
46getopts("c:nv");
47
48sub skipping {
49 my ($fname, $msg, $line1, $line2) = @_;
50
51 return if !$opt_v or $fname =~ $exclude;
52 print "$fname skipped: $msg\n";
53 print " $line1" if defined $line1;
54 print " $line2" if defined $line2;
55}
56
57sub gripe {
58 my ($fname, $msg) = @_;
59 return if $fname =~ $exclude;
60 print STDERR "$fname: warning: $msg\n";
61}
62
63sub slurp {
64 my ($fname) = @_;
65 local $/; # slurp
66 open(my $in, "<", $fname)
67 or die "can't open $fname for reading: $!";
68 return <$in>;
69}
70
71sub unslurp {
72 my ($fname, $contents) = @_;
73 open (my $out, ">", $fname)
74 or die "can't open $fname for writing: $!";
75 print $out $contents
76 or die "error writing $fname: $!";
77 close $out
78 or die "error writing $fname: $!";
79}
80
81sub fname2guard {
82 my ($fname) = @_;
83 $fname =~ tr/a-z/A-Z/;
84 $fname =~ tr/A-Z0-9/_/cs;
85 return $fname;
86}
87
88sub preprocess {
89 my ($fname, $guard) = @_;
90
91 open(my $pipe, "-|", "$opt_c -E -D$guard -c -P - <$fname")
92 or die "can't run $opt_c: $!";
93 while (<$pipe>) {
94 if ($_ =~ /\S/) {
95 gripe($fname, "not blank after preprocessing");
96 last;
97 }
98 }
99 close $pipe
100 or gripe($fname, "preprocessing failed ($opt_c exit status $?)");
101}
102
103for my $fname (@ARGV) {
104 my $text = slurp($fname);
105
106 $text =~ m,\A(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*|,msg;
107 my $pre = $&;
108 unless ($text =~ /\G(.*\n)/g) {
109 $text =~ /\G.*/;
110 skipping($fname, "no recognizable header guard", "$&\n");
111 next;
112 }
113 my $line1 = $1;
114 unless ($text =~ /\G(.*\n)/g) {
115 $text =~ /\G.*/;
116 skipping($fname, "no recognizable header guard", "$&\n");
117 next;
118 }
119 my $line2 = $1;
120 my $body = substr($text, pos($text));
121
122 unless ($line1 =~ /^\s*\#\s*(if\s*\!\s*defined(\s*\()?|ifndef)\s*
123 ([A-Za-z0-9_]+)/x) {
124 skipping($fname, "no recognizable header guard", $line1, $line2);
125 next;
126 }
127 my $guard = $3;
128 unless ($line2 =~ /^\s*\#\s*define\s+([A-Za-z0-9_]+)/) {
129 skipping($fname, "no recognizable header guard", $line1, $line2);
130 next;
131 }
132 my $guard2 = $1;
133 unless ($guard2 eq $guard) {
134 skipping($fname, "mismatched header guard ($guard vs. $guard2) ",
135 $line1, $line2);
136 next;
137 }
138
139 unless ($body =~ m,\A((.*\n)*)
140 (\s*\#\s*endif\s*(/\*\s*.*\s*\*/\s*)?\n?)
141 (\n|\s)*\Z,x) {
142 skipping($fname, "can't find end of header guard");
143 next;
144 }
145 $body = $1;
146 my $line3 = $3;
147 my $endif_comment = $4;
148
149 my $oldg = $guard;
150
151 unless ($fname =~ $exclude) {
152 my @issues = ();
153 $guard =~ tr/a-z/A-Z/
154 and push @issues, "contains lowercase letters";
155 $guard =~ s/^_+//
156 and push @issues, "is a reserved identifier";
157 $guard =~ s/(_H)?_*$/_H/
158 and $& ne "_H" and push @issues, "doesn't end with _H";
159 unless ($guard =~ /^[A-Z][A-Z0-9_]*_H/) {
160 skipping($fname, "can't clean up odd guard symbol $oldg\n",
161 $line1, $line2);
162 next;
163 }
164
165 my $exp = fname2guard($fname =~ s,.*/,,r);
166 unless ($guard =~ /\Q$exp\E\Z/) {
167 $guard = fname2guard($fname =~ s,^include/,,r);
168 push @issues, "doesn't match the file name";
169 }
170 if (@issues and $opt_v) {
171 print "$fname guard $oldg needs cleanup:\n ",
172 join(", ", @issues), "\n";
173 }
174 }
175
176 $old_guard{$guard} = $oldg
177 if $guard ne $oldg;
178
179 if (exists $guarded{$guard}) {
180 gripe($fname, "guard $guard also used by $guarded{$guard}");
181 } else {
182 $guarded{$guard} = $fname;
183 }
184
185 unless ($fname =~ $exclude) {
186 my $newl1 = "#ifndef $guard\n";
187 my $newl2 = "#define $guard\n";
188 my $newl3 = "#endif\n";
189 $newl3 =~ s,\Z, /* $guard */, if defined $endif_comment;
190 if ($line1 ne $newl1 or $line2 ne $newl2 or $line3 ne $newl3) {
191 $pre =~ s/\n*\Z/\n\n/ if $pre =~ /\N/;
192 $body =~ s/\A\n*/\n/;
193 if ($opt_n) {
194 print "$fname would be cleaned up\n" if $opt_v;
195 } else {
196 unslurp($fname, "$pre$newl1$newl2$body$newl3");
197 print "$fname cleaned up\n" if $opt_v;
198 }
199 }
200 }
201
202 preprocess($fname, $opt_n ? $oldg : $guard)
203 unless $fname =~ $exclude or $fname =~ $exclude_cpp;
204}
205
206if (%old_guard) {
207 print STDERR "warning: guard symbol renaming may break things\n";
208 for my $guard (sort keys %old_guard) {
209 print STDERR " $old_guard{$guard} -> $guard\n";
210 }
211 print STDERR "To find uses that may have to be updated try:\n";
212 print STDERR " git grep -Ew '", join("|", sort values %old_guard),
213 "'\n";
214}