217 lines
6.4 KiB
Perl
Executable File
217 lines
6.4 KiB
Perl
Executable File
#!/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 occurrences 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";
|
|
}
|