Generated: Tue Feb 2 17:54:20 2010 from amfile01.pl 2006/07/26 21.3 KB.
#!/Perl # READ Makefile.am my $debug_on3 = 0; my $debug_on4 = 0; my $verbose = 0; my $am_file = ''; my $exit_status = 0; # String constants. my $IGNORE_PATTERN = "^##([^#].*)?\$"; my $WHITE_PATTERN = "^[ \t]*\$"; my $COMMENT_PATTERN = "^#"; my $IF_PATTERN = "^if[ \t]+\([A-Za-z][A-Za-z0-9_]*\)[ \t]*\(#.*\)?\$"; my $ELSE_PATTERN = "^else[ \t]*\(#.*\)?\$"; my $ENDIF_PATTERN = "^endif[ \t]*\(#.*\)?\$"; my $RULE_PATTERN = "^([\$a-zA-Z_.][-.a-zA-Z0-9_(){}/\$]*) *:([^=].*|)\$"; my $SUFFIX_RULE_PATTERN = "^\\.([a-zA-Z]+)\\.([a-zA-Z]+)\$"; my $MACRO_PATTERN = "^([A-Za-z][A-Za-z0-9_]*)[ \t]*([:+]?)=[ \t]*(.*)\$"; my $BOGUS_MACRO_PATTERN = "^([^ \t]*)[ \t]*([:+]?)=[ \t]*(.*)\$"; # Hash table of AM_CONDITIONAL variables seen in configure. my %configure_cond = (); # This holds the names which are targets. These also appear in # %contents. my %targets = (); # This holds the line numbers at which various elements of # %contents are defined. my %content_lines = (); my @conditional_stack = (); my %contents = (); my %conditional = (); my $output_vars = ''; my $output_trailer = ''; # This maps the source extension of a suffix rule to its # corresponding output extension. my %suffix_rules = (); # Hash table of discovered configure substitutions. Keys are names, # values are `FILE:LINE' strings which are used by error message # generation. my %configure_vars = (); my %am_vars = (); my %def_type = (); my @var_list = (); my @include_stack = (); my %content_seen = (); my $relative_dir = ''; sub read_am_file { $am_file = shift; open( AM_FILE, $am_file ) or mydie( "Can't open $am_file: $!\n" ); prt( "$pack: reading $am_file\n" ) if $verbose; my @am_file_sources = (); my $saw_bk = 0; my $was_rule = 0; my $spacing = ''; my $comment = ''; my $last_var_name = ''; my $blank = 0; my $cond_true = 2; # undetermined ... while (<AM_FILE>) { if (/$IGNORE_PATTERN/o) { # Merely delete comments beginning with two hashes. } elsif (/$WHITE_PATTERN/o) { # Stick a single white line before the incoming macro or rule. $spacing = "\n"; $blank = 1; } elsif (/$COMMENT_PATTERN/o) { # Stick comments before the incoming macro or rule. Make # sure a blank line preceeds first block of comments. $spacing = "\n" unless $blank; $blank = 1; $comment .= $spacing . $_; $spacing = ''; } else { last; } } $output_vars .= $comment . "\n"; $comment = ''; $spacing = "\n"; my $source_suffix_pattern = ''; my $is_ok_macro = 0; while ($_) { if ($debug_on3) { my $aml = $_; chomp $aml; $aml =~ s/\r$//; # and remove CR, if present prt( "$. [$aml]\n" ); } $_ .= "\n" unless substr ($_, -1, 1) eq "\n"; if (/$IGNORE_PATTERN/o) { # Merely delete comments beginning with two hashes. } elsif (/$WHITE_PATTERN/o) { # Stick a single white line before the incoming macro or rule. $spacing = "\n"; &am_line_error ($., "blank line following trailing backslash") if $saw_bk; } elsif (/$COMMENT_PATTERN/o) { # Stick comments before the incoming macro or rule. $comment .= $spacing . $_; $spacing = ''; &am_line_error ($., "comment following trailing backslash") if $saw_bk; } elsif ($saw_bk) { # continuation of previous line(s) ... if ($was_rule) { $output_trailer .= join ('', @conditional_stack) . $_; $saw_bk = /\\$/; } else { $saw_bk = /\\$/; # Chop newline and backslash if this line is # continued. ensure trailing whitespace exists. chop if $saw_bk; chop if $saw_bk; # decide if to be added to 'contents' ... my $add_it = 0; # assume NO if ( defined $contents{$last_var_name} ) { if ( @conditional_stack ) { if ( $conditional_stack[$#conditional_stack] =~ /_TRUE\@$/ ) { # we are in the if TRUE state if ($cond_true == 1) { $add_it = 1; } } else { # we are in a FALSE state if ($cond_true != 1) { $add_it = 1; } } } else { ###prt( "No conditional stack for this macro! lv=[$last_var_name] ". ### "add_it\n" ) if ($debug_on3); $add_it = 1; # so add it } } else { # NO contents for this var prt( "NO contents for [$last_var_name]! ". "Technical this is an ERROR, but for now add_it...\n" ) if ($debug_on3); $add_it = 1; } if ($add_it) { $contents{$last_var_name} .= ' ' unless $contents{$last_var_name} =~ /\s$/; $contents{$last_var_name} .= $_; if (@conditional_stack) { $conditional{$last_var_name} .= "e_cond_val ($_); } prt( "End $last_var_name = [". trim_line($contents{$last_var_name})."]...\n" ) if ( ! $saw_bk && $debug_on3); if ( !$saw_bk && ($last_var_name =~ /._SOURCES$/) ) { prt( "NOTE list of SOURCE files...\n" ) if ($debug_on4); prt( "[".trim_line($contents{$last_var_name})."]...\n" ) if ($debug_on4); push(@am_file_sources, split( / /, $contents{$last_var_name})); } } else { prt( "Warning: Discarding this [$_] ... CHECK ME!\n" ) if ($debug_on3); } } } elsif (/$IF_PATTERN/o) { if ( defined $configure_cond{$1} ) { ###if ( defined $cfg_defines{$1} ) if ($configure_cond{$1} == 2) { prt( "Found if $1, and condition is TRUE (see cfg_defines) ...\n" ) if ($debug_on3); $cond_true = 1; } else { prt( "Found if $1, but NOT in cfg_defines ...\n" ) if ($debug_on3); $cond_true = 0; } } else { &am_line_error ($., "$1 does not appear in AM_CONDITIONAL"); } push (@conditional_stack, "\@" . $1 . "_TRUE\@"); } elsif (/$ELSE_PATTERN/o) { if (! @conditional_stack) { &am_line_error ($., "else without if"); } elsif ($conditional_stack[$#conditional_stack] =~ /_FALSE\@$/) { &am_line_error ($., "else after else"); } else { $conditional_stack[$#conditional_stack] =~ s/_TRUE\@$/_FALSE\@/; } } elsif (/$ENDIF_PATTERN/o) { if (! @conditional_stack) { &am_line_error ($., ": endif without if"); } else { pop @conditional_stack; } $cond_true = 2; # undetermined ... } elsif (/$RULE_PATTERN/o) { # Found a rule. $was_rule = 1; if (defined $contents{$1} && (@conditional_stack ? ! defined $conditional{$1} : defined $conditional{$1})) { &am_line_error ($1, "$1 defined both conditionally and unconditionally"); } # Value here doesn't matter; for targets we only note # existence. $contents{$1} = 1; $targets{$1} = 1; my $cond_string = join ('', @conditional_stack); if (@conditional_stack) { if ($conditional{$1}) { &check_ambiguous_conditional ($1, $cond_string); $conditional{$1} .= ' '; } else { $conditional{$1} = ''; } $conditional{$1} .= $cond_string . ' 1'; } $content_lines{$1} = $.; $output_trailer .= $comment . $spacing . $cond_string . $_; $comment = $spacing = ''; $saw_bk = /\\$/; # Check the rule for being a suffix rule. If so, store in # a hash. my $source_suffix; my $object_suffix; if (($source_suffix, $object_suffix) = ($1 =~ $SUFFIX_RULE_PATTERN)) { $suffix_rules{$source_suffix} = $object_suffix; prt( "Sources ending in .$source_suffix become .$object_suffix\n" ) if $verbose; $source_suffix_pattern = "(" . join('|', keys %suffix_rules) . ")"; } # FIXME: make sure both suffixes are in SUFFIXES? Or set # SUFFIXES from suffix_rules? } elsif (($is_ok_macro = /$MACRO_PATTERN/o) || /$BOGUS_MACRO_PATTERN/o) { prt( "Found a macro definition. 1[$1] 2[$2] 3[$3] ...\n" ) if ($debug_on3); $was_rule = 0; $last_var_name = $1; if (defined $contents{$1} && (@conditional_stack ? ! defined $conditional{$1} : defined $conditional{$1})) { &am_line_error ($1, "$1 defined both conditionally and unconditionally"); } my $value; if ($3 ne '' && substr ($3, -1) eq "\\") { $value = substr ($3, 0, length ($3) - 1); } else { $value = $3; } my $type = $2; if ($type eq '+') { if (! defined $contents{$last_var_name} && defined $configure_vars{$last_var_name}) { $contents{$last_var_name} = '@' . $last_var_name . '@'; } $contents{$last_var_name} .= ' ' . $value; } else { if ( defined $contents{$last_var_name} ) { if ( @conditional_stack ) { if ( $conditional_stack[$#conditional_stack] =~ /_TRUE\@$/ ) { # we are in the if TRUE state if ($cond_true == 1) { if ($debug_on3) { if ( $contents{$last_var_name} ne $value ) { prt( "NB: [$last_var_name] = [$contents{$last_var_name}]" . " replaced with [$value] (am_file=$am_file line=$.)". (/\\$/ ? "saw_bk" : "end") . "\n" ); } } $contents{$last_var_name} = $value; $content_lines{$last_var_name} = $.; } } else { # we are in the else FALSE state if ($cond_true != 1) { if ($debug_on3) { if ( $contents{$last_var_name} ne $value ) { prt( "NB: [$last_var_name] = [$contents{$last_var_name}]" . " replaced with [$value] (am_file=$am_file line=$.)". (/\\$/ ? "saw_bk" : "end") . "\n" ); } } $contents{$last_var_name} = $value; $content_lines{$last_var_name} = $.; } } } } else { prt( "First setting [$last_var_name] = [$value] " . "(am_file=$am_file line=$.) ". (/\\$/ ? "saw_bk" : "end") . "\n" ) if ($debug_on3); $contents{$last_var_name} = $value; # The first assignment to a macro sets the line # number. Ideally I suppose we would associate line # numbers with random bits of text. $content_lines{$last_var_name} = $.; } } my $cond_string = join ('', @conditional_stack); if (@conditional_stack) { my $found = 0; my $val; if ($conditional{$last_var_name}) { if ($type eq '+') { # If we're adding to the conditional, and it # exists, then we might want to simply replace # the old value with the new one. my (@new_vals, @cond_vals); @cond_vals = split (' ', $conditional{$last_var_name}); while (@cond_vals) { $vcond = shift (@cond_vals); push (@new_vals, $vcond); if (&conditional_same ($vcond, $cond_string)) { $found = 1; $val = (&unquote_cond_val (shift (@cond_vals)) . ' ' . $value); push (@new_vals, "e_cond_val ($val)); } else { push (@new_vals, shift (@cond_vals)); } } if ($found) { $conditional{$last_var_name} = join (' ', @new_vals); } } if (! $found) { &check_ambiguous_conditional ($last_var_name, $cond_string); $conditional{$last_var_name} .= ' '; $val = $value; } } else { $conditional{$last_var_name} = ''; $val = $contents{$last_var_name}; } if (! $found) { $conditional{$last_var_name} .= ($cond_string . ' ' . "e_cond_val ($val)); } } # FIXME: this doesn't always work correctly; it will group # all comments for a given variable, no matter where # defined. $am_vars{$last_var_name} = $comment . $spacing; $def_type{$last_var_name} = ($type eq ':') ? ':' : ''; push (@var_list, $last_var_name); $comment = $spacing = ''; $saw_bk = /\\$/; # Error if bogus. &am_line_error ($., "bad macro name \`$last_var_name'") if ! $is_ok_macro; } elsif (/$INCLUDE_PATTERN/o) { my ($path) = $1; if ($path =~ s/^\$\(top_srcdir\)\///) { push (@include_stack, "\$\(top_srcdir\)/$path"); } else { $path =~ s/\$\(srcdir\)\///; push (@include_stack, "\$\(srcdir\)/$path"); $path = $relative_dir . "/" . $path; } &read_am_file ($path); } else { # This isn't an error; it is probably a continued rule. # In fact, this is what we assume. $was_rule = 1; $output_trailer .= ($comment . $spacing . join ('', @conditional_stack) . $_); $comment = $spacing = ''; $saw_bk = /\\$/; } $_ = <AM_FILE>; } close(AM_FILE); $output_trailer .= $comment; &am_error ("unterminated conditionals: " . join (' ', @conditional_stack)) if (@conditional_stack); return @am_file_sources; } sub am_error { my $msg = shift; mydie( $msg."\n" ); } sub am_line_error { my ($symbol, @args) = @_; prt( "am_line_error: am_file=[$am_file] sym=[$symbol] arg0=[$args[0]] ...\n" ) if ($debug_on3); if ($symbol && "$symbol" ne '-1') { my ($file) = "${am_file}"; if ($symbol =~ /^\d+$/) { # SYMBOL is a line number, so just add the colon. $file .= ':' . $symbol; } elsif (defined $content_lines{$symbol}) { # SYMBOL is a variable defined in Makefile.am, so add the # line number we saved from there. $file .= ':' . $content_lines{$symbol}; } elsif (defined $configure_vars{$symbol}) { # SYMBOL is a variable defined in configure.ac, so add the # appropriate line number. $file = $configure_vars{$symbol}; } else { # Couldn't find the line number. } warn $file, ": ", join (' ', @args), "\n"; $exit_status = 1; } else { &am_error (@args); } } # See if a conditional is true. Both arguments are conditional # strings. This returns true if the first conditional is true when # the second conditional is true. sub conditional_true_when { my ($cond, $when) = @_; # Check the easy case first. if ($cond eq $when) { return 1; } # Check each component of $cond, which looks @COND1@@COND2@. foreach my $comp (split ('@', $cond)) { # The way we split will give null strings between each # condition. next if ! $comp; if (index ($when, '@' . $comp . '@') == -1) { return 0; } } return 1; } # Check for an ambiguous conditional. This is called when a variable # or target is being defined conditionally. If we already know about # a definition that is true under the same conditions, then we have an # ambiguity. sub check_ambiguous_conditional { my ($var_name, $cond) = @_; my (@cond_vals) = split (' ', $conditional{$var_name}); while (@cond_vals) { my ($vcond) = shift (@cond_vals); shift (@cond_vals); if (&conditional_true_when ($vcond, $cond) || &conditional_true_when ($cond, $vcond)) { prt( "$var_name multiply defined in condition\n" ); } } } # Quote a value in order to put it in $conditional. We need to quote # spaces, and we need to handle null strings, so that we can later # retrieve values by splitting on space. sub quote_cond_val { my ($val) = @_; $val =~ s/ /\001/g; $val =~ s/\t/\003/g; $val = "\002" if $val eq ''; return $val; } # this function seems MISSING - maybe never used! sub unquote_cond_val { my ($val) = @_; $val =~ s/\001/ /g; $val =~ s/\003/\t/g; $val = '' if $val eq "\002"; return $val; } sub initialize_per_input { # These two variables are used when generating each Makefile.in. # They hold the Makefile.in until it is ready to be printed. $output_vars = ''; $output_trailer = ''; # This holds the contents of a Makefile.am, as parsed by # read_am_file. %contents = (); # This holds the names which are targets. These also appear in # %contents. %targets = (); # For a variable or target which is defined conditionally, this # holds an array of the conditional values. The array is composed # of pairs of condition strings (the variables which configure # will substitute) and values (the value of a target is # meaningless). For an unconditional variable, this is empty. %conditional = (); # This holds the line numbers at which various elements of # %contents are defined. %content_lines = (); # This holds a 1 if a particular variable was examined. %content_seen = (); # This is the conditional stack. @conditional_stack = (); # This holds the set of included files. @include_stack = (); # This holds the "relative directory" of the current Makefile.in. # Eg for src/Makefile.in, this is "src". $relative_dir = ''; # This maps the source extension of a suffix rule to its # corresponding output extension. %suffix_rules = (); } sub trim_line { my ($l) = shift; chomp $l; $l =~ s/\r$//; # and remove CR, if present $l =~ s/\t/ /g; $l =~ s/\s\s/ /g while ($l =~ /\s\s/); #$l = substr($l,1) while ($l =~ /^\s/); #$l = substr($l,0,length($l)-1) while (($l =~ /\s$/)&&(length($l))); for ($l) { s/^\s+//; s/\s+$//; } return $l; } 1; # eof - amfile01.pl