[util] config-local.h to avoid accidental commits
[people/dverkamp/gpxe.git] / src / util / mkconfig.pl
1 #!/usr/bin/perl -w
2
3 use File::Spec::Functions qw ( :ALL );
4 use File::stat;
5 use strict;
6 use warnings;
7
8 my $cfgdir = "config";
9 my $config_h = shift || "config.h";
10 my @input_files;
11
12 # Read in a whole file
13 #
14 sub read_file {
15   my $file = shift;
16
17   open my $fh, "<$file" or die "Could not open file $file: $!\n";
18   local $/;
19   my $data = <$fh>;
20   close $fh;
21   return $data;
22 }
23
24 # Write out a whole file
25 #
26 sub write_file {
27   my $file = shift;
28   my $data = shift;
29
30   open my $fh, ">$file" or die "Could not write $file: $!\n";
31   print $fh $data;
32   close $fh;
33 }
34
35 # Delete a file
36 #
37 sub delete_file {
38   my $file = shift;
39
40   unlink $file or die "Could not delete $file: $!\n";
41 }
42
43 # Get a file modification time
44 #
45 sub file_mtime {
46   my $file = shift;
47
48   my $stat = stat ( $file ) or die "Could not stat $file: $!\n";
49   return $stat->mtime;
50 }
51
52 # Read all the .h files in a directory
53 #
54 sub read_dir {
55   my $dir = shift;
56
57   opendir my $dh, $dir or die "Could not open directory $dir: $!\n";
58   my @entries = grep { /\.h$/ } readdir $dh;
59   closedir $dh;
60   return @entries;
61 }
62
63 # Get the current configuration by reading the configuration file
64 # fragments
65 #
66 sub current_config {
67   my $dir = shift;
68
69   my $cfg = {};
70   foreach my $file ( read_dir ( $dir ) ) {
71     $cfg->{$file} = read_file ( catfile ( $dir, $file ) );
72   }
73   return $cfg;
74 }
75
76 # Calculate guard name for a header file
77 #
78 sub guard {
79   my $name = shift;
80
81   $name =~ s/\W/_/g;
82   return "CONFIG_".( uc $name );
83 }
84
85 # Calculate preamble for a header file
86 #
87 sub preamble {
88   my $name = shift;
89   my $master = shift;
90
91   my $guard = guard ( $name );
92   my $preamble = <<"EOF";
93 /*
94  * This file is automatically generated from $master.  Do not edit this
95  * file; edit $master instead.
96  *
97  */
98
99 #ifndef $guard
100 #define $guard
101 EOF
102   return $preamble;
103 }
104
105 # Calculate postamble for a header file
106 #
107 sub postamble {
108   my $name = shift;
109
110   my $guard = guard ( $name );
111   return "\n#endif /* $guard */\n";
112
113
114 # Parse one config.h file into an existing configuration
115 #
116 sub parse_config {
117   my $file = shift;
118   my $cfg = shift;
119   my $cursor = "";
120
121   push ( @input_files, $file );
122
123   open my $fh, "<$file" or die "Could not open $file: $!\n";
124   while ( <$fh> ) {
125     if ( ( my $newcursor, my $suffix ) = /\@BEGIN\s+(\w+\.h)(.*)$/ ) {
126       die "Missing \"\@END $cursor\" before \"\@BEGIN $1\""
127           ." at $file line $.\n" if $cursor;
128       $cursor = $newcursor;
129       $cfg->{$cursor} = preamble ( $cursor, $file )
130           unless exists $cfg->{$cursor};
131       $cfg->{$cursor} .= "\n/*".$suffix."\n";
132     } elsif ( ( my $prefix, my $oldcursor ) = /^(.*)\@END\s+(\w+\.h)/ ) {
133       die "Missing \"\@BEGIN $oldcursor\" before \"\@END $oldcursor\""
134           ." at $file line $.\n" unless $cursor eq $oldcursor;
135       $cfg->{$cursor} .= $prefix."*/\n";
136       $cursor = "";
137     } elsif ( ( my $newfile ) = /\@TRYSOURCE\s+([\w\-]+\.h)/ ) {
138       die "Missing \"\@END $cursor\" before \"\@TRYSOURCE $newfile\""
139           ." at $file line $.\n" if $cursor;
140       parse_config ( $newfile, $cfg ) if -e $newfile;
141     } else {
142       $cfg->{$cursor} .= $_ if $cursor;
143     }
144   }
145   close $fh;
146   die "Missing \"\@END $cursor\" in $file\n" if $cursor;
147 }
148
149 # Get the new configuration by splitting config.h file using the
150 # @BEGIN/@END tags
151 #
152 sub new_config {
153   my $file = shift;
154   my $cfg = {};
155
156   parse_config ( $file, $cfg );
157
158   foreach my $cursor ( keys %$cfg ) {
159     $cfg->{$cursor} .= postamble ( $cursor );
160   }
161
162   return $cfg;
163 }  
164
165 #############################################################################
166 #
167 # Main program
168
169 # Read in current config file fragments
170 #
171 my $current = current_config ( $cfgdir );
172
173 # Read in config.h and split it into fragments
174 #
175 my $new = new_config ( $config_h );
176
177 # Delete any no-longer-wanted config file fragments
178 #
179 foreach my $file ( keys %$current ) {
180   unlink catfile ( $cfgdir, $file ) unless exists $new->{$file};
181 }
182
183 # Write out any modified fragments, and find the oldest timestamp of
184 # any unmodified fragments.
185 #
186 my $oldest = time ();
187 foreach my $file ( keys %$new ) {
188   if ( $current->{$file} && $new->{$file} eq $current->{$file} ) {
189     # Unmodified
190     my $time = file_mtime ( catfile ( $cfgdir, $file ) );
191     $oldest = $time if $time < $oldest;
192   } else {
193     write_file ( catfile ( $cfgdir, $file ), $new->{$file} );
194   }
195 }
196
197 # If we now have fragments that are older than config.h, set the
198 # timestamp on each input file to match the oldest fragment, to
199 # prevent make from always attempting to rebuild the fragments.
200 #
201 foreach my $file ( @input_files ) {
202   if ( $oldest < file_mtime ( $file ) ) {
203     utime time(), $oldest, $file or die "Could not touch $file: $!\n";
204   }
205 }