Commit | Line | Data |
---|---|---|
6092c506 | 1 | #!/usr/bin/perl |
459d3fb5 MBT |
2 | |
3 | use sort "stable"; | |
4 | BEGIN { $ENV{LC_ALL} = "C"; } | |
a8ae8817 DH |
5 | |
6 | use FindBin; | |
5bc5d425 | 7 | use Getopt::Std; |
a8ae8817 DH |
8 | |
9 | $p5_metaconfig_base = "$FindBin::Bin/../"; | |
2edea9d0 | 10 | chdir "$p5_metaconfig_base/perl" or |
a8ae8817 | 11 | die "perl/ directory missing in $p5_metaconfig_base\n"; |
459d3fb5 | 12 | |
2edea9d0 | 13 | -w 'Configure' && -w 'config_h.SH' or |
e9be536b DH |
14 | die "both Configure and config_h.SH must be writable\n"; |
15 | ||
ba12031e DH |
16 | -l '.package' && -l 'U' or |
17 | die ".package and U should be symlinks as per README\n"; | |
18 | ||
459d3fb5 MBT |
19 | # $Id: mconfig.SH 22 2008-05-28 08:01:59Z rmanfredi $ |
20 | # | |
21 | # Copyright (c) 1991-1997, 2004-2006, Raphael Manfredi | |
22 | # | |
23 | # You may redistribute only under the terms of the Artistic Licence, | |
24 | # as specified in the README file that comes with the distribution. | |
25 | # You may reuse parts of this distribution only within the terms of | |
26 | # that same Artistic Licence; a copy of which may be found at the root | |
27 | # of the source tree for dist 4.0. | |
28 | # | |
29 | # Original Author: Larry Wall <lwall@netlabs.com> | |
30 | # Key Contributor: Harlan Stenn <harlan@mumps.pfcs.com> | |
31 | # | |
32 | # $Log: mconfig.SH,v $ | |
33 | # Revision 3.0.1.5 1995/07/25 14:19:05 ram | |
34 | # patch56: new -G option | |
35 | # | |
36 | # Revision 3.0.1.4 1994/06/20 07:11:04 ram | |
37 | # patch30: new -L option to override public library path for testing | |
38 | # | |
39 | # Revision 3.0.1.3 1994/01/24 14:20:53 ram | |
40 | # patch16: added ~/.dist_profile awareness | |
41 | # | |
42 | # Revision 3.0.1.2 1993/10/16 13:53:10 ram | |
43 | # patch12: new -M option for magic symbols and confmagic.h production | |
44 | # | |
45 | # Revision 3.0.1.1 1993/08/19 06:42:26 ram | |
46 | # patch1: leading config.sh searching was not aborting properly | |
47 | # | |
48 | # Revision 3.0 1993/08/18 12:10:17 ram | |
49 | # Baseline for dist 3.0 netwide release. | |
50 | # | |
51 | ||
52 | # Perload ON | |
53 | ||
a8ae8817 | 54 | $MC = "$p5_metaconfig_base/dist"; |
459d3fb5 MBT |
55 | $version = '3.5'; |
56 | $patchlevel = '0'; | |
57 | $grep = '/usr/bin/grep'; | |
58 | chop($date = `date`); | |
59 | &profile; # Read ~/.dist_profile | |
c3a1ef25 | 60 | &usage unless getopts("dhkmoOstvwGMVL:X:"); |
459d3fb5 MBT |
61 | |
62 | $MC = $opt_L if $opt_L; # May override public library path | |
63 | $MC = &tilda_expand($MC); # ~name expansion | |
64 | chop($WD = `pwd`); # Working directory | |
65 | chdir $MC || die "Can't chdir to $MC: $!\n"; | |
66 | chop($MC = `pwd`); # Real metaconfig lib path (no symbolic links) | |
67 | chdir $WD || die "Can't chdir back to $WD: $!\n"; | |
68 | ||
69 | ++$opt_k if $opt_d; | |
70 | ++$opt_M if -f 'confmagic.h'; # Force -M if confmagic.h already there | |
71 | ||
72 | if ($opt_V) { | |
73 | print STDERR "metaconfig $version PL$patchlevel\n"; | |
74 | exit 0; | |
75 | } elsif ($opt_h) { | |
76 | &usage; | |
77 | } | |
78 | ||
79 | unlink 'Wanted' unless $opt_w; # Wanted rebuilt if no -w | |
80 | unlink 'Obsolete' unless $opt_w; # Obsolete file rebuilt if no -w | |
81 | &readpackage; # Merely get the package's name | |
82 | &init; # Various initializations | |
720e47b7 AC |
83 | my %excluded_symbol; |
84 | read_exclusions($opt_X); # Load -X or .package equivalent | |
459d3fb5 MBT |
85 | `mkdir .MT 2>&1` unless -d '.MT'; # For private temporary files |
86 | ||
87 | &locate_units; # Fill in @ARGV with a unit list | |
88 | &extract_dependencies; # Extract dependencies from units | |
89 | &extract_filenames; # Extract files to be scanned for | |
90 | &build_wanted; # Build a list of wanted symbols in file Wanted | |
91 | &build_makefile; # To do the transitive closure of dependencies | |
92 | &solve_dependencies; # Now run the makefile to close dependency graph | |
93 | &create_configure; # Create the Configure script and related files | |
94 | &cosmetic_update; # Update the manifests | |
95 | ||
96 | if ($opt_k) { | |
97 | print "Leaving subdirectory .MT unremoved so you can peruse it.\n" | |
98 | unless $opt_s; | |
99 | } else { | |
100 | `rm -rf .MT 2>&1`; | |
101 | } | |
e9be536b | 102 | system $^X, "Porting/config_h.pl"; |
459d3fb5 MBT |
103 | print "Done.\n" unless $opt_s; |
104 | ||
105 | # General initializations | |
106 | sub init { | |
107 | &init_except; # Token which have upper-cased letters | |
108 | &init_keep; # The keep status for built-in interpreter | |
109 | &init_priority; # Priorities for diadic operators | |
110 | &init_constants; # Define global constants | |
111 | &init_depend; # The %Depend array records control line handling | |
112 | } | |
113 | ||
114 | sub init_constants { | |
115 | $NEWMANI = 'MANIFEST.new'; # List of files to be scanned | |
116 | $MANI = 'MANIFEST'; # For manifake | |
117 | ||
118 | # The distinction between MANIFEST.new and MANIFEST can make sense | |
119 | # when the "pat" tools are used, but if only metaconfig is used, then | |
120 | # we can very well leave without a MANIFEST.new. --RAM, 2006-08-25 | |
121 | $NEWMANI = $MANI if -f $MANI && ! -f $NEWMANI; | |
122 | } | |
123 | ||
124 | # Record the exceptions -- almost all symbols but these are lower case | |
125 | # We also use three symbols from Unix.U for default file suffixes. | |
126 | sub init_except { | |
127 | $Except{'Author'}++; | |
128 | $Except{'Date'}++; | |
129 | $Except{'Header'}++; | |
130 | $Except{'Id'}++; | |
131 | $Except{'Locker'}++; | |
132 | $Except{'Log'}++; | |
133 | $Except{'RCSfile'}++; | |
134 | $Except{'Revision'}++; | |
135 | $Except{'Source'}++; | |
136 | $Except{'State'}++; | |
137 | $Except{'_a'}++; | |
138 | $Except{'_o'}++; | |
139 | $Except{'_exe'}++; | |
140 | } | |
141 | ||
142 | # Print out metaconfig's usage and exits | |
143 | sub usage { | |
144 | print STDERR <<'EOH'; | |
2a192f88 | 145 | Usage: metaconfig [-dhkmostvwGMV] [-L dir] [-X file] |
459d3fb5 MBT |
146 | -d : debug mode. |
147 | -h : print this help message and exits. | |
148 | -k : keep temporary directory. | |
149 | -m : assume lots of memory and swap space. | |
150 | -o : maps obsolete symbols on new ones. | |
151 | -s : silent mode. | |
152 | -t : trace symbols as they are found. | |
153 | -v : verbose mode. | |
154 | -w : trust Wanted file as being up-to-date. | |
155 | -G : also provide a GNU configure-like front end. | |
156 | -L : specify main units repository. | |
157 | -M : activate production of confmagic.h. | |
158 | -V : print version number and exits. | |
2a192f88 | 159 | -X : read symbol exclusions from file (overriding .package) |
459d3fb5 MBT |
160 | EOH |
161 | exit 1; | |
162 | } | |
163 | ||
164 | package locate; | |
165 | ||
166 | # Locate the units and push their path in @ARGV (sorted alphabetically) | |
167 | sub main'locate_units { | |
168 | print "Locating units...\n" unless $main'opt_s; | |
169 | local(*WD) = *main'WD; # Current working directory | |
170 | local(*MC) = *main'MC; # Public metaconfig library | |
171 | undef %myUlist; # Records private units paths | |
172 | undef %myUseen; # Records private/public conflicts | |
173 | &private_units; # Locate private units in @myUlist | |
174 | &public_units; # Locate public units in @ARGV | |
175 | @ARGV = sort @ARGV; # Sort it alphabetically | |
176 | push(@ARGV, sort @myUlist); # Append user's units sorted | |
177 | &dump_list if $main'opt_v; # Dump the list of units | |
178 | } | |
179 | ||
180 | # Dump the list of units on stdout | |
181 | sub dump_list { | |
182 | print "\t"; | |
183 | $, = "\n\t"; | |
184 | print @ARGV; | |
185 | $, = ''; | |
186 | print "\n"; | |
187 | } | |
188 | ||
189 | # Scan private units | |
190 | sub private_units { | |
191 | return unless -d 'U'; # Nothing to be done if no 'U' entry | |
192 | local(*ARGV) = *myUlist; # Really fill in @myUlist | |
193 | local($MC) = $WD; # We are really in the working directory | |
194 | &units_path("U"); # Locate units in the U directory | |
195 | local($unit_name); # Unit's name (without .U) | |
196 | local(@kept); # Array of kept units | |
197 | # Loop over the units and remove duplicates (the first one seen is the one | |
198 | # we keep). Also set the %myUseen H table to record private units seen. | |
199 | foreach (@ARGV) { | |
200 | ($unit_name) = m|^.*/(.*)\.U$|; # Get unit's name from path | |
201 | next if $myUseen{$unit_name}; # Already recorded | |
202 | $myUseen{$unit_name} = 1; # Record pirvate unit | |
203 | push(@kept, $_); # Keep this unit | |
204 | } | |
205 | @ARGV = @kept; | |
206 | } | |
207 | ||
208 | # Scan public units | |
209 | sub public_units { | |
210 | chdir($MC) || die "Can't find directory $MC.\n"; | |
211 | &units_path("U"); # Locate units in public U directory | |
212 | chdir($WD) || die "Can't go back to directory $WD.\n"; | |
213 | local($path); # Relative path from $WD | |
214 | local($unit_name); # Unit's name (without .U) | |
215 | local(*Unit) = *main'Unit; # Unit is a global from main package | |
216 | local(@kept); # Units kept | |
217 | local(%warned); # Units which have already issued a message | |
218 | # Loop over all the units and keep only the ones that were not found in | |
219 | # the user's U directory. As it is possible two or more units with the same | |
220 | # name be found in | |
221 | foreach (@ARGV) { | |
222 | ($unit_name) = m|^.*/(.*)\.U$|; # Get unit's name from path | |
223 | next if $warned{$unit_name}; # We have already seen this unit | |
224 | $warned{$unit_name} = 1; # Remember we have warned the user | |
225 | if ($myUseen{$unit_name}) { # User already has a private unit | |
226 | $path = $Unit{$unit_name}; # Extract user's unit path | |
227 | next if $path eq $_; # Same path, we must be in mcon/ | |
228 | $path =~ s|^$WD/||o; # Weed out leading working dir path | |
229 | $::opt_O and next; | |
230 | print " Your private $path overrides the public one.\n" | |
231 | unless $main'opt_s; | |
232 | } else { | |
233 | push(@kept, $_); # We may keep this one | |
234 | } | |
235 | } | |
236 | @ARGV = @kept; | |
237 | } | |
238 | ||
239 | # Recursively locate units in the directory. Each file ending with .U has to be | |
240 | # a unit. Others are stat()'ed, and if they are a directory, they are also | |
241 | # scanned through. The $MC and @ARGV variable are dynamically set by the caller. | |
242 | sub units_path { | |
243 | local($dir) = @_; # Directory where units are to be found | |
244 | local(@contents); # Contents of the directory | |
245 | local($unit_name); # Unit's name, without final .U | |
246 | local($path); # Full path of a unit | |
247 | local(*Unit) = *main'Unit; # Unit is a global from main package | |
248 | unless (opendir(DIR, $dir)) { | |
249 | warn("Cannot open directory $dir.\n"); | |
250 | return; | |
251 | } | |
252 | print "Locating in $MC/$dir...\n" if $main'opt_v; | |
253 | @contents = readdir DIR; # Slurp the whole thing | |
254 | closedir DIR; # And close dir, ready for recursion | |
90dcc1e7 | 255 | foreach (sort @contents) { |
459d3fb5 MBT |
256 | next if $_ eq '.' || $_ eq '..'; |
257 | if (/\.U$/) { # A unit, definitely | |
258 | ($unit_name) = /^(.*)\.U$/; | |
259 | $path = "$MC/$dir/$_"; # Full path of unit | |
260 | push(@ARGV, $path); # Record its path | |
261 | if (defined $Unit{$unit_name}) { # Already seen this unit | |
262 | if ($main'opt_v) { | |
263 | ($path) = $Unit{$unit_name} =~ m|^(.*)/.*|; | |
264 | print " We've already seen $unit_name.U in $path.\n"; | |
265 | } | |
266 | } else { | |
267 | $Unit{$unit_name} = $path; # Map name to path | |
268 | } | |
269 | next; | |
270 | } | |
271 | # We have found a file which does not look like a unit. If it is a | |
272 | # directory, then scan it. Otherwise skip the file. | |
273 | unless (-d "$dir/$_") { | |
274 | print " Skipping file $_ in $dir.\n" if $main'opt_v; | |
275 | next; | |
276 | } | |
277 | &units_path("$dir/$_"); | |
278 | print "Back to $MC/$dir...\n" if $main'opt_v; | |
279 | } | |
280 | } | |
281 | ||
282 | package main; | |
283 | ||
284 | # Initialize the extraction process by setting some variables. | |
285 | # We return a string to be eval to do more customized initializations. | |
286 | sub init_extraction { | |
287 | open(INIT, ">$WD/.MT/Init.U") || | |
288 | die "Can't create .MT/Init.U\n"; | |
289 | open(CONF_H, ">$WD/.MT/Config_h.U") || | |
290 | die "Can't create .MT/Config_h.U\n"; | |
291 | open(EXTERN, ">$WD/.MT/Extern.U") || | |
292 | die "Can't create .MT/Extern.U\n"; | |
293 | open(MAGIC_H, ">$WD/.MT/Magic_h.U") || | |
294 | die "Can't create .MT/Magic_h.U\n"; | |
295 | ||
296 | $c_symbol = ''; # Current symbol seen in ?C: lines | |
297 | $s_symbol = ''; # Current symbol seen in ?S: lines | |
298 | $m_symbol = ''; # Current symbol seen in ?M: lines | |
299 | $heredoc = ''; # Last "here" document symbol seen | |
300 | $heredoc_nosubst = 0; # True for <<'EOM' here docs | |
301 | $condlist = ''; # List of conditional symbols | |
302 | $defined = ''; # List of defined symbols in the unit | |
303 | $body = ''; # No procedure to handle body | |
304 | $ending = ''; # No procedure to clean-up | |
305 | } | |
306 | ||
307 | # End the extraction process | |
308 | sub end_extraction { | |
309 | close EXTERN; # External dependencies (libraries, includes...) | |
310 | close CONF_H; # C symbol definition template | |
311 | close INIT; # Required initializations | |
312 | close MAGIC; # Magic C symbol redefinition templates | |
313 | ||
314 | print $dependencies if $opt_v; # Print extracted dependencies | |
315 | } | |
316 | ||
317 | # Process the ?MAKE: line | |
318 | sub p_make { | |
319 | local($_) = @_; | |
320 | local(@ary); # Locally defined symbols | |
321 | local(@dep); # Dependencies | |
322 | if (/^[\w+ ]*:/) { # Main dependency rule | |
323 | s|^\s*||; # Remove leading spaces | |
324 | chop; | |
325 | s/:(.*)//; | |
326 | @dep = split(' ', $1); # Dependencies | |
327 | @ary = split(' '); # Locally defined symbols | |
328 | foreach $sym (@ary) { | |
329 | # Symbols starting with a '+' are meant for internal use only. | |
330 | next if $sym =~ s/^\+//; | |
331 | # Only sumbols starting with a lowercase letter are to | |
332 | # appear in config.sh, excepted the ones listed in Except. | |
333 | if ($sym =~ /^[_a-z]/ || $Except{$sym}) { | |
334 | $shmaster{"\$$sym"} = undef; | |
335 | push(@Master,"?$unit:$sym=''\n"); # Initializations | |
336 | } | |
337 | } | |
338 | $condlist = ''; # List of conditional symbols | |
339 | local($sym); # Symbol copy, avoid @dep alteration | |
340 | foreach $dep (@dep) { | |
341 | if ($dep =~ /^\+[A-Za-z]/) { | |
342 | ($sym = $dep) =~ s|^\+||; | |
343 | $condlist .= "$sym "; | |
344 | push(@Cond, $sym) unless $condseen{$sym}; | |
345 | $condseen{$sym}++; # Conditionally wanted | |
346 | } | |
347 | } | |
348 | # Append to already existing dependencies. The 'defined' variable | |
349 | # is set for &write_out, used to implement ?L: and ?I: canvas. It is | |
350 | # reset each time a new unit is parsed. | |
351 | # NB: leading '+' for defined symbols (internal use only) have been | |
352 | # removed at this point, but conditional dependencies still bear it. | |
353 | $defined = join(' ', @ary); # Symbols defined by this unit | |
354 | $dependencies .= $defined . ':' . join(' ', @dep) . "\n"; | |
355 | $dependencies .= " -cond $condlist\n" if $condlist; | |
356 | } else { | |
357 | $dependencies .= $_; # Building rules | |
358 | } | |
359 | } | |
360 | ||
361 | # Process the ?O: line | |
362 | sub p_obsolete { | |
363 | local($_) = @_; | |
364 | $Obsolete{"$unit.U"} .= $_; # Message(s) to print if unit is used | |
365 | } | |
366 | ||
367 | # Process the ?S: lines | |
368 | sub p_shell { | |
369 | local($_) = @_; | |
370 | unless ($s_symbol) { | |
371 | if (/^(\w+).*:/) { | |
372 | $s_symbol = $1; | |
373 | print " ?S: $s_symbol\n" if $opt_d; | |
374 | } else { | |
375 | warn "\"$file\", line $.: syntax error in ?S: construct.\n"; | |
376 | $s_symbol = $unit; | |
377 | return; | |
378 | } | |
379 | # Deal with obsolete symbol list (enclosed between parenthesis) | |
380 | &record_obsolete("\$$_") if /\(/; | |
381 | } | |
382 | m|^\.\s*$| && ($s_symbol = ''); # End of comment | |
383 | } | |
384 | ||
385 | # Process the ?C: lines | |
386 | sub p_c { | |
387 | local($_) = @_; | |
388 | unless ($c_symbol) { | |
389 | if (s/^(\w+)\s*~\s*(\S+)\s*(.*):/$1 $3:/) { | |
390 | # The ~ operator aliases the main C symbol to another symbol which | |
391 | # is to be used instead for definition in config.h. That is to say, | |
392 | # the line '?C:SYM ~ other:' would look for symbol 'other' instead, | |
393 | # and the documentation for symbol SYM would only be included in | |
394 | # config.h if 'other' were actually wanted. | |
395 | $c_symbol = $2; # Alias for definition in config.h | |
396 | print " ?C: $1 ~ $c_symbol\n" if $opt_d; | |
397 | } elsif (/^(\w+).*:/) { | |
398 | # Default behaviour. Include in config.h if symbol is needed. | |
399 | $c_symbol = $1; | |
400 | print " ?C: $c_symbol\n" if $opt_d; | |
401 | } else { | |
402 | warn "\"$file\", line $.: syntax error in ?C: construct.\n"; | |
403 | $c_symbol = $unit; | |
404 | return; | |
405 | } | |
406 | # Deal with obsolete symbol list (enclosed between parenthesis) and | |
407 | # make sure that list do not appear in config.h.SH by removing it. | |
408 | &record_obsolete("$_") if /\(/; | |
409 | s/\s*\(.*\)//; # Get rid of obsolete symbol list | |
410 | } | |
411 | s|^(\w+)\s*|?$c_symbol:/* $1| || # Start of comment | |
412 | (s|^\.\s*$|?$c_symbol: */\n| && ($c_symbol = '', 1)) || # End of comment | |
413 | s|^(.*)|?$c_symbol: *$1|; # Middle of comment | |
414 | &p_config("$_"); # Add comments to config.h.SH | |
415 | } | |
416 | ||
417 | # Process the ?H: lines | |
418 | sub p_config { | |
419 | local($_) = @_; | |
420 | local($constraint); # Constraint to be used for inclusion | |
421 | ++$old_version if s/^\?%1://; # Old version | |
422 | if (s/^\?(\w+)://) { # Remove leading '?var:' | |
423 | $constraint = $1; # Constraint is leading '?var' | |
424 | } else { | |
425 | $constraint = ''; # No constraint | |
426 | } | |
427 | if (/^#.*\$/) { # Look only for cpp lines | |
428 | if (m|^#\$(\w+)\s+(\w+).*\$(\w+)|) { | |
429 | # Case: #$d_var VAR "$var" | |
430 | $constraint = $2 unless $constraint; | |
431 | print " ?H: ($constraint) #\$$1 $2 \"\$$3\"\n" if $opt_d; | |
432 | $cmaster{$2} = undef; | |
433 | $cwanted{$2} = "$1\n$3"; | |
434 | } elsif (m|^#define\s+(\w+)\((.*)\)\s+\$(\w+)|) { | |
435 | # Case: #define VAR(x) $var | |
436 | $constraint = $1 unless $constraint; | |
437 | print " ?H: ($constraint) #define $1($2) \$$3\n" if $opt_d; | |
438 | $cmaster{$1} = undef; | |
439 | $cwanted{$1} = $3; | |
440 | } elsif (m|^#\$define\s+(\w+)|) { | |
441 | # Case: #$define VAR | |
442 | $constraint = $1 unless $constraint; | |
443 | print " ?H: ($constraint) #define $1\n" if $opt_d; | |
444 | $cmaster{$1} = undef; | |
445 | $cwanted{$1} = "define\n$unit"; | |
446 | } elsif (m|^#\$(\w+)\s+(\w+)|) { | |
447 | # Case: #$d_var VAR | |
448 | $constraint = $2 unless $constraint; | |
449 | print " ?H: ($constraint) #\$$1 $2\n" if $opt_d; | |
450 | $cmaster{$2} = undef; | |
451 | $cwanted{$2} = $1; | |
452 | } elsif (m|^#define\s+(\w+).*\$(\w+)|) { | |
453 | # Case: #define VAR "$var" | |
454 | $constraint = $1 unless $constraint; | |
455 | print " ?H: ($constraint) #define $1 \"\$$2\"\n" if $opt_d; | |
456 | $cmaster{$1} = undef; | |
457 | $cwanted{$1} = $2; | |
458 | } else { | |
459 | $constraint = $unit unless $constraint; | |
460 | print " ?H: ($constraint) $_" if $opt_d; | |
461 | } | |
462 | } else { | |
463 | print " ?H: ($constraint) $_" if $opt_d; | |
464 | } | |
465 | # If not a single ?H:. line, add the leading constraint | |
466 | s/^\.// || s/^/?$constraint:/; | |
467 | print CONF_H; | |
468 | } | |
469 | ||
470 | # Process the ?M: lines | |
471 | sub p_magic { | |
472 | local($_) = @_; | |
473 | unless ($m_symbol) { | |
474 | if (/^(\w+):\s*([\w\s]*)\n$/) { | |
475 | # A '?M:sym:' line implies a '?W:%<:sym' since we'll need to know | |
476 | # about the wantedness of sym later on when building confmagic.h. | |
477 | # Buf is sym is wanted, then the C symbol dependencies have to | |
478 | # be triggered. That is done by introducing sym in the mwanted | |
479 | # array, known by the Wanted file construction process... | |
480 | $m_symbol = $1; | |
481 | print " ?M: $m_symbol\n" if $opt_d; | |
482 | $mwanted{$m_symbol} = $2; # Record C dependencies | |
483 | &p_wanted("$unit:$m_symbol"); # Build fake ?W: line | |
484 | } else { | |
485 | warn "\"$file\", line $.: syntax error in ?M: construct.\n"; | |
486 | } | |
487 | return; | |
488 | } | |
489 | (s/^\.\s*$/?$m_symbol:\n/ && ($m_symbol = '', 1)) || # End of block | |
490 | s/^/?$m_symbol:/; | |
491 | print MAGIC_H; # Definition goes to confmagic.h | |
492 | print " ?M: $_" if $opt_d; | |
493 | } | |
494 | ||
495 | sub p_ignore {} # Ignore comment line | |
496 | sub p_lint {} # Ignore lint directives | |
497 | sub p_visible {} # No visible checking in metaconfig | |
498 | sub p_temp {} # No temporary variable control | |
499 | sub p_file {} # Ignore produced file directives (for now) | |
500 | ||
501 | # Process the ?W: lines | |
502 | sub p_wanted { | |
503 | # Syntax is ?W:<shell symbols>:<C symbols> | |
504 | local($active) = $_[0] =~ /^([^:]*):/; # Symbols to activate | |
505 | local($look_symbols) = $_[0] =~ /:(.*)/; # When those are used | |
506 | local(@syms) = split(/ /, $look_symbols); # Keep original spacing info | |
507 | $active =~ s/\s+/\n/g; # One symbol per line | |
508 | ||
509 | # Concatenate quoted strings, so saying something like 'two words' will | |
510 | # be introduced as one single symbol "two words". | |
511 | local(@symbols); # Concatenated symbols to look for | |
512 | local($concat) = ''; # Concatenation buffer | |
513 | foreach (@syms) { | |
514 | if (s/^\'//) { | |
515 | $concat = $_; | |
516 | } elsif (s/\'$//) { | |
517 | push(@symbols, $concat . ' ' . $_); | |
518 | $concat = ''; | |
519 | } else { | |
520 | push(@symbols, $_) unless $concat; | |
521 | $concat .= ' ' . $_ if $concat; | |
522 | } | |
523 | } | |
524 | ||
525 | # Now record symbols in master and wanted tables | |
526 | foreach (@symbols) { | |
527 | $cmaster{$_} = undef; # Asks for look-up in C files | |
528 | $cwanted{$_} = "$active" if $active; # Shell symbols to activate | |
529 | } | |
c3a1ef25 AC |
530 | |
531 | delete @cmaster{keys %excluded_symbol}; | |
532 | delete @cwanted{keys %excluded_symbol}; | |
459d3fb5 MBT |
533 | } |
534 | ||
535 | # Process the ?INIT: lines | |
536 | sub p_init { | |
537 | local($_) = @_; | |
538 | print INIT "?$unit:", $_; # Wanted only if unit is loaded | |
539 | } | |
540 | ||
541 | # Process the ?D: lines | |
542 | sub p_default { | |
543 | local($_) = @_; | |
544 | s/^([A-Za-z_]+)=(.*)/\@if !$1\n%$1:$1=$2\n\@define $1\n\@end/ | |
545 | && ($hasdefault{$1}++, print INIT $_); | |
546 | } | |
547 | ||
548 | # Process the ?P: lines | |
549 | sub p_public { | |
550 | local($_) = @_; | |
551 | local($csym); # C symbol(s) we're trying to look at | |
552 | local($nosym); # List of symbol(s) which mustn't be wanted | |
553 | local($cfile); # Name of file implementing csym (no .ext) | |
554 | ($csym, $nosym, $cfile) = /([^()]+)\s*(\(.*\))\s*:\s*(\S+)/; | |
555 | unless ($csym eq '' || $cfile eq '') { | |
556 | # Add dependencies for each C symbol, of the form: | |
557 | # -pick public <sym> <file> <notdef symbols list> | |
558 | # and the file will be added to config.c whenever sym is wanted and | |
559 | # none of the notdef symbols is wanted. | |
560 | foreach $sym (split(' ', $csym)) { | |
561 | $dependencies .= "\t-pick public $sym $cfile $nosym\n"; | |
562 | } | |
563 | } | |
564 | } | |
565 | ||
566 | # Process the ?Y: lines | |
567 | # Valid layouts are for now are: top, bottom, default. | |
568 | # | |
569 | # NOTA BENE: | |
570 | # This routine relies on the $defined variable, a global variable set | |
571 | # during the ?MAKE: processing, which lists all the defined symbols in | |
572 | # the unit (the optional leading '+' for internal symbols has been removed | |
573 | # if present). | |
574 | # | |
575 | # The routine fills up a %Layout table, indexed by symbol, yielding the | |
576 | # layout imposed to this unit. That table will then be used later on when | |
577 | # we sort wanted symbols for the Makefile. | |
578 | sub p_layout { | |
579 | local($_) = @_; | |
580 | local($layout) = /^\s*(\w+)/; | |
581 | $layout =~ tr/A-Z/a-z/; # Case is not significant for layouts | |
582 | unless (defined $Lcmp{$layout}) { | |
583 | warn "\"$file\", line $.: unknown layout directive '$layout'.\n"; | |
584 | return; | |
585 | } | |
586 | foreach $sym (split(' ', $defined)) { | |
587 | $Layout{$sym} = $Lcmp{$layout}; | |
588 | } | |
589 | } | |
590 | ||
591 | # Process the ?L: lines | |
592 | # There should not be any '-l' in front of the library name | |
593 | sub p_library { | |
594 | &write_out("L:$_"); | |
595 | } | |
596 | ||
597 | # Process the ?I: lines | |
598 | sub p_include { | |
599 | &write_out("I:$_"); | |
600 | } | |
601 | ||
602 | # Write out line in file Extern.U. The information recorded there has the | |
603 | # following prototypical format: | |
604 | # ?symbol:L:inet bsd | |
605 | # If 'symbol' is wanted, then 'inet bsd' will be added to $libswanted. | |
606 | sub write_out { | |
607 | local($_) = @_; | |
608 | local($target) = $defined; # By default, applies to defined symbols | |
609 | $target = $1 if s/^(.*)://; # List is qualified "?L:target:symbols" | |
610 | local(@target) = split(' ', $target); | |
611 | chop; | |
612 | foreach $key (@target) { | |
613 | print EXTERN "?$key:$_\n"; # EXTERN file defined in xref.pl | |
614 | } | |
615 | } | |
616 | ||
617 | # The %Depend array records the functions we use to process the configuration | |
618 | # lines in the unit, with a special meaning. It is important that all the | |
619 | # known control symbols be listed below, so that metalint does not complain. | |
620 | # The %Lcmp array contains valid layouts and their comparaison value. | |
621 | sub init_depend { | |
622 | %Depend = ( | |
623 | 'MAKE', 'p_make', # The ?MAKE: line records dependencies | |
624 | 'INIT', 'p_init', # Initializations printed verbatim | |
625 | 'LINT', 'p_lint', # Hints for metalint | |
626 | 'RCS', 'p_ignore', # RCS comments are ignored | |
627 | 'C', 'p_c', # C symbols | |
628 | 'D', 'p_default', # Default value for conditional symbols | |
629 | 'E', 'p_example', # Example of usage | |
630 | 'F', 'p_file', # Produced files | |
631 | 'H', 'p_config', # Process the config.h lines | |
632 | 'I', 'p_include', # Added includes | |
633 | 'L', 'p_library', # Added libraries | |
634 | 'M', 'p_magic', # Process the confmagic.h lines | |
635 | 'O', 'p_obsolete', # Unit obsolescence | |
636 | 'P', 'p_public', # Location of PD implementation file | |
637 | 'S', 'p_shell', # Shell variables | |
638 | 'T', 'p_temp', # Shell temporaries used | |
639 | 'V', 'p_visible', # Visible symbols like 'rp', 'dflt' | |
640 | 'W', 'p_wanted', # Wanted value for interpreter | |
641 | 'X', 'p_ignore', # User comment is ignored | |
642 | 'Y', 'p_layout', # User-defined layout preference | |
643 | ); | |
644 | %Lcmp = ( | |
645 | 'top', -1, | |
646 | 'default', 0, | |
647 | 'bottom', 1, | |
648 | ); | |
649 | } | |
650 | ||
651 | # Extract dependencies from units held in @ARGV | |
652 | sub extract_dependencies { | |
653 | local($proc); # Procedure used to handle a ctrl line | |
654 | local($file); # Current file scanned | |
655 | local($dir, $unit); # Directory and unit's name | |
656 | local($old_version) = 0; # True when old-version unit detected | |
657 | local($mc) = "$MC/U"; # Public metaconfig directory | |
658 | local($line); # Last processed line for metalint | |
659 | ||
660 | printf "Extracting dependency lists from %d units...\n", $#ARGV+1 | |
661 | unless $opt_s; | |
662 | ||
663 | chdir $WD; # Back to working directory | |
664 | &init_extraction; # Initialize extraction files | |
665 | $dependencies = ' ' x (50 * @ARGV); # Pre-extend | |
666 | $dependencies = ''; | |
667 | ||
668 | # We do not want to use the <> construct here, because we need the | |
669 | # name of the opened files (to get the unit's name) and we want to | |
670 | # reset the line number for each files, and do some pre-processing. | |
671 | ||
672 | file: while ($file = shift(@ARGV)) { | |
673 | close FILE; # Reset line number | |
674 | $old_version = 0; # True if unit is an old version | |
675 | if (open(FILE, $file)) { | |
676 | ($dir, $unit) = ('', $file) | |
677 | unless ($dir, $unit) = ($file =~ m|(.*)/(.*)|); | |
678 | $unit =~ s|\.U$||; # Remove extension | |
679 | } else { | |
680 | warn("Can't open $file.\n"); | |
681 | } | |
682 | # If unit is in the standard public directory, keep only the unit name | |
683 | $file = "$unit.U" if $dir eq $mc; | |
684 | print "$dir/$unit.U:\n" if $opt_d; | |
685 | line: while (<FILE>) { | |
686 | $line = $_; # Save last processed unit line | |
687 | if (s/^\?([\w\-]+)://) { # We may have found a control line | |
688 | $proc = $Depend{$1}; # Look for a procedure to handle it | |
689 | unless ($proc) { # Unknown control line | |
690 | $proc = $1; # p_unknown expects symbol in '$proc' | |
691 | eval '&p_unknown'; # Signal error (metalint only) | |
692 | next line; # And go on next line | |
693 | } | |
694 | # Long lines may be escaped with a final backslash | |
695 | $_ .= &complete_line(FILE) if s/\\\s*$//; | |
696 | # Run macros substitutions | |
697 | s/%</$unit/g; # %< expands into the unit's name | |
698 | if (s/%\*/$unit/) { | |
699 | # %* expanded into the entire set of defined symbols | |
700 | # in the old version. Now it is only the unit's name. | |
701 | ++$old_version; | |
702 | } | |
703 | eval { &$proc($_) }; # Process the line | |
704 | } else { | |
705 | next file unless $body; # No procedure to handle body | |
706 | do { | |
707 | $line = $_; # Save last processed unit line | |
708 | eval { &$body($_) } ; # From now on, it's the unit body | |
709 | } while (defined ($_ = <FILE>)); | |
710 | next file; | |
711 | } | |
712 | } | |
713 | } continue { | |
714 | warn(" Warning: $file is a pre-3.0 version.\n") if $old_version; | |
715 | &$ending($line) if $ending; # Post-processing for metalint | |
716 | } | |
717 | ||
718 | &end_extraction; # End the extraction process | |
719 | } | |
720 | ||
721 | # The first line was escaped with a final \ character. Every following line | |
722 | # is to be appended to it (until we found a real \n not escaped). Note that | |
723 | # the leading spaces of the continuation line are removed, so any space should | |
724 | # be added before the former \ if needed. | |
725 | sub complete_line { | |
726 | local($file) = @_; # File where lines come from | |
727 | local($_); | |
728 | local($read) = ''; # Concatenation of all the continuation lines found | |
729 | while (<$file>) { | |
730 | s/^\s+//; # Remove leading spaces | |
731 | if (s/\\\s*$//) { # Still followed by a continuation line | |
732 | $read .= $_; | |
733 | } else { # We've reached the end of the continuation | |
734 | return $read . $_; | |
735 | } | |
736 | } | |
737 | } | |
738 | ||
739 | # Extract filenames from manifest | |
740 | sub extract_filenames { | |
741 | &build_filext; # Construct &is_cfile and &is_shfile | |
742 | print "Extracting filenames (C and SH files) from $NEWMANI...\n" | |
743 | unless $opt_s; | |
744 | open(NEWMANI,$NEWMANI) || die "Can't open $NEWMANI.\n"; | |
745 | local($file); | |
746 | while (<NEWMANI>) { | |
747 | ($file) = split(' '); | |
748 | next if $file eq 'config_h.SH'; # skip config_h.SH | |
749 | next if $file eq 'Configure'; # also skip Configure | |
750 | next if $file eq 'confmagic.h' && $opt_M; | |
751 | push(@SHlist, $file) if &is_shfile($file); | |
752 | push(@clist, $file) if &is_cfile($file); | |
753 | } | |
754 | } | |
755 | ||
756 | # Construct two file identifiers based on the file suffix: one for C files, | |
757 | # and one for SH files (using the $cext and $shext variables) defined in | |
758 | # the .package file. | |
759 | # The &is_cfile and &is_shfile routine may then be called to known whether | |
760 | # a given file is a candidate for holding C or SH symbols. | |
761 | sub build_filext { | |
762 | &build_extfun('is_cfile', $cext, '.c .h .y .l'); | |
763 | &build_extfun('is_shfile', $shext, '.SH'); | |
764 | } | |
765 | ||
766 | # Build routine $name to identify extensions listed in $exts, ensuring | |
767 | # that $minimum is at least matched (both to be backward compatible with | |
768 | # older .package and because it is really the minimum requirred). | |
769 | sub build_extfun { | |
770 | local($name, $exts, $minimum) = @_; | |
771 | local(@single); # Single letter dot extensions (may be grouped) | |
772 | local(@others); # Other extensions | |
773 | local(%seen); # Avoid duplicate extensions | |
774 | foreach $ext (split(' ', "$exts $minimum")) { | |
775 | next if $seen{$ext}++; | |
776 | if ($ext =~ s/^\.(\w)$/$1/) { | |
777 | push(@single, $ext); | |
778 | } else { | |
779 | # Convert into perl's regexp | |
780 | $ext =~ s/\./\\./g; # Escape . | |
781 | $ext =~ s/\?/./g; # ? turns into . | |
782 | $ext =~ s/\*/.*/g; # * turns into .* | |
783 | push(@others, $ext); | |
784 | } | |
785 | } | |
786 | local($fn) = &q(<<EOF); # Function being built | |
787 | :sub $name { | |
788 | : local(\$_) = \@_; | |
789 | EOF | |
790 | local($single); # Single regexp: .c .h grouped into .[ch] | |
791 | $single = '\.[' . join('', @single) . ']' if @single; | |
792 | $fn .= &q(<<EOL) if @single; | |
793 | : return 1 if /$single\$/; | |
794 | EOL | |
795 | foreach $ext (@others) { | |
796 | $fn .= &q(<<EOL); | |
797 | : return 1 if /$ext\$/; | |
798 | EOL | |
799 | } | |
800 | $fn .= &q(<<EOF); | |
801 | : 0; # None of the extensions may be applied to file name | |
802 | :} | |
803 | EOF | |
804 | print $fn if $opt_d; | |
805 | eval $fn; | |
806 | chop($@) && die "Can't compile '$name':\n$fn\n$@.\n"; | |
807 | } | |
808 | ||
809 | # Remove ':' quotations in front of the lines | |
810 | sub q { | |
811 | local($_) = @_; | |
812 | s/^://gm; | |
813 | $_; | |
814 | } | |
815 | ||
c3a1ef25 AC |
816 | sub read_exclusions { |
817 | my ($filename) = @_; | |
2a192f88 AC |
818 | if (!defined $filename) { |
819 | $filename = $exclusions_file; # default to name from .package | |
820 | return if !defined $filename || $filename eq ''; | |
821 | } | |
c3a1ef25 AC |
822 | print "Reading exclusions from $filename...\n" unless $opt_s; |
823 | open(EXCLUSIONS, "< $filename\0") || die "Can't read $filename: $!\n"; | |
824 | local $_; | |
825 | while (<EXCLUSIONS>) { | |
826 | if (/^\s*#|^\s*$/) { | |
827 | # comment or blank line, ignore | |
828 | } | |
829 | elsif (/^\s*(\w+)\s*$/) { | |
830 | $excluded_symbol{$1} = 1; | |
831 | } | |
832 | else { | |
833 | die "$filename:$.: unrecognised line\n"; | |
834 | } | |
835 | } | |
836 | close(EXCLUSIONS) || die "Can't close $filename: $!\n"; | |
837 | } | |
838 | ||
459d3fb5 MBT |
839 | # Build a wanted file from the files held in @SHlist and @clist arrays |
840 | sub build_wanted { | |
841 | # If wanted file is already there, parse it to map obsolete if -o option | |
842 | # was used. Otherwise, build a new one. | |
843 | if (-f 'Wanted') { | |
844 | &map_obsolete if $opt_o; # Build Obsol*.U files | |
845 | &dump_obsolete; # Dump obsolete symbols if any | |
846 | return; | |
847 | } | |
848 | &parse_files; | |
849 | } | |
850 | ||
851 | sub parse_files { | |
852 | print "Building a Wanted file...\n" unless $opt_s; | |
853 | open(WANTED,"| sort | uniq >Wanted") || die "Can't create Wanted.\n"; | |
854 | unless (-f $NEWMANI) { | |
855 | &manifake; | |
856 | die "No $NEWMANI--can't build a Wanted file.\n" unless -f $NEWMANI; | |
857 | } | |
858 | ||
859 | local($search); # Where to-be-evaled script is held | |
860 | local($_) = ' ' x 50000 if $opt_m; # Pre-extend pattern search space | |
861 | local(%visited); # Records visited files | |
862 | local(%lastfound); # Where last occurence of key was | |
863 | ||
864 | # Now we are a little clever, and build a loop to eval so that we don't | |
865 | # have to recompile our patterns on every file. We also use "study" since | |
866 | # we are searching the same string for many different things. Hauls! | |
867 | ||
868 | if (@clist) { | |
869 | local($others) = $cext ? " $cext" : ''; | |
870 | print " Scanning .[chyl]$others files for symbols...\n" | |
871 | unless $opt_s; | |
872 | $search = ' ' x (40 * (@cmaster + @ocmaster)); # Pre-extend | |
873 | $search = "while (<>) {study;\n"; # Init loop over ARGV | |
874 | foreach $key (keys(%cmaster)) { | |
875 | $search .= "&cmaster('$key') if /\\b$key\\b/;\n"; | |
876 | } | |
877 | foreach $key (grep(!/^\$/, keys %Obsolete)) { | |
878 | $search .= "&ofound('$key') if /\\b$key\\b/;\n"; | |
879 | } | |
880 | $search .= "}\n"; # terminate loop | |
881 | print $search if $opt_d; | |
882 | @ARGV = @clist; | |
883 | # Swallow each file as a whole, if memory is available | |
884 | undef $/ if $opt_m; | |
885 | eval $search; | |
886 | eval ''; | |
887 | $/ = "\n"; | |
888 | while (($key,$value) = each(%cmaster)) { | |
889 | print WANTED $cwanted{$key}, "\n", ">$key", "\n" if $value; | |
890 | } | |
891 | } | |
892 | ||
893 | # If they don't use magic but use magically guarded symbols without | |
894 | # their corresponding C symbol dependency, warn them, since they might | |
895 | # not know about that portability issue. | |
896 | ||
897 | if (@clist && !$opt_M) { | |
898 | local($nused); # list of non-used symbols | |
899 | local($warning) = 0; # true when one warning issued | |
900 | foreach $cmag (keys %mwanted) { # loop over all used magic symbols | |
901 | next unless $cmaster{$cmag}; | |
902 | $nused = ''; | |
903 | foreach $cdep (split(' ', $mwanted{$cmag})) { | |
904 | $nused .= " $cdep" unless $cmaster{$cdep}; | |
905 | } | |
906 | $nused =~ s/^ //; | |
907 | $nused = "one of " . $nused if $nused =~ s/ /, /g; | |
908 | if ($nused ne '') { | |
909 | print " Warning: $cmag is used without $nused.\n"; | |
910 | $warning++; | |
911 | } | |
912 | } | |
913 | if ($warning) { | |
914 | local($those) = $warning == 1 ? 'that' : 'those'; | |
915 | local($s) = $warning == 1 ? '' : 's'; | |
916 | print "Note: $those previous warning$s may be suppressed by -M.\n"; | |
917 | } | |
918 | } | |
919 | ||
920 | # Cannot remove $cmaster as it is used later on when building Configure | |
921 | undef @clist; | |
922 | undef %cwanted; | |
923 | undef %mwanted; | |
924 | %visited = (); | |
925 | %lastfound = (); | |
926 | ||
927 | if (@SHlist) { | |
928 | local($others) = $shext ? " $shext" : ''; | |
929 | print " Scanning .SH$others files for symbols...\n" unless $opt_s; | |
930 | $search = ' ' x (40 * (@shmaster + @oshmaster)); # Pre-extend | |
931 | $search = "while (<>) {study;\n"; | |
932 | # All the keys already have a leading '$' | |
933 | foreach $key (keys(%shmaster)) { | |
934 | $search .= "&shmaster('$key') if /\\$key\\b/;\n"; | |
935 | } | |
936 | foreach $key (grep (/^\$/, keys %Obsolete)) { | |
937 | $search .= "&ofound('$key') if /\\$key\\b/;\n"; | |
938 | } | |
939 | $search .= "}\n"; | |
940 | print $search if $opt_d; | |
941 | @ARGV = @SHlist; | |
942 | # Swallow each file as a whole, if memory is available | |
943 | undef $/ if $opt_m; | |
944 | eval $search; | |
945 | eval ''; | |
946 | $/ = "\n"; | |
947 | while (($key,$value) = each(%shmaster)) { | |
948 | if ($value) { | |
949 | $key =~ s/^\$//; | |
950 | print WANTED $key, "\n"; | |
951 | } | |
952 | } | |
953 | } | |
954 | ||
955 | # Obsolete symbols, if any, are written in the Wanted file preceded by a | |
956 | # '!' character. In case -w is used, we'll thus be able to correctly build | |
957 | # the Obsol_h.U and Obsol_sh.U files. | |
958 | ||
959 | &add_obsolete; # Add obsolete symbols in Wanted file | |
960 | ||
961 | close WANTED; | |
962 | ||
963 | # If obsolete symbols where found, write an Obsolete file which lists where | |
964 | # each of them appear and the new symbol to be used. Also write Obsol_h.U | |
965 | # and Obsol_sh.U in .MT for later perusal. | |
966 | ||
967 | &dump_obsolete; # Dump obsolete symbols if any | |
968 | ||
969 | die "No desirable symbols found--aborting.\n" unless -s 'Wanted'; | |
970 | ||
971 | # Clean-up memory by freeing useless data structures | |
972 | undef @SHlist; | |
973 | undef %shmaster; | |
974 | } | |
975 | ||
976 | # This routine records matches of C master keys | |
977 | sub cmaster { | |
978 | local($key) = @_; | |
979 | $cmaster{$key}++; # This symbol is wanted | |
980 | return unless $opt_t || $opt_M; # Return if neither -t nor -M | |
981 | if ($opt_t && | |
982 | $lastfound{$key} ne $ARGV # Never mentionned for this file ? | |
983 | ) { | |
984 | $visited{$ARGV}++ || print $ARGV,":\n"; | |
985 | print "\t$key\n"; | |
986 | $lastfound{$key} = $ARGV; | |
987 | } | |
988 | if ($opt_M && | |
989 | defined($mwanted{$key}) # Found a ?M: symbol | |
990 | ) { | |
991 | foreach $csym (split(' ', $mwanted{$key})) { | |
992 | $cmaster{$csym}++; # Activate C symbol dependencies | |
993 | } | |
994 | } | |
995 | } | |
996 | ||
997 | # This routine records matches of obsolete keys (C or shell) | |
998 | sub ofound { | |
999 | local($key) = @_; | |
1000 | local($_) = $Obsolete{$key}; # Value of new symbol | |
1001 | $ofound{"$ARGV $key $_"}++; # Record obsolete match | |
1002 | $cmaster{$_}++ unless /^\$/; # A C hit | |
1003 | $shmaster{$_}++ if /^\$/; # Or a shell one | |
1004 | return unless $opt_t; # Continue if trace option on | |
1005 | if ($lastfound{$key} ne $ARGV) { # Never mentionned for this file ? | |
1006 | $visited{$ARGV}++ || print $ARGV,":\n"; | |
1007 | print "\t$key (obsolete, use $_)\n"; | |
1008 | $lastfound{$key} = $ARGV; | |
1009 | } | |
1010 | } | |
1011 | ||
1012 | # This routine records matches of shell master keys | |
1013 | sub shmaster { | |
1014 | local($key) = @_; | |
1015 | $shmaster{$key}++; # This symbol is wanted | |
1016 | return unless $opt_t; # Continue if trace option on | |
1017 | if ($lastfound{$key} ne $ARGV) { # Never mentionned for this file ? | |
1018 | $visited{$ARGV}++ || print $ARGV,":\n"; | |
1019 | print "\t$key\n"; | |
1020 | $lastfound{$key} = $ARGV; | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | # Write obsolete symbols into the Wanted file for later perusal by -w. | |
1025 | sub add_obsolete { | |
1026 | local($file); # File where obsolete symbol was found | |
1027 | local($old); # Name of this old symbol | |
1028 | local($new); # Value of the new symbol to be used | |
1029 | foreach $key (sort keys %ofound) { | |
1030 | ($file, $old, $new) = ($key =~ /^(\S+)\s+(\S+)\s+(\S+)/); | |
1031 | if ($new =~ s/^\$//) { # We found an obsolete shell symbol | |
1032 | print WANTED "!$old\n"; | |
1033 | } else { # We found an obsolete C symbol | |
1034 | print WANTED "!>$old\n"; | |
1035 | } | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | # Map obsolete symbols from Wanted file into %Obsolete and call dump_obsolete | |
1040 | # to actually build the Obsol_sh.U and Obsol_h.U files. Those will be needed | |
1041 | # during the Configure building phase to actually do the remaping. | |
1042 | # The obsolete symbols found are entered in the %ofound array, tagged as from | |
1043 | # file 'XXX', which is specially recognized by dump_obsolete. | |
1044 | sub map_obsolete { | |
1045 | open(WANTED, 'Wanted') || die "Can't open Wanted file.\n"; | |
1046 | local($new); # New symbol to be used instead of obsolete one | |
1047 | while (<WANTED>) { | |
1048 | chop; | |
1049 | next unless s/^!//; # Skip non-obsolete symbols | |
1050 | if (s/^>//) { # C symbol | |
1051 | $new = $Obsolete{$_}; # Fetch new symbol | |
1052 | $ofound{"XXX $_ $new"}++; # Record obsolete match (XXX = no file) | |
1053 | } else { # Shell symbol | |
1054 | $new = $Obsolete{"\$$_"}; # Fetch new symbol | |
1055 | $ofound{"XXX \$$_ $new"}++; # Record obsolete match (XXX = no file) | |
1056 | } | |
1057 | } | |
1058 | close WANTED; | |
1059 | } | |
1060 | ||
1061 | # Record obsolete symbols association (new versus old), that is to say for a | |
1062 | # given old symbol, $Obsolete{'old'} = new symbol to be used. A '$' is prepended | |
1063 | # for all shell variables | |
1064 | sub record_obsolete { | |
1065 | local($_) = @_; | |
1066 | local(@obsoleted); # List of obsolete symbols | |
1067 | local($symbol); # New symbol which must be used | |
1068 | local($dollar) = s/^\$// ? '$':''; # The '$' or a null string | |
1069 | # Syntax for obsolete symbols specification is | |
1070 | # list of symbols (obsolete ones): | |
1071 | if (/^(\w+)\s*\((.*)\)\s*:$/) { | |
1072 | $symbol = "$dollar$1"; | |
1073 | @obsoleted = split(' ', $2); # List of obsolete symbols | |
1074 | } else { | |
1075 | if (/^(\w+)\s*\((.*):$/) { | |
1076 | warn "\"$file\", line $.: final ')' before ':' missing.\n"; | |
1077 | $symbol = "$dollar$1"; | |
1078 | @obsoleted = split(' ', $2); | |
1079 | } else { | |
1080 | warn "\"$file\", line $.: syntax error.\n"; | |
1081 | return; | |
1082 | } | |
1083 | } | |
1084 | foreach $val (@obsoleted) { | |
1085 | $_ = $dollar . $val; | |
1086 | if (defined $Obsolete{$_}) { | |
1087 | warn "\"$file\", line $.: '$_' already obsoleted by '$Obsolete{$_}'.\n"; | |
1088 | } else { | |
1089 | $Obsolete{$_} = $symbol; # Record (old, new) tuple | |
1090 | } | |
1091 | } | |
1092 | } | |
1093 | ||
1094 | # Dump obsolete symbols used in file 'Obsolete'. Also write Obsol_h.U and | |
1095 | # Obsol_sh.U to record old versus new mappings if the -o option was used. | |
1096 | sub dump_obsolete { | |
1097 | unless (-f 'Obsolete') { | |
1098 | open(OBSOLETE, ">Obsolete") || die "Can't create Obsolete.\n"; | |
1099 | } | |
1100 | open(OBSOL_H, ">.MT/Obsol_h.U") || die "Can't create .MT/Obsol_h.U.\n"; | |
1101 | open(OBSOL_SH, ">.MT/Obsol_sh.U") || die "Can't create .MT/Obsol_sh.U.\n"; | |
1102 | local($file); # File where obsolete symbol was found | |
1103 | local($old); # Name of this old symbol | |
1104 | local($new); # Value of the new symbol to be used | |
1105 | # Leave a blank line at the top so that anny added ^L will stand on a line | |
1106 | # by itself (the formatting process adds a ^L when a new page is needed). | |
1107 | format OBSOLETE_TOP = | |
1108 | ||
1109 | File | Old symbol | New symbol | |
1110 | -----------------------------------+----------------------+--------------------- | |
1111 | . | |
1112 | format OBSOLETE = | |
1113 | @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< | @<<<<<<<<<<<<<<<<<<< | @<<<<<<<<<<<<<<<<<<< | |
1114 | $file, $old, $new | |
1115 | . | |
1116 | local(%seen); | |
1117 | foreach $key (sort keys %ofound) { | |
1118 | ($file, $old, $new) = ($key =~ /^(\S+)\s+(\S+)\s+(\S+)/); | |
1119 | write(OBSOLETE) unless $file eq 'XXX'; | |
1120 | next unless $opt_o; # Obsolete mapping done only with -o | |
1121 | next if $seen{$old}++; # Already remapped, thank you | |
1122 | if ($new =~ s/^\$//) { # We found an obsolete shell symbol | |
1123 | $old =~ s/^\$//; | |
1124 | print OBSOL_SH "$old=\"\$$new\"\n"; | |
1125 | } else { # We found an obsolete C symbol | |
1126 | print OBSOL_H "#ifdef $new\n"; | |
1127 | print OBSOL_H "#define $old $new\n"; | |
1128 | print OBSOL_H "#endif\n\n"; | |
1129 | } | |
1130 | } | |
1131 | close OBSOLETE; | |
1132 | close OBSOL_H; | |
1133 | close OBSOL_SH; | |
1134 | if (-s 'Obsolete') { | |
1135 | print "*** Obsolete symbols found -- see file 'Obsolete' for a list.\n"; | |
1136 | } else { | |
1137 | unlink 'Obsolete'; | |
1138 | } | |
1139 | undef %ofound; # Not needed any more | |
1140 | } | |
1141 | ||
1142 | # Build the private makefile we use to compute the transitive closure of the | |
1143 | # previously determined dependencies. | |
1144 | sub build_makefile { | |
1145 | print "Computing optimal dependency graph...\n" unless $opt_s; | |
1146 | chdir('.MT') || die "Can't chdir to .MT\n"; | |
1147 | local($wanted); # Wanted shell symbols | |
1148 | &build_private; # Build a first makefile from dependencies | |
1149 | &compute_loadable; # Compute loadable units | |
1150 | &update_makefile; # Update makefile using feedback from first pass | |
1151 | chdir($WD) || die "Can't chdir back to $WD\n"; | |
1152 | # Free memory by removing useless data structures | |
1153 | undef $dependencies; | |
1154 | undef $saved_dependencies; | |
1155 | } | |
1156 | ||
1157 | # First pass: build a private makefile from the extracted dependency, changing | |
1158 | # conditional units to truly wanted ones if the symbol is used, removing the | |
1159 | # dependency otherwise. The original dependencies are saved. | |
1160 | sub build_private { | |
1161 | print " Building private make file...\n" unless $opt_s; | |
1162 | open(WANTED,"../Wanted") || die "Can't reopen Wanted.\n"; | |
1163 | $wanted = ' ' x 2000; # Pre-extend string | |
1164 | $wanted = ''; | |
1165 | while (<WANTED>) { | |
1166 | chop; | |
1167 | next if /^!/; # Skip obsolete symbols | |
1168 | if (s/^>//) { | |
1169 | $cmaster{$_}++; | |
1170 | } else { | |
1171 | $wanted .= "$_ "; | |
1172 | } | |
1173 | } | |
1174 | close WANTED; | |
1175 | ||
1176 | # The wanted symbols are sorted so that d_* (checking for C library symbol) | |
1177 | # come first and i_* (checking for includes) comes at the end. Grouping the | |
1178 | # d_* symbols together has good chances of improving the locality of the | |
1179 | # other questions and i_* symbols must come last since some depend on h_* | |
1180 | # values which prevent incompatible headers inclusions. | |
1181 | $wanted = join(' ', sort symbols split(' ', $wanted)); | |
1182 | ||
1183 | # Now generate the first makefile, which will be used to determine which | |
1184 | # symbols we really need, so that conditional dependencies may be solved. | |
1185 | open(MAKEFILE,">Makefile") || die "Can't create .MT/Makefile.\n"; | |
1186 | print MAKEFILE "SHELL = /bin/sh\n"; | |
1187 | print MAKEFILE "W = $wanted\n"; | |
1188 | $saved_dependencies = $dependencies; | |
1189 | foreach $sym (@Cond) { | |
1190 | if ($symwanted{$sym}) { | |
1191 | $dependencies =~ s/\+($sym\s)/$1/gm; | |
1192 | } else { | |
1193 | $dependencies =~ s/\+$sym(\s)/$1/gm; | |
1194 | } | |
1195 | } | |
1196 | print MAKEFILE $dependencies; | |
1197 | close MAKEFILE; | |
1198 | } | |
1199 | ||
1200 | # Ordering for symbols. Give higher priority to d_* ones and lower to i_* ones. | |
1201 | # If any layout priority is defined in %Layout, it is used to order the | |
1202 | # symbols. | |
1203 | sub symbols { | |
1204 | local($r) = $Layout{$a} <=> $Layout{$b}; | |
1205 | return $r if $r; | |
1206 | # If we come here, both symbols have the same layout priority. | |
1207 | if ($a =~ /^d_/) { | |
1208 | return -1 unless $b =~ /^d_/; | |
1209 | } elsif ($b =~ /^d_/) { | |
1210 | return 1; | |
1211 | } elsif ($a =~ /^i_/) { | |
1212 | return 1 unless $b =~ /^i_/; | |
1213 | } elsif ($b =~ /^i_/) { | |
1214 | return -1; | |
1215 | } | |
1216 | $a cmp $b; | |
1217 | } | |
1218 | ||
1219 | # Run the makefile produced in the first pass to find the whole set of units we | |
1220 | # have to load, filling in the %symwanted and %condwanted structures. | |
1221 | sub compute_loadable { | |
1222 | print " Determining loadable units...\n" unless $opt_s; | |
1223 | open(MAKE, "make -n |") || die "Can't run make"; | |
1224 | while (<MAKE>) { | |
1225 | s|^\s+||; # Some make print tabs before command | |
1226 | if (/^pick/) { | |
1227 | print "\t$_" if $opt_v; | |
1228 | ($pick,$cmd,$symbol,$unit) = split(' '); | |
1229 | $symwanted{$symbol}++; | |
1230 | $symwanted{$unit}++; | |
1231 | } elsif (/^cond/) { | |
1232 | print "\t$_" if $opt_v; | |
1233 | ($pick,@symbol) = split(' '); | |
1234 | for (@symbol) { | |
1235 | $condwanted{$_}++; # Default value is requested | |
1236 | } | |
1237 | } | |
1238 | } | |
1239 | close MAKE; | |
1240 | } | |
1241 | ||
1242 | # Now that we know all the desirable symbols, we have to rebuild | |
1243 | # another makefile, in order to have the units in a more optimal | |
1244 | # way. | |
1245 | # Actually, if we have both ?MAKE:a:+b and ?MAKE:d:b and 'd' is | |
1246 | # wanted; then 'b' will be loaded. However, 'b' is a conditional | |
1247 | # dependency for 'a', and it would be better if 'b' were loaded | |
1248 | # before 'a' is, though this is not necessary. | |
1249 | # It is hard to know that 'b' will be loaded *before* the first make. | |
1250 | ||
1251 | # Back to the original dependencies, make loadable units truly wanted ones and | |
1252 | # remove optional ones. | |
1253 | sub update_makefile { | |
1254 | print " Updating make file...\n" unless $opt_s; | |
1255 | open(MAKEFILE,">Makefile") || die "Can't create .MT/Makefile.\n"; | |
1256 | print MAKEFILE "SHELL = /bin/sh\n"; | |
1257 | print MAKEFILE "W = $wanted\n"; | |
1258 | foreach $sym (@Cond) { | |
1259 | if ($symwanted{$sym}) { | |
1260 | $saved_dependencies =~ s/\+($sym\s)/$1/gm; | |
1261 | } else { | |
1262 | $saved_dependencies =~ s/\+$sym(\s)/$1/gm; | |
1263 | } | |
1264 | } | |
1265 | print MAKEFILE $saved_dependencies; | |
1266 | close MAKEFILE; | |
1267 | } | |
1268 | ||
1269 | # Solve dependencies by saving the 'pick' command in @cmdwanted | |
1270 | sub solve_dependencies { | |
1271 | local(%unitseen); # Record already picked units (avoid duplicates) | |
1272 | print "Determining the correct order for the units...\n" unless $opt_s; | |
1273 | chdir('.MT') || die "Can't chdir to .MT: $!.\n"; | |
1274 | open(MAKE, "make -n |") || die "Can't run make"; | |
1275 | while (<MAKE>) { | |
1276 | s|^\s+||; # Some make print tabs before command | |
1277 | print "\t$_" if $opt_v; | |
1278 | if (/^pick/) { | |
1279 | ($pick,$cmd,$symbol,$unit) = split(' '); | |
1280 | push(@cmdwanted,"$cmd $symbol $unit") | |
1281 | unless $unitseen{"$cmd:$unit"}++; | |
1282 | } elsif (/^cond/) { | |
1283 | # Ignore conditional symbol request | |
1284 | } else { | |
1285 | chop; | |
035bc654 | 1286 | system $_; |
459d3fb5 MBT |
1287 | } |
1288 | } | |
1289 | chdir($WD) || die "Can't chdir to $WD: $!.\n"; | |
1290 | close MAKE; | |
1291 | } | |
1292 | ||
1293 | # Create the Configure script | |
1294 | sub create_configure { | |
1295 | print "Creating Configure...\n" unless $opt_s; | |
1296 | open(CONFIGURE,">Configure") || die "Can't create Configure: $!\n"; | |
1297 | open(CONF_H,">config_h.SH") || die "Can't create config_h.SH: $!\n"; | |
1298 | if ($opt_M) { | |
1299 | open(MAGIC_H,">confmagic.h") || die "Can't create confmagic.h: $!\n"; | |
1300 | } | |
1301 | ||
1302 | chdir('.MT') || die "Can't cd to .MT: $!\n"; | |
1303 | for (@cmdwanted) { | |
1304 | &process_command($_); # Run the makefile command | |
1305 | } | |
1306 | chdir($WD) || die "Can't cd back to $WD\n"; | |
1307 | close CONFIGURE; | |
1308 | print CONF_H "#endif\n"; # Close the opened #ifdef (see Config_h.U) | |
1309 | print CONF_H "!GROK!THIS!\n"; | |
1310 | close CONF_H; | |
1311 | if ($opt_M) { | |
1312 | print MAGIC_H "#endif\n"; # Close the opened #ifdef (see Magic_h.U) | |
1313 | close MAGIC_H; | |
1314 | } | |
1315 | `chmod +x Configure`; | |
1316 | } | |
1317 | ||
1318 | # Process a Makefile 'pick' command | |
1319 | sub process_command { | |
1320 | local($cmd, $target, $unit_name) = split(' ', $_[0]); | |
1321 | local($name) = $unit_name . '.U'; # Restore missing .U | |
1322 | local($file) = $name; # Where unit is located | |
1323 | unless ($file =~ m|^\./|) { # Unit produced earlier by metaconfig | |
1324 | $file = $Unit{$unit_name}; # Fetch unit from U directory | |
1325 | } | |
1326 | if (defined $Obsolete{$name}) { # Signal use of an obsolete unit | |
1327 | warn "\tObsolete unit $name is used:\n"; | |
1328 | local(@msg) = split(/\n/, $Obsolete{$name}); | |
1329 | foreach $msg (@msg) { | |
1330 | warn "\t $msg\n"; | |
1331 | } | |
1332 | } | |
d6208abd | 1333 | die "Can't open UNIT (name: $name, target: $target) $file.\n" unless open(UNIT, $file); |
459d3fb5 MBT |
1334 | print "\t$cmd $file\n" if $opt_v; |
1335 | &init_interp; # Initializes the interpreter | |
1336 | ||
1337 | # The 'add' command adds the unit to Configure. | |
1338 | if ($cmd eq 'add') { | |
1339 | while (<UNIT>) { | |
1340 | print CONFIGURE unless &skipped || !&interpret($_); | |
1341 | } | |
1342 | } | |
1343 | ||
1344 | # The 'weed' command adds the unit to Configure, but | |
1345 | # makes some tests for the lines starting with '?' or '%'. | |
1346 | # These lines are kept only if the symbol is wanted. | |
1347 | elsif ($cmd eq 'weed') { | |
1348 | while (<UNIT>) { | |
1349 | if (/^\?(\w+):/) { | |
1350 | s/^\?\w+:// if $symwanted{$1}; | |
1351 | } | |
1352 | if (/^%(\w+):/) { | |
1353 | s/^%\w+:// if $condwanted{$1}; | |
1354 | } | |
1355 | print CONFIGURE unless &skipped || !&interpret($_); | |
1356 | } | |
1357 | } | |
1358 | ||
1359 | # The 'wipe' command adds the unit to Configure, but | |
1360 | # also substitues some hardwired macros. | |
1361 | elsif ($cmd eq 'wipe') { | |
1362 | while (<UNIT>) { | |
1363 | s/<PACKAGENAME>/$package/g; | |
1364 | s/<MAINTLOC>/$maintloc/g; | |
1365 | s/<VERSION>/$version/g; # This is metaconfig's version | |
1366 | s/<PATCHLEVEL>/$patchlevel/g; # And patchlevel information | |
1367 | s/<DATE>/$date/g; | |
1368 | s/<BASEREV>/$baserev/g; | |
1369 | s/<\$(\w+)>/eval("\$$1")/ge; # <$var> -> $var substitution | |
1370 | print CONFIGURE unless &skipped || !&interpret($_); | |
1371 | } | |
1372 | } | |
1373 | ||
1374 | # The 'add.Null' command adds empty initializations | |
1375 | # to Configure for all the shell variable used. | |
1376 | elsif ($cmd eq 'add.Null') { | |
1377 | for (sort @Master) { | |
1378 | if (/^\?(\w+):/) { | |
1379 | s/^\?\w+:// if $symwanted{$1}; | |
1380 | } | |
1381 | print CONFIGURE unless &skipped; | |
1382 | } | |
1383 | for (sort @Cond) { | |
1384 | print CONFIGURE "$_=''\n" | |
1385 | unless $symwanted{$_} || $hasdefault{$_}; | |
1386 | } | |
1387 | while (<UNIT>) { | |
1388 | print CONFIGURE unless &skipped || !&interpret($_); | |
1389 | } | |
1390 | print CONFIGURE "CONFIG=''\n\n"; | |
1391 | } | |
1392 | ||
1393 | # The 'add.Config_sh' command fills in the production of | |
1394 | # the config.sh script within Configure. Only the used | |
1395 | # variable are added, the conditional ones are skipped. | |
1396 | elsif ($cmd eq 'add.Config_sh') { | |
1397 | while (<UNIT>) { | |
1398 | print CONFIGURE unless &skipped || !&interpret($_); | |
1399 | } | |
1400 | for (sort @Master) { | |
1401 | if (/^\?(\w+):/) { | |
1402 | # Can't use $shmaster, because config.sh must | |
1403 | # also contain some internal defaults used by | |
1404 | # Configure (e.g. nm_opt, libc, etc...). | |
1405 | s/^\?\w+:// if $symwanted{$1}; | |
1406 | } | |
1407 | s/^(\w+)=''/$1='\$$1'/; | |
1408 | print CONFIGURE unless &skipped; | |
1409 | } | |
1410 | } | |
1411 | ||
1412 | # The 'close.Config_sh' command adds the final EOT line at | |
1413 | # the end of the here-document construct which produces the | |
1414 | # config.sh file within Configure. | |
1415 | elsif ($cmd eq 'close.Config_sh') { | |
1416 | print CONFIGURE "EOT\n\n"; # Ends up file | |
1417 | } | |
1418 | ||
1419 | # The 'c_h_weed' command produces the config_h.SH file. | |
1420 | # Only the necessary lines are kept. If no conditional line is | |
1421 | # ever printed, then the file is useless and will be removed. | |
1422 | elsif ($cmd eq 'c_h_weed') { | |
1423 | $printed = 0; | |
1424 | while (<UNIT>) { | |
1425 | if (/^\?(\w+):/) { | |
1426 | s/^\?\w+:// if $cmaster{$1} || $symwanted{$1}; | |
1427 | } | |
1428 | unless (&skipped || !&interpret($_)) { | |
1429 | if (/^$/) { | |
1430 | print CONF_H "\n" if $printed; | |
1431 | $printed = 0; | |
1432 | } else { | |
1433 | print CONF_H; | |
1434 | ++$printed; | |
1435 | } | |
1436 | } | |
1437 | } | |
1438 | } | |
1439 | ||
1440 | # The 'cm_h_weed' command produces the confmagic.h file. | |
1441 | # Only the necessary lines are kept. If no conditional line is | |
1442 | # ever printed, then the file is useless and will be removed. | |
1443 | elsif ($cmd eq 'cm_h_weed') { | |
1444 | if ($opt_M) { | |
1445 | $printed = 0; | |
1446 | while (<UNIT>) { | |
1447 | if (/^\?(\w+):/) { | |
1448 | s/^\?\w+:// if $cmaster{$1} || $symwanted{$1}; | |
1449 | } | |
1450 | unless (&skipped || !&interpret($_)) { | |
1451 | if (/^$/) { | |
1452 | print MAGIC_H "\n" if $printed; | |
1453 | $printed = 0; | |
1454 | } else { | |
1455 | print MAGIC_H; | |
1456 | ++$printed; | |
1457 | } | |
1458 | } | |
1459 | } | |
1460 | } | |
1461 | } | |
1462 | ||
1463 | # The 'prepend' command will add the content of the target to | |
1464 | # the current file (held in $file, the one which UNIT refers to), | |
1465 | # if the file is not empty. | |
1466 | elsif ($cmd eq 'prepend') { | |
1467 | if (-s $file) { | |
1468 | open(PREPEND, ">.prepend") || | |
1469 | die "Can't create .MT/.prepend.\n"; | |
1470 | open(TARGET, $Unit{$target}) || | |
d6208abd | 1471 | die "Can't open Unit $Unit{$target}.\n"; |
459d3fb5 MBT |
1472 | while (<TARGET>) { |
1473 | print PREPEND unless &skipped; | |
1474 | } | |
1475 | print PREPEND <UNIT>; # Now add original file contents | |
1476 | close PREPEND; | |
1477 | close TARGET; | |
1478 | rename('.prepend', $file) || | |
1479 | die "Can't rename .prepend into $file.\n"; | |
1480 | } | |
1481 | } | |
1482 | ||
1483 | # Command not found | |
1484 | else { | |
1485 | die "Unrecognized command from Makefile: $cmd\n"; | |
1486 | } | |
1487 | &check_state; # Make sure there are no pending statements | |
1488 | close UNIT; | |
1489 | } | |
1490 | ||
1491 | # Skip lines starting with ? or %, including all the following continuation | |
1492 | # lines, if any. Return 0 if the line was not to be skipped, 1 otherwise. | |
1493 | sub skipped { | |
1494 | return 0 unless /^\?|^%/; | |
1495 | &complete_line(UNIT) if /\\\s*$/; # Swallow continuation lines | |
1496 | 1; | |
1497 | } | |
1498 | ||
1499 | # Update the MANIFEST.new file if necessary | |
1500 | sub cosmetic_update { | |
1501 | # Check for an "empty" config_h.SH (2 blank lines only). This test relies | |
1502 | # on the actual text held in Config_h.U. If the unit is modified, then the | |
1503 | # following might need adjustments. | |
1504 | local($blank_lines) = 0; | |
1505 | local($spaces) = 0; | |
1506 | open(CONF_H, 'config_h.SH') || die "Can't open config_h.SH\n"; | |
1507 | while(<CONF_H>) { | |
1508 | ++$blank_lines if /^$/; | |
1509 | } | |
1510 | unlink 'config_h.SH' unless $blank_lines > 3; | |
1511 | ||
1512 | open(NEWMANI,$NEWMANI); | |
1513 | $_ = <NEWMANI>; | |
1514 | /(\S+\s+)\S+/ && ($spaces = length($1)); # Spaces wanted | |
1515 | close NEWMANI; | |
1516 | $spaces = 29 if ($spaces < 12); # Default value | |
1517 | open(NEWMANI,$NEWMANI); | |
1518 | $/ = "\001"; # Swallow the whole file | |
1519 | $_ = <NEWMANI>; | |
1520 | $/ = "\n"; | |
1521 | close NEWMANI; | |
1522 | ||
1523 | &mani_add('Configure', 'Portability tool', $spaces) unless /^Configure\b/m; | |
1524 | &mani_add('config_h.SH', 'Produces config.h', $spaces) | |
1525 | unless /^config_h\.SH\b/m || !-f 'config_h.SH'; | |
1526 | &mani_add('confmagic.h', 'Magic symbol remapping', $spaces) | |
1527 | if $opt_M && !/^confmagic\.h\b/m; | |
1528 | ||
1529 | &mani_remove('config_h.SH') if /^config_h\.SH\b/m && !-f 'config_h.SH'; | |
1530 | &mani_remove('confmagic.h') if /^confmagic.h\b/m && !$opt_M; | |
1531 | ||
1532 | if ($opt_G) { # Want a GNU-like configure wrapper | |
1533 | &add_configure; | |
1534 | &mani_add('configure', 'GNU configure-like wrapper', $spaces) | |
1535 | if !/^configure\s/m && -f 'configure'; | |
1536 | } else { | |
1537 | &mani_remove('configure') if /^configure\s/m && !-f 'configure'; | |
1538 | } | |
1539 | } | |
1540 | ||
1541 | # Add file to MANIFEST.new, with properly indented comment | |
1542 | sub mani_add { | |
1543 | local($file, $comment, $spaces) = @_; | |
1544 | print "Adding $file to your $NEWMANI file...\n" unless $opt_s; | |
1545 | open(NEWMANI, ">>$NEWMANI") || warn "Can't add $file to $NEWMANI: $!\n"; | |
1546 | local($blank) = ' ' x ($spaces - length($file)); | |
1547 | print NEWMANI "${file}${blank}${comment}\n"; | |
1548 | close NEWMANI; | |
1549 | } | |
1550 | ||
1551 | # Remove file from MANIFEST.new | |
1552 | sub mani_remove { | |
1553 | local($file) = @_; | |
1554 | print "Removing $file from $NEWMANI...\n" unless $opt_s; | |
1555 | unless (open(NEWMANI, ">$NEWMANI.x")) { | |
1556 | warn "Can't create backup $NEWMANI copy: $!\n"; | |
1557 | return; | |
1558 | } | |
1559 | unless (open(OLDMANI, $NEWMANI)) { | |
1560 | warn "Can't open $NEWMANI: $!\n"; | |
1561 | return; | |
1562 | } | |
1563 | local($_); | |
1564 | while (<OLDMANI>) { | |
1565 | print NEWMANI unless /^$file\b/ | |
1566 | } | |
1567 | close OLDMANI; | |
1568 | close NEWMANI; | |
1569 | rename("$NEWMANI.x", $NEWMANI) || | |
1570 | warn "Couldn't restore $NEWMANI from $NEWMANI.x\n"; | |
1571 | } | |
1572 | ||
1573 | # Copy GNU-like configure wrapper to the package root directory | |
1574 | sub add_configure { | |
1575 | if (-f "$MC/configure") { | |
1576 | print "Copying GNU configure-like front end...\n" unless $opt_s; | |
1577 | system "cp $MC/configure ./configure"; | |
1578 | `chmod +x configure`; | |
1579 | } else { | |
1580 | warn "Can't locate $MC/configure: $!\n"; | |
1581 | } | |
1582 | } | |
1583 | ||
1584 | package interpreter; | |
1585 | ||
1586 | # States used by our interpeter -- in sync with @Keep | |
1587 | sub main'init_keep { | |
1588 | # Status in which we keep lines -- $Keep[$status] | |
1589 | @Keep = (0, 1, 1, 0, 1); | |
1590 | ||
1591 | # Available status ($status) | |
1592 | $SKIP = 0; | |
1593 | $IF = 1; | |
1594 | $ELSE = 2; | |
1595 | $NOT = 3; | |
1596 | $OUT = 4; | |
1597 | } | |
1598 | ||
1599 | # Priorities for operators -- magic numbers :-) | |
1600 | sub main'init_priority { | |
1601 | $Priority{'&&'} = 4; | |
1602 | $Priority{'||'} = 3; | |
1603 | } | |
1604 | ||
1605 | # Initializes the state stack of the interpreter | |
1606 | sub main'init_interp { | |
1607 | @state = (); | |
1608 | push(@state, $OUT); | |
1609 | } | |
1610 | ||
1611 | # Print error messages -- asssumes $unit and $. correctly set. | |
1612 | sub error { | |
1613 | warn "\"$main'file\", line $.: @_.\n"; | |
1614 | } | |
1615 | ||
1616 | # If some states are still in the stack, warn the user | |
1617 | sub main'check_state { | |
1618 | &error("one statement pending") if $#state == 1; | |
1619 | &error("$#state statements pending") if $#state > 1; | |
1620 | } | |
1621 | ||
1622 | # Add a value on the stack, modified by all the monadic operators. | |
1623 | # We use the locals @val and @mono from eval_expr. | |
1624 | sub push_val { | |
1625 | local($val) = shift(@_); | |
1626 | while ($#mono >= 0) { | |
1627 | # Cheat... the only monadic operator is '!'. | |
1628 | pop(@mono); | |
1629 | $val = !$val; | |
1630 | } | |
1631 | push(@val, $val); | |
1632 | } | |
1633 | ||
1634 | # Execute a stacked operation, leave result in stack. | |
1635 | # We use the locals @val and @op from eval_expr. | |
1636 | # If the value stack holds only one operand, do nothing. | |
1637 | sub execute { | |
1638 | return unless $#val > 0; | |
1639 | local($op) = pop(@op); | |
1640 | local($val1) = pop(@val); | |
1641 | local($val2) = pop(@val); | |
1642 | push(@val, eval("$val1 $op $val2") ? 1: 0); | |
1643 | } | |
1644 | ||
1645 | # Given an operator, either we add it in the stack @op, because its | |
1646 | # priority is lower than the one on top of the stack, or we first execute | |
1647 | # the stacked operations until we reach the end of stack or an operand | |
1648 | # whose priority is lower than ours. | |
1649 | # We use the locals @val and @op from eval_expr. | |
1650 | sub update_stack { | |
1651 | local($op) = shift(@_); # Operator | |
1652 | if (!$Priority{$op}) { | |
1653 | &error("illegal operator $op"); | |
1654 | return; | |
1655 | } else { | |
1656 | if ($#val < 0) { | |
1657 | &error("missing first operand for '$op' (diadic operator)"); | |
1658 | return; | |
1659 | } | |
1660 | # Because of the special behaviour of do-SUBR with the while modifier, | |
1661 | # I'm using a while-BLOCK construct. I consider this to be a bug of perl | |
1662 | # 4.0 PL19, although it is clearly documented in the man page. | |
1663 | while ( | |
1664 | $Priority{$op[$#op]} > $Priority{$op} # Higher priority op | |
1665 | && $#val > 0 # At least 2 values | |
1666 | ) { | |
1667 | &execute; # Execute an higher priority stacked operation | |
1668 | } | |
1669 | push(@op, $op); # Everything at higher priority has been executed | |
1670 | } | |
1671 | } | |
1672 | ||
1673 | # This is the heart of our little interpreter. Here, we evaluate | |
1674 | # a logical expression and return its value. | |
1675 | sub eval_expr { | |
1676 | local(*expr) = shift(@_); # Expression to parse | |
1677 | local(@val) = (); # Stack of values | |
1678 | local(@op) = (); # Stack of diadic operators | |
1679 | local(@mono) =(); # Stack of monadic operators | |
1680 | local($tmp); | |
1681 | $_ = $expr; | |
1682 | while (1) { | |
1683 | s/^\s+//; # Remove spaces between words | |
1684 | # The '(' construct | |
1685 | if (s/^\(//) { | |
1686 | &push_val(&eval_expr(*_)); | |
1687 | # A final '\' indicates an end of line | |
1688 | &error("missing final parenthesis") if !s/^\\//; | |
1689 | } | |
1690 | # Found a ')' or end of line | |
1691 | elsif (/^\)/ || /^$/) { | |
1692 | s/^\)/\\/; # Signals: left parenthesis found | |
1693 | $expr = $_; # Remove interpreted stuff | |
1694 | &execute() while $#val > 0; # Executed stacked operations | |
1695 | while ($#op >= 0) { | |
1696 | $_ = pop(@op); | |
1697 | &error("missing second operand for '$_' (diadic operator)"); | |
1698 | } | |
1699 | return $val[0]; | |
1700 | } | |
1701 | # A perl statement '{{' | |
1702 | elsif (s/^\{\{//) { | |
1703 | if (s/^(.*)\}\}//) { | |
1704 | &push_val((system | |
1705 | ('perl','-e', "if ($1) {exit 0;} else {exit 1;}" | |
1706 | ))? 0 : 1); | |
1707 | } else { | |
1708 | &error("incomplete perl statement"); | |
1709 | } | |
1710 | } | |
1711 | # A shell statement '{' | |
1712 | elsif (s/^\{//) { | |
1713 | if (s/^(.*)\}//) { | |
1714 | &push_val((system | |
1715 | ("if $1 >/dev/null 2>&1; then exit 0; else exit 1; fi" | |
1716 | ))? 0 : 1); | |
1717 | } else { | |
1718 | &error("incomplete shell statement"); | |
1719 | } | |
1720 | } | |
1721 | # Operator '||' and '&&' | |
1722 | elsif (s/^(\|\||&&)//) { | |
1723 | $tmp = $1; # Save for perl5 (Dataloaded update_stack) | |
1724 | &update_stack($tmp); | |
1725 | } | |
1726 | # Unary operator '!' | |
1727 | elsif (s/^!//) { | |
1728 | push(@mono,'!'); | |
1729 | } | |
1730 | # Everything else is a test for a defined value | |
1731 | elsif (s/^([\?%]?\w+)//) { | |
1732 | $tmp = $1; | |
1733 | # Test for wanted | |
1734 | if ($tmp =~ s/^\?//) { | |
1735 | &push_val(($main'symwanted{$tmp})? 1 : 0); | |
1736 | } | |
1737 | # Test for conditionally wanted | |
1738 | elsif ($tmp =~ s/^%//) { | |
1739 | &push_val(($main'condwanted{$tmp})? 1 : 0); | |
1740 | } | |
1741 | # Default: test for definition (see op @define) | |
1742 | else { | |
1743 | &push_val(( | |
1744 | $main'symwanted{$tmp} || | |
1745 | $main'cmaster{$tmp} || | |
1746 | $main'userdef{$tmp}) ? 1 : 0); | |
1747 | } | |
1748 | } | |
1749 | # An error occured -- we did not recognize the expression | |
1750 | else { | |
1751 | s/^([^\s\(\)\{\|&!]+)//; # Skip until next meaningful char | |
1752 | } | |
1753 | } | |
1754 | } | |
1755 | ||
1756 | # Given an expression in a '@' command, returns a boolean which is | |
1757 | # the result of the evaluation. Evaluate is collecting all the lines | |
1758 | # in the expression into a single string, and then calls eval_expr to | |
1759 | # really evaluate it. | |
1760 | sub evaluate { | |
1761 | local($val); # Value returned | |
1762 | local($expr) = ""; # Expression to be parsed | |
1763 | chop; | |
1764 | while (s/\\$//) { # While end of line escaped | |
1765 | $expr .= $_; | |
1766 | $_ = <UNIT>; # Fetch next line | |
1767 | unless ($_) { | |
1768 | &error("EOF in expression"); | |
1769 | last; | |
1770 | } | |
1771 | chop; | |
1772 | } | |
1773 | $expr .= $_; | |
1774 | while ($expr ne '') { | |
1775 | $val = &eval_expr(*expr); # Expression will be modified | |
1776 | # We return from eval_expr either when a closing parenthisis | |
1777 | # is found, or when the expression has been fully analysed. | |
1778 | &error("extra closing parenthesis ignored") if $expr ne ''; | |
1779 | } | |
1780 | $val; | |
1781 | } | |
1782 | ||
1783 | # Given a line, we search for commands (lines starting with '@'). | |
1784 | # If there is no command in the line, then we return the boolean state. | |
1785 | # Otherwise, the command is analysed and a new state is computed. | |
1786 | # The returned value of interpret is 1 if the line is to be printed. | |
1787 | sub main'interpret { | |
1788 | local($value); | |
1789 | local($status) = $state[$#state]; # Current status | |
1790 | if (s|^\s*@\s*(\w+)\s*(.*)|$2|) { | |
1791 | local($cmd) = $1; | |
1792 | $cmd =~ y/A-Z/a-z/; # Canonicalize to lower case | |
1793 | # The 'define' command | |
1794 | if ($cmd eq 'define') { | |
1795 | chop; | |
1796 | $userdef{$_}++ if $Keep[$status]; | |
1797 | return 0; | |
1798 | } | |
1799 | # The 'if' command | |
1800 | elsif ($cmd eq 'if') { | |
1801 | # We always evaluate, in order to find possible errors | |
1802 | $value = &evaluate($_); | |
1803 | if (!$Keep[$status]) { | |
1804 | # We have to skip until next 'end' | |
1805 | push(@state, $SKIP); # Record structure | |
1806 | return 0; | |
1807 | } | |
1808 | if ($value) { # True | |
1809 | push(@state, $IF); | |
1810 | return 0; | |
1811 | } else { # False | |
1812 | push(@state, $NOT); | |
1813 | return 0; | |
1814 | } | |
1815 | } | |
1816 | # The 'else' command | |
1817 | elsif ($cmd eq 'else') { | |
1818 | &error("expression after 'else' ignored") if /\S/; | |
1819 | $state[$#state] = $SKIP if $state[$#state] == $IF; | |
1820 | return 0 if $state[$#state] == $SKIP; | |
1821 | if ($state[$#state] == $OUT) { | |
1822 | &error("unexpected 'else'"); | |
1823 | return 0; | |
1824 | } | |
1825 | $state[$#state] = $ELSE; | |
1826 | return 0; | |
1827 | } | |
1828 | # The 'elsif' command | |
1829 | elsif ($cmd eq 'elsif') { | |
1830 | # We always evaluate, in order to find possible errors | |
1831 | $value = &evaluate($_); | |
1832 | $state[$#state] = $SKIP if $state[$#state] == $IF; | |
1833 | return 0 if $state[$#state] == $SKIP; | |
1834 | if ($state[$#state] == $OUT) { | |
1835 | &error("unexpected 'elsif'"); | |
1836 | return 0; | |
1837 | } | |
1838 | if ($value) { # True | |
1839 | $state[$#state] = $IF; | |
1840 | return 0; | |
1841 | } else { # False | |
1842 | $state[$#state] = $NOT; | |
1843 | return 0; | |
1844 | } | |
1845 | } | |
1846 | # The 'end' command | |
1847 | elsif ($cmd eq 'end') { | |
1848 | &error("expression after 'end' ignored") if /\S/; | |
1849 | pop(@state); | |
1850 | &error("unexpected 'end'") if $#state < 0; | |
1851 | return 0; | |
1852 | } | |
1853 | # Unknown command | |
1854 | else { | |
1855 | &error("unknown command '$cmd'"); | |
1856 | return 0; | |
1857 | } | |
1858 | } | |
1859 | $Keep[$status]; | |
1860 | } | |
1861 | ||
1862 | package main; | |
1863 | ||
1864 | sub readpackage { | |
1865 | if (! -f '.package') { | |
1866 | if ( | |
1867 | -f '../.package' || | |
1868 | -f '../../.package' || | |
1869 | -f '../../../.package' || | |
1870 | -f '../../../../.package' | |
1871 | ) { | |
1872 | die "Run in top level directory only.\n"; | |
1873 | } else { | |
1874 | die "No .package file! Run packinit.\n"; | |
1875 | } | |
1876 | } | |
1877 | open(PACKAGE,'.package'); | |
1878 | while (<PACKAGE>) { | |
1879 | next if /^:/; | |
1880 | next if /^#/; | |
1881 | if (($var,$val) = /^\s*(\w+)=(.*)/) { | |
1882 | $val = "\"$val\"" unless $val =~ /^['"]/; | |
1883 | eval "\$$var = $val;"; | |
1884 | } | |
1885 | } | |
1886 | close PACKAGE; | |
1887 | } | |
1888 | ||
1889 | sub manifake { | |
1890 | # make MANIFEST and MANIFEST.new say the same thing | |
1891 | if (! -f $NEWMANI) { | |
1892 | if (-f $MANI) { | |
1893 | open(IN,$MANI) || die "Can't open $MANI"; | |
1894 | open(OUT,">$NEWMANI") || die "Can't create $NEWMANI"; | |
1895 | while (<IN>) { | |
1896 | if (/---/) { | |
1897 | # Everything until now was a header... | |
1898 | close OUT; | |
1899 | open(OUT,">$NEWMANI") || | |
1900 | die "Can't recreate $NEWMANI"; | |
1901 | next; | |
1902 | } | |
1903 | s/^\s*(\S+\s+)[0-9]*\s*(.*)/$1$2/; | |
1904 | print OUT; | |
1905 | print OUT "\n" unless /\n$/; # If no description | |
1906 | } | |
1907 | close IN; | |
1908 | close OUT; | |
1909 | } | |
1910 | else { | |
1911 | die "You need to make a $NEWMANI file, with names and descriptions.\n"; | |
1912 | } | |
1913 | } | |
1914 | } | |
1915 | ||
1916 | # Perform ~name expansion ala ksh... | |
1917 | # (banish csh from your vocabulary ;-) | |
1918 | sub tilda_expand { | |
1919 | local($path) = @_; | |
1920 | return $path unless $path =~ /^~/; | |
1921 | $path =~ s:^~([^/]+):(getpwnam($1))[$[+7]:e; # ~name | |
1922 | $path =~ s:^~:$ENV{'HOME'} || (getpwuid($<))[$[+7]:e; # ~ | |
1923 | $path; | |
1924 | } | |
1925 | ||
1926 | # Set up profile components into %Profile, add any profile-supplied options | |
1927 | # into @ARGV and return the command invocation name. | |
1928 | sub profile { | |
1929 | local($profile) = &tilda_expand($ENV{'DIST'} || '~/.dist_profile'); | |
1930 | local($me) = $0; # Command name | |
1931 | $me =~ s|.*/(.*)|$1|; # Keep only base name | |
1932 | return $me unless -s $profile; | |
1933 | local(*PROFILE); # Local file descriptor | |
1934 | local($options) = ''; # Options we get back from profile | |
1935 | unless (open(PROFILE, $profile)) { | |
1936 | warn "$me: cannot open $profile: $!\n"; | |
1937 | return; | |
1938 | } | |
1939 | local($_); | |
1940 | local($component); | |
1941 | while (<PROFILE>) { | |
1942 | next if /^\s*#/; # Skip comments | |
1943 | next unless /^$me/o; | |
1944 | if (s/^$me://o) { # progname: options | |
1945 | chop; | |
1946 | $options .= $_; # Merge options if more than one line | |
1947 | } | |
1948 | elsif (s/^$me-([^:]+)://o) { # progname-component: value | |
1949 | $component = $1; | |
1950 | chop; | |
1951 | s/^\s+//; # Trim leading and trailing spaces | |
1952 | s/\s+$//; | |
1953 | $Profile{$component} = $_; | |
1954 | } | |
1955 | } | |
1956 | close PROFILE; | |
1957 | return unless $options; | |
1958 | require 'shellwords.pl'; | |
1959 | local(@opts); | |
1960 | eval '@opts = &shellwords($options)'; # Protect against mismatched quotes | |
1961 | unshift(@ARGV, @opts); | |
1962 | return $me; # Return our invocation name | |
1963 | } | |
1964 |