123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- #!/usr/bin/env perl
- #
- # Clean up include guards in headers
- #
- # Copyright (C) 2016 Red Hat, Inc.
- #
- # Authors:
- # Markus Armbruster <armbru@redhat.com>
- #
- # This work is licensed under the terms of the GNU GPL, version 2 or
- # (at your option) any later version. See the COPYING file in the
- # top-level directory.
- #
- # Usage: scripts/clean-header-guards.pl [OPTION]... [FILE]...
- # -c CC Use a compiler other than cc
- # -n Suppress actual cleanup
- # -v Show which files are cleaned up, and which are skipped
- #
- # Does the following:
- # - Header files without a recognizable header guard are skipped.
- # - Clean up any untidy header guards in-place. Warn if the cleanup
- # renames guard symbols, and explain how to find occurences of these
- # symbols that may have to be updated manually.
- # - Warn about duplicate header guard symbols. To make full use of
- # this warning, you should clean up *all* headers in one run.
- # - Warn when preprocessing a header with its guard symbol defined
- # produces anything but whitespace. The preprocessor is run like
- # "cc -E -DGUARD_H -c -P -", and fed the test program on stdin.
- use strict;
- use warnings;
- use Getopt::Std;
- # Stuff we don't want to clean because we import it into our tree:
- my $exclude = qr,^(disas/libvixl/|include/standard-headers/
- |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x;
- # Stuff that is expected to fail the preprocessing test:
- my $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,;
- my %guarded = ();
- my %old_guard = ();
- our $opt_c = "cc";
- our $opt_n = 0;
- our $opt_v = 0;
- getopts("c:nv");
- sub skipping {
- my ($fname, $msg, $line1, $line2) = @_;
- return if !$opt_v or $fname =~ $exclude;
- print "$fname skipped: $msg\n";
- print " $line1" if defined $line1;
- print " $line2" if defined $line2;
- }
- sub gripe {
- my ($fname, $msg) = @_;
- return if $fname =~ $exclude;
- print STDERR "$fname: warning: $msg\n";
- }
- sub slurp {
- my ($fname) = @_;
- local $/; # slurp
- open(my $in, "<", $fname)
- or die "can't open $fname for reading: $!";
- return <$in>;
- }
- sub unslurp {
- my ($fname, $contents) = @_;
- open (my $out, ">", $fname)
- or die "can't open $fname for writing: $!";
- print $out $contents
- or die "error writing $fname: $!";
- close $out
- or die "error writing $fname: $!";
- }
- sub fname2guard {
- my ($fname) = @_;
- $fname =~ tr/a-z/A-Z/;
- $fname =~ tr/A-Z0-9/_/cs;
- return $fname;
- }
- sub preprocess {
- my ($fname, $guard) = @_;
- open(my $pipe, "-|", "$opt_c -E -D$guard -c -P - <$fname")
- or die "can't run $opt_c: $!";
- while (<$pipe>) {
- if ($_ =~ /\S/) {
- gripe($fname, "not blank after preprocessing");
- last;
- }
- }
- close $pipe
- or gripe($fname, "preprocessing failed ($opt_c exit status $?)");
- }
- for my $fname (@ARGV) {
- my $text = slurp($fname);
- $text =~ m,\A(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*|,sg;
- my $pre = $&;
- unless ($text =~ /\G(.*\n)/g) {
- $text =~ /\G.*/;
- skipping($fname, "no recognizable header guard", "$&\n");
- next;
- }
- my $line1 = $1;
- unless ($text =~ /\G(.*\n)/g) {
- $text =~ /\G.*/;
- skipping($fname, "no recognizable header guard", "$&\n");
- next;
- }
- my $line2 = $1;
- my $body = substr($text, pos($text));
- unless ($line1 =~ /^\s*\#\s*(if\s*\!\s*defined(\s*\()?|ifndef)\s*
- ([A-Za-z0-9_]+)/x) {
- skipping($fname, "no recognizable header guard", $line1, $line2);
- next;
- }
- my $guard = $3;
- unless ($line2 =~ /^\s*\#\s*define\s+([A-Za-z0-9_]+)/) {
- skipping($fname, "no recognizable header guard", $line1, $line2);
- next;
- }
- my $guard2 = $1;
- unless ($guard2 eq $guard) {
- skipping($fname, "mismatched header guard ($guard vs. $guard2) ",
- $line1, $line2);
- next;
- }
- unless ($body =~ m,\A((.*\n)*)
- ([ \t]*\#[ \t]*endif([ \t]*\N*)\n)
- ((?s)(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*)
- \Z,x) {
- skipping($fname, "can't find end of header guard");
- next;
- }
- $body = $1;
- my $line3 = $3;
- my $endif_comment = $4;
- my $post = $5;
- my $oldg = $guard;
- unless ($fname =~ $exclude) {
- my @issues = ();
- $guard =~ tr/a-z/A-Z/
- and push @issues, "contains lowercase letters";
- $guard =~ s/^_+//
- and push @issues, "is a reserved identifier";
- $guard =~ s/(_H)?_*$/_H/
- and $& ne "_H" and push @issues, "doesn't end with _H";
- unless ($guard =~ /^[A-Z][A-Z0-9_]*_H/) {
- skipping($fname, "can't clean up odd guard symbol $oldg\n",
- $line1, $line2);
- next;
- }
- my $exp = fname2guard($fname =~ s,.*/,,r);
- unless ($guard =~ /\Q$exp\E\Z/) {
- $guard = fname2guard($fname =~ s,^include/,,r);
- push @issues, "doesn't match the file name";
- }
- if (@issues and $opt_v) {
- print "$fname guard $oldg needs cleanup:\n ",
- join(", ", @issues), "\n";
- }
- }
- $old_guard{$guard} = $oldg
- if $guard ne $oldg;
- if (exists $guarded{$guard}) {
- gripe($fname, "guard $guard also used by $guarded{$guard}");
- } else {
- $guarded{$guard} = $fname;
- }
- unless ($fname =~ $exclude) {
- my $newl1 = "#ifndef $guard\n";
- my $newl2 = "#define $guard\n";
- my $newl3 = "#endif\n";
- $newl3 =~ s,\Z, /* $guard */, if $endif_comment;
- if ($line1 ne $newl1 or $line2 ne $newl2 or $line3 ne $newl3) {
- $pre =~ s/\n*\Z/\n\n/ if $pre =~ /\N/;
- $body =~ s/\A\n*/\n/;
- if ($opt_n) {
- print "$fname would be cleaned up\n" if $opt_v;
- } else {
- unslurp($fname, "$pre$newl1$newl2$body$newl3$post");
- print "$fname cleaned up\n" if $opt_v;
- }
- }
- }
- preprocess($fname, $opt_n ? $oldg : $guard)
- unless $fname =~ $exclude or $fname =~ $exclude_cpp;
- }
- if (%old_guard) {
- print STDERR "warning: guard symbol renaming may break things\n";
- for my $guard (sort keys %old_guard) {
- print STDERR " $old_guard{$guard} -> $guard\n";
- }
- print STDERR "To find uses that may have to be updated try:\n";
- print STDERR " git grep -Ew '", join("|", sort values %old_guard),
- "'\n";
- }
|