add and change NEWS for 2.8.0 release
[parrot.git] / t / codingstd / c_header_guards.t
bloba6fb981a13a818897162a831f91aa3e2c3548eec
1 #! perl
2 # Copyright (C) 2006-2009, Parrot Foundation.
3 # $Id$
5 use strict;
6 use warnings;
8 use lib qw( . lib ../lib ../../lib );
9 use Test::More tests => 5;
10 use Parrot::Distribution;
12 =head1 NAME
14 t/codingstd/c_header_guards.t - checks for rules related to guards in C header files
16 =head1 SYNOPSIS
18     # test all files
19     % prove t/codingstd/c_header_guards.t
21     # test specific files
22     % perl t/codingstd/c_header_guards.t include/parrot/bar.h
24 =head1 DESCRIPTION
26 Checks that all C language header files have an
27 #ifndef PARROT_WHATEVER_H_GUARD definition, then they
28 #define PARROT_WHATEVER_H_GUARD and add an
29 #endif /* PARROT_WHATEVER_H_GUARD */ at the end, of the file as specified
30 in PDD07.
32 =head1 AUTHOR
34 Mark Glines <mark at glines dot org>
36 =head1 SEE ALSO
38 L<docs/pdds/pdd07_codingstd.pod>
40 =cut
42 my $DIST = Parrot::Distribution->new();
43 my @files;
44 if (@ARGV) {
45     @files = <@ARGV>;
47 else {
48     my %files = map { $_->path() => 1 } $DIST->c_header_files();
49     my $href  = $DIST->generated_files();
51     foreach my $file ( keys %$href ) {
52         if ( $file =~ /\.h$/ ) {
53             $files{$file} = 1 if -f $file;
54         }
55     }
57     # not all files should be subject to the coding standards
58     foreach my $file ( keys %files ) {
59         delete $files{$file} if $DIST->is_c_exemption($file);
60     }
62     @files = sort keys %files;
65 check_header_guards(@files);
67 exit;
69 sub check_header_guards {
70     my ( %guardnames, %redundants, %collisions, %missing_guard, %missing_define, %missing_comment );
72 F: foreach my $file (@_) {
73         open my $fh, '<', $file
74             or die "Cannot open '$file' for reading!\n";
75         my @source = <$fh>;
76         close $fh;
77         chomp @source;
79         my ( $ifndef, $define, $endif );
80     L: foreach my $line (@source) {
81             $line =~ s/\s+/ /;
82             $line =~ s/^ //;
84             # skip Bison parser files
85             next F if $line =~ /A Bison parser/;
87             # skip the non-preprocessor lines
88             next L unless substr( $line, 0, 1 ) eq '#';
90             # skip the "#", and any leading whitespace
91             $line = substr( $line, 1 );
92             $line =~ s/^ //;
94             if ( $line =~ m{ifndef (PARROT_.+_GUARD)$} ) {
96                 # allow include/parrot/platform.h to have redundant guards;
97                 # it contains verbatim copies of other header files (which
98                 # have their own guards).
99                 next L if ( defined($ifndef) && $ifndef eq 'PARROT_PLATFORM_H_GUARD' );
101                 # check for multiple guards in the same file
102                 $redundants{$file} = $1 if defined $ifndef;
104                 # check for the same guard-name in multiple files
105                 if ( exists( $guardnames{$1} ) ) {
106                     if ( !duplicate_files( $file, $guardnames{$1} ) ) {
107                         $collisions{$file} = $guardnames{$1};
108                     }
109                 }
111                 $ifndef         = $1;
112                 $guardnames{$1} = $file;
113             }
115             if ( $line =~ m{define (PARROT_.+_GUARD)$} ) {
116                 $define = $1
117                     if ( defined($ifndef) && $ifndef eq $1 );
118             }
120             if ( $line =~ m{endif /\* (PARROT_.+_GUARD) \*/$} ) {
121                 $endif = $1
122                     if ( defined($ifndef) && $ifndef eq $1 );
123             }
124         }
126         $missing_guard{$file}   = 1 unless defined $ifndef;
127         $missing_define{$file}  = 1 unless defined $define;
128         $missing_comment{$file} = 1 unless defined $endif;
129     }
131     ok( !%collisions, "identical PARROT_*_GUARD macro names used in headers" )
132         or diag( "collisions: \n" . join( ", \n", %collisions ) );
134     ok( !%redundants, "multiple PARROT_*_GUARD macros found in headers" )
135         or diag( "redundants: \n" . join( ", \n", keys %redundants ) );
137     ok( !%missing_guard,
138         "missing or misspelled PARROT_*_GUARD ifndef in headers" )
139         or diag(     "missing guard: \n"
140             . join( ", \n", sort keys %missing_guard )
141             . "\nyou need to add a line like:\n"
142             . "  #ifndef PARROT_*_GUARD\n"
143             . "at the top of the header." );
145     ok( !%missing_define,
146         "missing or misspelled PARROT_*_GUARD define in headers" )
147         or diag(     "missing define: \n"
148             . join( ", \n", sort keys %missing_define )
149             . "\nyou need to add a line like:\n"
150             . "  #define PARROT_*_GUARD\n"
151             . "at the top of the header." );
153     ok( !%missing_comment, "missing or misspelled PARROT_*_GUARD "
154         . "comment after the endif in headers" )
155         or diag(     "missing endif comment: \n"
156             . join( ", \n", sort keys %missing_comment )
157             . "\nyou need to add a line like:\n"
158             . "  #endif /* PARROT_*_GUARD */\n"
159             . "at the end of the header." );
161     return 0;
164 sub duplicate_files {
165     my ( $file1, $file2 ) = @_;
166     open my $fh1, '<', $file1
167         or die "Cannot open '$file1' for reading!\n";
168     open my $fh2, '<', $file2
169         or die "Cannot open '$file2' for reading!\n";
170     local $/;
171     $file1 = <$fh1>;
172     $file2 = <$fh2>;
173     return $file1 eq $file2;
176 # Local Variables:
177 #   mode: cperl
178 #   cperl-indent-level: 4
179 #   fill-column: 100
180 # End:
181 # vim: expandtab shiftwidth=4: