clean-header-guards.pl 6.4 KB

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