1b7d5a9c2SKamil Rytarowski#!/usr/bin/env perl 22dbc4ebcSMarkus Armbruster# 32dbc4ebcSMarkus Armbruster# Clean up include guards in headers 42dbc4ebcSMarkus Armbruster# 52dbc4ebcSMarkus Armbruster# Copyright (C) 2016 Red Hat, Inc. 62dbc4ebcSMarkus Armbruster# 72dbc4ebcSMarkus Armbruster# Authors: 82dbc4ebcSMarkus Armbruster# Markus Armbruster <armbru@redhat.com> 92dbc4ebcSMarkus Armbruster# 102dbc4ebcSMarkus Armbruster# This work is licensed under the terms of the GNU GPL, version 2 or 112dbc4ebcSMarkus Armbruster# (at your option) any later version. See the COPYING file in the 122dbc4ebcSMarkus Armbruster# top-level directory. 132dbc4ebcSMarkus Armbruster# 142dbc4ebcSMarkus Armbruster# Usage: scripts/clean-header-guards.pl [OPTION]... [FILE]... 152dbc4ebcSMarkus Armbruster# -c CC Use a compiler other than cc 162dbc4ebcSMarkus Armbruster# -n Suppress actual cleanup 172dbc4ebcSMarkus Armbruster# -v Show which files are cleaned up, and which are skipped 182dbc4ebcSMarkus Armbruster# 192dbc4ebcSMarkus Armbruster# Does the following: 202dbc4ebcSMarkus Armbruster# - Header files without a recognizable header guard are skipped. 212dbc4ebcSMarkus Armbruster# - Clean up any untidy header guards in-place. Warn if the cleanup 22*65fdb3ccSzhaolichang# renames guard symbols, and explain how to find occurrences of these 232dbc4ebcSMarkus Armbruster# symbols that may have to be updated manually. 242dbc4ebcSMarkus Armbruster# - Warn about duplicate header guard symbols. To make full use of 252dbc4ebcSMarkus Armbruster# this warning, you should clean up *all* headers in one run. 262dbc4ebcSMarkus Armbruster# - Warn when preprocessing a header with its guard symbol defined 272dbc4ebcSMarkus Armbruster# produces anything but whitespace. The preprocessor is run like 282dbc4ebcSMarkus Armbruster# "cc -E -DGUARD_H -c -P -", and fed the test program on stdin. 292dbc4ebcSMarkus Armbruster 302dbc4ebcSMarkus Armbrusteruse strict; 31b7d5a9c2SKamil Rytarowskiuse warnings; 322dbc4ebcSMarkus Armbrusteruse Getopt::Std; 332dbc4ebcSMarkus Armbruster 342dbc4ebcSMarkus Armbruster# Stuff we don't want to clean because we import it into our tree: 352dbc4ebcSMarkus Armbrustermy $exclude = qr,^(disas/libvixl/|include/standard-headers/ 362dbc4ebcSMarkus Armbruster |linux-headers/|pc-bios/|tests/tcg/|tests/multiboot/),x; 372dbc4ebcSMarkus Armbruster# Stuff that is expected to fail the preprocessing test: 382dbc4ebcSMarkus Armbrustermy $exclude_cpp = qr,^include/libdecnumber/decNumberLocal.h,; 392dbc4ebcSMarkus Armbruster 402dbc4ebcSMarkus Armbrustermy %guarded = (); 412dbc4ebcSMarkus Armbrustermy %old_guard = (); 422dbc4ebcSMarkus Armbruster 432dbc4ebcSMarkus Armbrusterour $opt_c = "cc"; 442dbc4ebcSMarkus Armbrusterour $opt_n = 0; 452dbc4ebcSMarkus Armbrusterour $opt_v = 0; 462dbc4ebcSMarkus Armbrustergetopts("c:nv"); 472dbc4ebcSMarkus Armbruster 482dbc4ebcSMarkus Armbrustersub skipping { 492dbc4ebcSMarkus Armbruster my ($fname, $msg, $line1, $line2) = @_; 502dbc4ebcSMarkus Armbruster 512dbc4ebcSMarkus Armbruster return if !$opt_v or $fname =~ $exclude; 522dbc4ebcSMarkus Armbruster print "$fname skipped: $msg\n"; 532dbc4ebcSMarkus Armbruster print " $line1" if defined $line1; 542dbc4ebcSMarkus Armbruster print " $line2" if defined $line2; 552dbc4ebcSMarkus Armbruster} 562dbc4ebcSMarkus Armbruster 572dbc4ebcSMarkus Armbrustersub gripe { 582dbc4ebcSMarkus Armbruster my ($fname, $msg) = @_; 592dbc4ebcSMarkus Armbruster return if $fname =~ $exclude; 602dbc4ebcSMarkus Armbruster print STDERR "$fname: warning: $msg\n"; 612dbc4ebcSMarkus Armbruster} 622dbc4ebcSMarkus Armbruster 632dbc4ebcSMarkus Armbrustersub slurp { 642dbc4ebcSMarkus Armbruster my ($fname) = @_; 652dbc4ebcSMarkus Armbruster local $/; # slurp 662dbc4ebcSMarkus Armbruster open(my $in, "<", $fname) 672dbc4ebcSMarkus Armbruster or die "can't open $fname for reading: $!"; 682dbc4ebcSMarkus Armbruster return <$in>; 692dbc4ebcSMarkus Armbruster} 702dbc4ebcSMarkus Armbruster 712dbc4ebcSMarkus Armbrustersub unslurp { 722dbc4ebcSMarkus Armbruster my ($fname, $contents) = @_; 732dbc4ebcSMarkus Armbruster open (my $out, ">", $fname) 742dbc4ebcSMarkus Armbruster or die "can't open $fname for writing: $!"; 752dbc4ebcSMarkus Armbruster print $out $contents 762dbc4ebcSMarkus Armbruster or die "error writing $fname: $!"; 772dbc4ebcSMarkus Armbruster close $out 782dbc4ebcSMarkus Armbruster or die "error writing $fname: $!"; 792dbc4ebcSMarkus Armbruster} 802dbc4ebcSMarkus Armbruster 812dbc4ebcSMarkus Armbrustersub fname2guard { 822dbc4ebcSMarkus Armbruster my ($fname) = @_; 832dbc4ebcSMarkus Armbruster $fname =~ tr/a-z/A-Z/; 842dbc4ebcSMarkus Armbruster $fname =~ tr/A-Z0-9/_/cs; 852dbc4ebcSMarkus Armbruster return $fname; 862dbc4ebcSMarkus Armbruster} 872dbc4ebcSMarkus Armbruster 882dbc4ebcSMarkus Armbrustersub preprocess { 892dbc4ebcSMarkus Armbruster my ($fname, $guard) = @_; 902dbc4ebcSMarkus Armbruster 912dbc4ebcSMarkus Armbruster open(my $pipe, "-|", "$opt_c -E -D$guard -c -P - <$fname") 922dbc4ebcSMarkus Armbruster or die "can't run $opt_c: $!"; 932dbc4ebcSMarkus Armbruster while (<$pipe>) { 942dbc4ebcSMarkus Armbruster if ($_ =~ /\S/) { 952dbc4ebcSMarkus Armbruster gripe($fname, "not blank after preprocessing"); 962dbc4ebcSMarkus Armbruster last; 972dbc4ebcSMarkus Armbruster } 982dbc4ebcSMarkus Armbruster } 992dbc4ebcSMarkus Armbruster close $pipe 1002dbc4ebcSMarkus Armbruster or gripe($fname, "preprocessing failed ($opt_c exit status $?)"); 1012dbc4ebcSMarkus Armbruster} 1022dbc4ebcSMarkus Armbruster 1032dbc4ebcSMarkus Armbrusterfor my $fname (@ARGV) { 1042dbc4ebcSMarkus Armbruster my $text = slurp($fname); 1052dbc4ebcSMarkus Armbruster 106c0a9956bSMarkus Armbruster $text =~ m,\A(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*|,sg; 1072dbc4ebcSMarkus Armbruster my $pre = $&; 1082dbc4ebcSMarkus Armbruster unless ($text =~ /\G(.*\n)/g) { 1092dbc4ebcSMarkus Armbruster $text =~ /\G.*/; 1102dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", "$&\n"); 1112dbc4ebcSMarkus Armbruster next; 1122dbc4ebcSMarkus Armbruster } 1132dbc4ebcSMarkus Armbruster my $line1 = $1; 1142dbc4ebcSMarkus Armbruster unless ($text =~ /\G(.*\n)/g) { 1152dbc4ebcSMarkus Armbruster $text =~ /\G.*/; 1162dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", "$&\n"); 1172dbc4ebcSMarkus Armbruster next; 1182dbc4ebcSMarkus Armbruster } 1192dbc4ebcSMarkus Armbruster my $line2 = $1; 1202dbc4ebcSMarkus Armbruster my $body = substr($text, pos($text)); 1212dbc4ebcSMarkus Armbruster 1222dbc4ebcSMarkus Armbruster unless ($line1 =~ /^\s*\#\s*(if\s*\!\s*defined(\s*\()?|ifndef)\s* 1232dbc4ebcSMarkus Armbruster ([A-Za-z0-9_]+)/x) { 1242dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", $line1, $line2); 1252dbc4ebcSMarkus Armbruster next; 1262dbc4ebcSMarkus Armbruster } 1272dbc4ebcSMarkus Armbruster my $guard = $3; 1282dbc4ebcSMarkus Armbruster unless ($line2 =~ /^\s*\#\s*define\s+([A-Za-z0-9_]+)/) { 1292dbc4ebcSMarkus Armbruster skipping($fname, "no recognizable header guard", $line1, $line2); 1302dbc4ebcSMarkus Armbruster next; 1312dbc4ebcSMarkus Armbruster } 1322dbc4ebcSMarkus Armbruster my $guard2 = $1; 1332dbc4ebcSMarkus Armbruster unless ($guard2 eq $guard) { 1342dbc4ebcSMarkus Armbruster skipping($fname, "mismatched header guard ($guard vs. $guard2) ", 1352dbc4ebcSMarkus Armbruster $line1, $line2); 1362dbc4ebcSMarkus Armbruster next; 1372dbc4ebcSMarkus Armbruster } 1382dbc4ebcSMarkus Armbruster 1392dbc4ebcSMarkus Armbruster unless ($body =~ m,\A((.*\n)*) 140c0a9956bSMarkus Armbruster ([ \t]*\#[ \t]*endif([ \t]*\N*)\n) 141c0a9956bSMarkus Armbruster ((?s)(\s*\n|\s*//\N*\n|\s*/\*.*?\*/\s*\n)*) 142c0a9956bSMarkus Armbruster \Z,x) { 1432dbc4ebcSMarkus Armbruster skipping($fname, "can't find end of header guard"); 1442dbc4ebcSMarkus Armbruster next; 1452dbc4ebcSMarkus Armbruster } 1462dbc4ebcSMarkus Armbruster $body = $1; 1472dbc4ebcSMarkus Armbruster my $line3 = $3; 1482dbc4ebcSMarkus Armbruster my $endif_comment = $4; 149c0a9956bSMarkus Armbruster my $post = $5; 1502dbc4ebcSMarkus Armbruster 1512dbc4ebcSMarkus Armbruster my $oldg = $guard; 1522dbc4ebcSMarkus Armbruster 1532dbc4ebcSMarkus Armbruster unless ($fname =~ $exclude) { 1542dbc4ebcSMarkus Armbruster my @issues = (); 1552dbc4ebcSMarkus Armbruster $guard =~ tr/a-z/A-Z/ 1562dbc4ebcSMarkus Armbruster and push @issues, "contains lowercase letters"; 1572dbc4ebcSMarkus Armbruster $guard =~ s/^_+// 1582dbc4ebcSMarkus Armbruster and push @issues, "is a reserved identifier"; 1592dbc4ebcSMarkus Armbruster $guard =~ s/(_H)?_*$/_H/ 1602dbc4ebcSMarkus Armbruster and $& ne "_H" and push @issues, "doesn't end with _H"; 1612dbc4ebcSMarkus Armbruster unless ($guard =~ /^[A-Z][A-Z0-9_]*_H/) { 1622dbc4ebcSMarkus Armbruster skipping($fname, "can't clean up odd guard symbol $oldg\n", 1632dbc4ebcSMarkus Armbruster $line1, $line2); 1642dbc4ebcSMarkus Armbruster next; 1652dbc4ebcSMarkus Armbruster } 1662dbc4ebcSMarkus Armbruster 1672dbc4ebcSMarkus Armbruster my $exp = fname2guard($fname =~ s,.*/,,r); 1682dbc4ebcSMarkus Armbruster unless ($guard =~ /\Q$exp\E\Z/) { 1692dbc4ebcSMarkus Armbruster $guard = fname2guard($fname =~ s,^include/,,r); 1702dbc4ebcSMarkus Armbruster push @issues, "doesn't match the file name"; 1712dbc4ebcSMarkus Armbruster } 1722dbc4ebcSMarkus Armbruster if (@issues and $opt_v) { 1732dbc4ebcSMarkus Armbruster print "$fname guard $oldg needs cleanup:\n ", 1742dbc4ebcSMarkus Armbruster join(", ", @issues), "\n"; 1752dbc4ebcSMarkus Armbruster } 1762dbc4ebcSMarkus Armbruster } 1772dbc4ebcSMarkus Armbruster 1782dbc4ebcSMarkus Armbruster $old_guard{$guard} = $oldg 1792dbc4ebcSMarkus Armbruster if $guard ne $oldg; 1802dbc4ebcSMarkus Armbruster 1812dbc4ebcSMarkus Armbruster if (exists $guarded{$guard}) { 1822dbc4ebcSMarkus Armbruster gripe($fname, "guard $guard also used by $guarded{$guard}"); 1832dbc4ebcSMarkus Armbruster } else { 1842dbc4ebcSMarkus Armbruster $guarded{$guard} = $fname; 1852dbc4ebcSMarkus Armbruster } 1862dbc4ebcSMarkus Armbruster 1872dbc4ebcSMarkus Armbruster unless ($fname =~ $exclude) { 1882dbc4ebcSMarkus Armbruster my $newl1 = "#ifndef $guard\n"; 1892dbc4ebcSMarkus Armbruster my $newl2 = "#define $guard\n"; 1902dbc4ebcSMarkus Armbruster my $newl3 = "#endif\n"; 191c0a9956bSMarkus Armbruster $newl3 =~ s,\Z, /* $guard */, if $endif_comment; 1922dbc4ebcSMarkus Armbruster if ($line1 ne $newl1 or $line2 ne $newl2 or $line3 ne $newl3) { 1932dbc4ebcSMarkus Armbruster $pre =~ s/\n*\Z/\n\n/ if $pre =~ /\N/; 1942dbc4ebcSMarkus Armbruster $body =~ s/\A\n*/\n/; 1952dbc4ebcSMarkus Armbruster if ($opt_n) { 1962dbc4ebcSMarkus Armbruster print "$fname would be cleaned up\n" if $opt_v; 1972dbc4ebcSMarkus Armbruster } else { 198c0a9956bSMarkus Armbruster unslurp($fname, "$pre$newl1$newl2$body$newl3$post"); 1992dbc4ebcSMarkus Armbruster print "$fname cleaned up\n" if $opt_v; 2002dbc4ebcSMarkus Armbruster } 2012dbc4ebcSMarkus Armbruster } 2022dbc4ebcSMarkus Armbruster } 2032dbc4ebcSMarkus Armbruster 2042dbc4ebcSMarkus Armbruster preprocess($fname, $opt_n ? $oldg : $guard) 2052dbc4ebcSMarkus Armbruster unless $fname =~ $exclude or $fname =~ $exclude_cpp; 2062dbc4ebcSMarkus Armbruster} 2072dbc4ebcSMarkus Armbruster 2082dbc4ebcSMarkus Armbrusterif (%old_guard) { 2092dbc4ebcSMarkus Armbruster print STDERR "warning: guard symbol renaming may break things\n"; 2102dbc4ebcSMarkus Armbruster for my $guard (sort keys %old_guard) { 2112dbc4ebcSMarkus Armbruster print STDERR " $old_guard{$guard} -> $guard\n"; 2122dbc4ebcSMarkus Armbruster } 2132dbc4ebcSMarkus Armbruster print STDERR "To find uses that may have to be updated try:\n"; 2142dbc4ebcSMarkus Armbruster print STDERR " git grep -Ew '", join("|", sort values %old_guard), 2152dbc4ebcSMarkus Armbruster "'\n"; 2162dbc4ebcSMarkus Armbruster} 217