<html><head><meta name="color-scheme" content="light dark"></head><body><pre style="word-wrap: break-word; white-space: pre-wrap;"># Copyright (C) 2003-2021 Free Software Foundation, Inc.

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.

# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see &lt;https://www.gnu.org/licenses/&gt;.

package Automake::Variable;

use 5.006;
use strict;
use warnings FATAL =&gt; 'all';

use Carp;
use Exporter;

use Automake::Channels;
use Automake::ChannelDefs;
use Automake::Configure_ac;
use Automake::Item;
use Automake::VarDef;
use Automake::Condition qw (TRUE FALSE);
use Automake::DisjConditions;
use Automake::General 'uniq';
use Automake::Wrap 'makefile_wrap';

our @ISA = qw (Automake::Item Exporter);
our @EXPORT = qw (err_var msg_var msg_cond_var reject_var
		  var rvar vardef rvardef
		  variables
		  scan_variable_expansions check_variable_expansions
		  variable_delete
		  variables_dump
		  set_seen
		  require_variables
		  variable_value
		  output_variables
		  transform_variable_recursively);

=head1 NAME

Automake::Variable - support for variable definitions

=head1 SYNOPSIS

  use Automake::Variable;
  use Automake::VarDef;

  # Defining a variable.
  Automake::Variable::define($varname, $owner, $type,
                             $cond, $value, $comment,
                             $where, $pretty)

  # Looking up a variable.
  my $var = var $varname;
  if ($var)
    {
      ...
    }

  # Looking up a variable that is assumed to exist.
  my $var = rvar $varname;

  # The list of conditions where $var has been defined.
  # ($var-&gt;conditions is an Automake::DisjConditions,
  # $var-&gt;conditions-&gt;conds is a list of Automake::Condition.)
  my @conds = $var-&gt;conditions-&gt;conds

  # Access to the definition in Condition $cond.
  # $def is an Automake::VarDef.
  my $def = $var-&gt;def ($cond);
  if ($def)
    {
      ...
    }

  # When the conditional definition is assumed to exist, use
  my $def = $var-&gt;rdef ($cond);


=head1 DESCRIPTION

This package provides support for Makefile variable definitions.

An C&lt;Automake::Variable&gt; is a variable name associated to possibly
many conditional definitions.  These definitions are instances
of C&lt;Automake::VarDef&gt;.

Therefore obtaining the value of a variable under a given
condition involves two lookups.  One to look up the variable,
and one to look up the conditional definition:

  my $var = var $name;
  if ($var)
    {
      my $def = $var-&gt;def ($cond);
      if ($def)
        {
          return $def-&gt;value;
        }
      ...
    }
  ...

When it is known that the variable and the definition
being looked up exist, the above can be simplified to

  return var ($name)-&gt;def ($cond)-&gt;value; # Do not write this.

but is better written

  return rvar ($name)-&gt;rdef ($cond)-&gt;value;

or even

  return rvardef ($name, $cond)-&gt;value;

The I&lt;r&gt; variants of the C&lt;var&gt;, C&lt;def&gt;, and C&lt;vardef&gt; methods add an
extra test to ensure that the lookup succeeded, and will diagnose
failures as internal errors (with a message which is much more
informative than Perl's warning about calling a method on a
non-object).

=cut

my $_VARIABLE_CHARACTERS = '[.A-Za-z0-9_@]+';
my $_VARIABLE_PATTERN = '^' . $_VARIABLE_CHARACTERS . "\$";
my $_VARIABLE_RECURSIVE_PATTERN =
    '^([.A-Za-z0-9_@]|\$[({]' . $_VARIABLE_CHARACTERS . '[})]?)+' . "\$";

# The order in which variables should be output.  (May contain
# duplicates -- only the first occurrence matters.)
my @_var_order;

# This keeps track of all variables defined by &amp;_gen_varname.
# $_gen_varname{$base} is a hash for all variables defined with
# prefix '$base'.  Values stored in this hash are the variable names.
# Keys have the form "(COND1)VAL1(COND2)VAL2..." where VAL1 and VAL2
# are the values of the variable for condition COND1 and COND2.
my %_gen_varname = ();
# $_gen_varname_n{$base} is the number of variables generated by
# _gen_varname() for $base.  This is not the same as keys
# %{$_gen_varname{$base}} because %_gen_varname may also contain
# variables not generated by _gen_varname.
my %_gen_varname_n = ();

# Declare the macros that define known variables, so we can
# hint the user if she try to use one of these variables.

# Macros accessible via aclocal.
my %_am_macro_for_var =
  (
   CCAS =&gt; 'AM_PROG_AS',
   CCASFLAGS =&gt; 'AM_PROG_AS',
   EMACS =&gt; 'AM_PATH_LISPDIR',
   GCJ =&gt; 'AM_PROG_GCJ',
   LEX =&gt; 'AM_PROG_LEX',
   LIBTOOL =&gt; 'LT_INIT',
   lispdir =&gt; 'AM_PATH_LISPDIR',
   pkgpyexecdir =&gt; 'AM_PATH_PYTHON',
   pkgpythondir =&gt; 'AM_PATH_PYTHON',
   pyexecdir =&gt; 'AM_PATH_PYTHON',
   PYTHON =&gt; 'AM_PATH_PYTHON',
   pythondir =&gt; 'AM_PATH_PYTHON',
   );

# Macros shipped with Autoconf.
my %_ac_macro_for_var =
  (
   ALLOCA =&gt; 'AC_FUNC_ALLOCA',
   CC =&gt; 'AC_PROG_CC',
   CFLAGS =&gt; 'AC_PROG_CC',
   CXX =&gt; 'AC_PROG_CXX',
   CXXFLAGS =&gt; 'AC_PROG_CXX',
   F77 =&gt; 'AC_PROG_F77',
   FFLAGS =&gt; 'AC_PROG_F77',
   FC =&gt; 'AC_PROG_FC',
   FCFLAGS =&gt; 'AC_PROG_FC',
   OBJC =&gt; 'AC_PROG_OBJC',
   OBJCFLAGS =&gt; 'AC_PROG_OBJC',
   OBJCXX =&gt; 'AC_PROG_OBJCXX',
   OBJCXXFLAGS =&gt; 'AC_PROG_OBJCXX',
   RANLIB =&gt; 'AC_PROG_RANLIB',
   UPC =&gt; 'AM_PROG_UPC',
   UPCFLAGS =&gt; 'AM_PROG_UPC',
   YACC =&gt; 'AC_PROG_YACC',
   );

# The name of the configure.ac file.
my $configure_ac;

# Variables that can be overridden without complaint from -Woverride
my %_silent_variable_override =
  (AM_DISTCHECK_DVI_TARGET =&gt; 1,
   AM_MAKEINFOHTMLFLAGS =&gt; 1,
   AR =&gt; 1,
   ARFLAGS =&gt; 1,
   DEJATOOL =&gt; 1,
   JAVAC =&gt; 1,
   JAVAROOT =&gt; 1);

# Count of helper variables used to implement conditional '+='.
my $_appendvar;

# Each call to C&lt;Automake::Variable::traverse_recursively&gt; gets an
# unique label. This is used to detect recursively defined variables.
my $_traversal = 0;


=head2 Error reporting functions

In these functions, C&lt;$var&gt; can be either a variable name, or
an instance of C&lt;Automake::Variable&gt;.

=over 4

=item C&lt;err_var ($var, $message, [%options])&gt;

Uncategorized errors about variables.

=cut

sub err_var ($$;%)
{
  msg_var ('error', @_);
}

=item C&lt;msg_cond_var ($channel, $cond, $var, $message, [%options])&gt;

Messages about conditional variable.

=cut

sub msg_cond_var ($$$$;%)
{
  my ($channel, $cond, $var, $msg, %opts) = @_;
  my $v = ref ($var) ? $var : rvar ($var);
  msg $channel, $v-&gt;rdef ($cond)-&gt;location, $msg, %opts;
}

=item C&lt;msg_var ($channel, $var, $message, [%options])&gt;

Messages about variables.

=cut

sub msg_var ($$$;%)
{
  my ($channel, $var, $msg, %opts) = @_;
  my $v = ref ($var) ? $var : rvar ($var);
  # Don't know which condition is concerned.  Pick any.
  my $cond = $v-&gt;conditions-&gt;one_cond;
  msg_cond_var $channel, $cond, $v, $msg, %opts;
}

=item C&lt;$bool = reject_var ($varname, $error_msg)&gt;

Bail out with C&lt;$error_msg&gt; if a variable with name C&lt;$varname&gt; has
been defined.

Return true iff C&lt;$varname&gt; is defined.

=cut

sub reject_var ($$)
{
  my ($var, $msg) = @_;
  my $v = var ($var);
  if ($v)
    {
      err_var $v, $msg;
      return 1;
    }
  return 0;
}

=back

=head2 Administrative functions

=over 4

=item C&lt;Automake::Variable::hook ($varname, $fun)&gt;

Declare a function to be called whenever a variable
named C&lt;$varname&gt; is defined or redefined.

C&lt;$fun&gt; should take two arguments: C&lt;$type&gt; and C&lt;$value&gt;.
When type is C&lt;''&gt; or &lt;':'&gt;, C&lt;$value&gt; is the value being
assigned to C&lt;$varname&gt;.  When C&lt;$type&gt; is C&lt;'+'&gt;, C&lt;$value&gt;
is the value being appended to  C&lt;$varname&gt;.

=cut

our %_hooks;
sub hook ($$)
{
  my ($var, $fun) = @_;
  $_hooks{$var} = $fun;
}

=item C&lt;variables ([$suffix])&gt;

Returns the list of all L&lt;Automake::Variable&gt; instances.  (I.e., all
variables defined so far.)  If C&lt;$suffix&gt; is supplied, return only
the L&lt;Automake::Variable&gt; instances that ends with C&lt;_$suffix&gt;.

=cut

our (%_variable_dict, %_primary_dict);
sub variables (;$)
{
  my ($suffix) = @_;
  my @vars = ();
  if ($suffix)
    {
      if (exists $_primary_dict{$suffix})
	{
	  @vars = values %{$_primary_dict{$suffix}};
	}
    }
  else
    {
      @vars = values %_variable_dict;
    }
  # The behaviour of the 'sort' built-in is undefined in scalar
  # context, hence we need an ad-hoc handling for such context.
  return wantarray ? sort { $a-&gt;name cmp $b-&gt;name } @vars : scalar @vars;
}

=item C&lt;Automake::Variable::reset&gt;

The I&lt;forget all&gt; function.  Clears all know variables and reset some
other internal data.

=cut

sub reset ()
{
  %_variable_dict = ();
  %_primary_dict = ();
  $_appendvar = 0;
  @_var_order = ();
  %_gen_varname = ();
  %_gen_varname_n = ();
  $_traversal = 0;
}

=item C&lt;var ($varname)&gt;

Return the C&lt;Automake::Variable&gt; object for the variable
named C&lt;$varname&gt; if defined.  Return 0 otherwise.

=cut

sub var ($)
{
  my ($name) = @_;
  return $_variable_dict{$name} if exists $_variable_dict{$name};
  return 0;
}

=item C&lt;vardef ($varname, $cond)&gt;

Return the C&lt;Automake::VarDef&gt; object for the variable named
C&lt;$varname&gt; if defined in condition C&lt;$cond&gt;.  Return false
if the condition or the variable does not exist.

=cut

sub vardef ($$)
{
  my ($name, $cond) = @_;
  my $var = var $name;
  return $var &amp;&amp; $var-&gt;def ($cond);
}

# Create the variable if it does not exist.
# This is used only by other functions in this package.
sub _cvar ($)
{
  my ($name) = @_;
  my $v = var $name;
  return $v if $v;
  return _new Automake::Variable $name;
}

=item C&lt;rvar ($varname)&gt;

Return the C&lt;Automake::Variable&gt; object for the variable named
C&lt;$varname&gt;.  Abort with an internal error if the variable was not
defined.

The I&lt;r&gt; in front of C&lt;var&gt; stands for I&lt;required&gt;.  One
should call C&lt;rvar&gt; to assert the variable's existence.

=cut

sub rvar ($)
{
  my ($name) = @_;
  my $v = var $name;
  prog_error ("undefined variable $name\n" . &amp;variables_dump)
    unless $v;
  return $v;
}

=item C&lt;rvardef ($varname, $cond)&gt;

Return the C&lt;Automake::VarDef&gt; object for the variable named
C&lt;$varname&gt; if defined in condition C&lt;$cond&gt;.  Abort with an internal
error if the condition or the variable does not exist.

=cut

sub rvardef ($$)
{
  my ($name, $cond) = @_;
  return rvar ($name)-&gt;rdef ($cond);
}

=back

=head2 Methods

C&lt;Automake::Variable&gt; is a subclass of C&lt;Automake::Item&gt;.  See
that package for inherited methods.

Here are the methods specific to the C&lt;Automake::Variable&gt; instances.
Use the C&lt;define&gt; function, described latter, to create such objects.

=over 4

=cut

# Create Automake::Variable objects.  This is used
# only in this file.  Other users should use
# the "define" function.
sub _new ($$)
{
  my ($class, $name) = @_;
  my $self = Automake::Item::new ($class, $name);
  $self-&gt;{'scanned'} = 0;
  $self-&gt;{'last-append'} = []; # helper variable for last conditional append.
  $_variable_dict{$name} = $self;
  if ($name =~ /_([[:alnum:]]+)$/)
    {
      $_primary_dict{$1}{$name} = $self;
    }
  return $self;
}

# _check_ambiguous_condition ($SELF, $COND, $WHERE)
# -------------------------------------------------
# Check for an ambiguous conditional.  This is called when a variable
# 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_condition ($$$)
{
  my ($self, $cond, $where) = @_;
  my $var = $self-&gt;name;
  my ($message, $ambig_cond) = $self-&gt;conditions-&gt;ambiguous_p ($var, $cond);

  # We allow silent variables to be overridden silently,
  # by either silent or non-silent variables.
  my $def = $self-&gt;def ($ambig_cond);
  if ($message &amp;&amp; $def-&gt;pretty != VAR_SILENT)
    {
      msg 'syntax', $where, "$message ...", partial =&gt; 1;
      msg_var ('syntax', $var, "... '$var' previously defined here");
      verb ($self-&gt;dump);
    }
}

=item C&lt;$bool = $var-E&lt;gt&gt;check_defined_unconditionally ([$parent, $parent_cond])&gt;

Warn if the variable is conditionally defined.  C&lt;$parent&gt; is the name
of the parent variable, and C&lt;$parent_cond&gt; the condition of the parent
definition.  These two variables are used to display diagnostics.

=cut

sub check_defined_unconditionally ($;$$)
{
  my ($self, $parent, $parent_cond) = @_;

  if (!$self-&gt;conditions-&gt;true)
    {
      if ($parent)
	{
	  msg_cond_var ('unsupported', $parent_cond, $parent,
			"automake does not support conditional definition of "
			. $self-&gt;name . " in $parent");
	}
      else
	{
	  msg_var ('unsupported', $self,
		   "automake does not support " . $self-&gt;name
		   . " being defined conditionally");
	}
    }
}

=item C&lt;$str = $var-E&lt;gt&gt;output ([@conds])&gt;

Format all the definitions of C&lt;$var&gt; if C&lt;@cond&gt; is not specified,
else only that corresponding to C&lt;@cond&gt;.

=cut

sub output ($@)
{
  my ($self, @conds) = @_;

  @conds = $self-&gt;conditions-&gt;conds
    unless @conds;

  my $res = '';
  my $name = $self-&gt;name;

  foreach my $cond (@conds)
    {
      my $def = $self-&gt;def ($cond);
      prog_error ("unknown condition '" . $cond-&gt;human . "' for '"
		  . $self-&gt;name . "'")
	unless $def;

      next
	if $def-&gt;pretty == VAR_SILENT;

      $res .= $def-&gt;comment;

      my $val = $def-&gt;raw_value;
      my $equals = $def-&gt;type eq ':' ? ':=' : '=';
      my $str = $cond-&gt;subst_string;


      if ($def-&gt;pretty == VAR_ASIS)
	{
	  my $output_var = "$name $equals $val";
	  $output_var =~ s/^/$str/meg;
	  $res .= "$output_var\n";
	}
      elsif ($def-&gt;pretty == VAR_PRETTY)
	{
	  # Suppress escaped new lines.  &amp;makefile_wrap will
	  # add them back, maybe at other places.
	  $val =~ s/\\$//mg;
	  my $wrap = makefile_wrap ("$str$name $equals", "$str\t",
				    split (' ', $val));

	  # If the last line of the definition is made only of
	  # @substitutions@, append an empty variable to make sure it
	  # cannot be substituted as a blank line (that would confuse
	  # HP-UX Make).
	  $wrap = makefile_wrap ("$str$name $equals", "$str\t",
				 split (' ', $val), '$(am__empty)')
	    if $wrap =~ /\n(\s*@\w+@)+\s*$/;

	  $res .= $wrap;
	}
      else # ($def-&gt;pretty == VAR_SORTED)
	{
	  # Suppress escaped new lines.  &amp;makefile_wrap will
	  # add them back, maybe at other places.
	  $val =~ s/\\$//mg;
	  $res .= makefile_wrap ("$str$name $equals", "$str\t",
				 sort (split (' ' , $val)));
	}
    }
  return $res;
}

=item C&lt;@values = $var-E&lt;gt&gt;value_as_list ($cond, [$parent, $parent_cond])&gt;

Get the value of C&lt;$var&gt; as a list, given a specified condition,
without recursing through any subvariables.

C&lt;$cond&gt; is the condition of interest.  C&lt;$var&gt; does not need
to be defined for condition C&lt;$cond&gt; exactly, but it needs
to be defined for at most one condition implied by C&lt;$cond&gt;.

C&lt;$parent&gt; and C&lt;$parent_cond&gt; designate the name and the condition
of the parent variable, i.e., the variable in which C&lt;$var&gt; is
being expanded.  These are used in diagnostics.

For example, if C&lt;A&gt; is defined as "C&lt;foo $(B) bar&gt;" in condition
C&lt;TRUE&gt;, calling C&lt;rvar ('A')-&gt;value_as_list (TRUE)&gt; will return
C&lt;("foo", "$(B)", "bar")&gt;.

=cut

sub value_as_list ($$;$$)
{
  my ($self, $cond, $parent, $parent_cond) = @_;
  my @result;

  # Get value for given condition
  my $onceflag;
  foreach my $vcond ($self-&gt;conditions-&gt;conds)
    {
      if ($vcond-&gt;true_when ($cond))
	{
	  # If there is more than one definitions of $var matching
	  # $cond then we are in trouble: tell the user we need a
	  # paddle.  Continue by merging results from all conditions,
	  # although it doesn't make much sense.
	  $self-&gt;check_defined_unconditionally ($parent, $parent_cond)
	    if $onceflag;
	  $onceflag = 1;

	  my $val = $self-&gt;rdef ($vcond)-&gt;value;
	  push @result, split (' ', $val);
	}
    }
  return @result;
}

=item C&lt;@values = $var-E&lt;gt&gt;value_as_list_recursive ([%options])&gt;

Return the contents of C&lt;$var&gt; as a list, split on whitespace.  This
will recursively follow C&lt;$(...)&gt; and C&lt;${...}&gt; inclusions.  It
preserves C&lt;@...@&gt; substitutions.

C&lt;%options&gt; is a list of option for C&lt;Variable::traverse_recursively&gt;
(see this method).  The most useful is C&lt;cond_filter&gt;:

  $var-&gt;value_as_list_recursive (cond_filter =&gt; $cond)

will return the contents of C&lt;$var&gt; and any subvariable in all
conditions implied by C&lt;$cond&gt;.

C&lt;%options&gt; can also carry options specific to C&lt;value_as_list_recursive&gt;.
Presently, the only such option is C&lt;location =E&lt;gt&gt; 1&gt; which instructs
C&lt;value_as_list_recursive&gt; to return a list of C&lt;[$location, @values]&gt; pairs.

=cut

sub value_as_list_recursive ($;%)
{
  my ($var, %options) = @_;

  return $var-&gt;traverse_recursively
    (# Construct [$location, $value] pairs if requested.
     sub {
       my ($var, $val, $cond, $full_cond) = @_;
       return [$var-&gt;rdef ($cond)-&gt;location, $val] if $options{'location'};
       return $val;
     },
     # Collect results.
     sub {
       my ($var, $parent_cond, @allresults) = @_;
       return map { my ($cond, @vals) = @$_; @vals } @allresults;
     },
     %options);
}


=item C&lt;$bool = $var-E&lt;gt&gt;has_conditional_contents&gt;

Return 1 if C&lt;$var&gt; or one of its subvariable was conditionally
defined.  Return 0 otherwise.

=cut

sub has_conditional_contents ($)
{
  my ($self) = @_;

  # Traverse the variable recursively until we
  # find a variable defined conditionally.
  # Use 'die' to abort the traversal, and pass it '$full_cond'
  # to we can find easily whether the 'eval' block aborted
  # because we found a condition, or for some other error.
  eval
    {
      $self-&gt;traverse_recursively
	(sub
	 {
	   my ($subvar, $val, $cond, $full_cond) = @_;
	   die $full_cond if ! $full_cond-&gt;true;
	   return ();
	 },
	 sub { return (); });
    };
  if ($@)
    {
      return 1 if ref ($@) &amp;&amp; $@-&gt;isa ("Automake::Condition");
      # Propagate other errors.
      die;
    }
  return 0;
}


=item C&lt;$string = $var-E&lt;gt&gt;dump&gt;

Return a string describing all we know about C&lt;$var&gt;.
For debugging.

=cut

sub dump ($)
{
  my ($self) = @_;

  my $text = $self-&gt;name . ": \n  {\n";
  foreach my $vcond ($self-&gt;conditions-&gt;conds)
    {
      $text .= "    " . $vcond-&gt;human . " =&gt; " . $self-&gt;rdef ($vcond)-&gt;dump;
    }
  $text .= "  }\n";
  return $text;
}


=back

=head2 Utility functions

=over 4

=item C&lt;@list = scan_variable_expansions ($text)&gt;

Return the list of variable names expanded in C&lt;$text&gt;.  Note that
unlike some other functions, C&lt;$text&gt; is not split on spaces before we
check for subvariables.

=cut

sub scan_variable_expansions ($)
{
  my ($text) = @_;
  my @result = ();

  # Strip comments.
  $text =~ s/#.*$//;

  # Record each use of ${stuff} or $(stuff) that does not follow a $.
  while ($text =~ /(?&lt;!\$)\$(?:\{([^\}]*)\}|\(([^\)]*)\))/g)
    {
      my $var = $1 || $2;
      # The occurrence may look like $(string1[:subst1=[subst2]]) but
      # we want only 'string1'.
      $var =~ s/:[^:=]*=[^=]*$//;
      push @result, $var;
    }

  return @result;
}

=item C&lt;check_variable_expansions ($text, $where)&gt;

Check variable expansions in C&lt;$text&gt; and warn about any name that
does not conform to POSIX.  C&lt;$where&gt; is the location of C&lt;$text&gt;
for the error message.

=cut

sub check_variable_expansions ($$)
{
  my ($text, $where) = @_;
  # Catch expansion of variables whose name does not conform to POSIX.
  foreach my $var (scan_variable_expansions ($text))
    {
      if ($var !~ /$_VARIABLE_PATTERN/o)
	{
	  # If the variable name contains a space, it's likely
	  # to be a GNU make extension (such as $(addsuffix ...)).
	  # Mention this in the diagnostic.
	  my $gnuext = "";
	  $gnuext = "\n(probably a GNU make extension)" if $var =~ / /;
	  # Accept recursive variable expansions if so desired
	  # (we hope they are rather portable in practice).
	  if ($var =~ /$_VARIABLE_RECURSIVE_PATTERN/o)
	    {
	      msg ('portability-recursive', $where,
		   "$var: non-POSIX recursive variable expansion$gnuext");
	    }
	  else
	    {
	      msg ('portability', $where, "$var: non-POSIX variable name$gnuext");
	    }
	}
    }
}



=item C&lt;Automake::Variable::define($varname, $owner, $type, $cond, $value, $comment, $where, $pretty)&gt;

Define or append to a new variable.

C&lt;$varname&gt;: the name of the variable being defined.

C&lt;$owner&gt;: owner of the variable (one of C&lt;VAR_MAKEFILE&gt;,
C&lt;VAR_CONFIGURE&gt;, or C&lt;VAR_AUTOMAKE&gt;, defined by L&lt;Automake::VarDef&gt;).
Variables can be overridden, provided the new owner is not weaker
(C&lt;VAR_AUTOMAKE&gt; &lt; C&lt;VAR_CONFIGURE&gt; &lt; C&lt;VAR_MAKEFILE&gt;).

C&lt;$type&gt;: the type of the assignment (C&lt;''&gt; for C&lt;FOO = bar&gt;,
C&lt;':'&gt; for C&lt;FOO := bar&gt;, and C&lt;'+'&gt; for C&lt;'FOO += bar'&gt;).

C&lt;$cond&gt;: the C&lt;Condition&gt; in which C&lt;$var&gt; is being defined.

C&lt;$value&gt;: the value assigned to C&lt;$var&gt; in condition C&lt;$cond&gt;.

C&lt;$comment&gt;: any comment (C&lt;'# bla.'&gt;) associated with the assignment.
Comments from C&lt;+=&gt; assignments stack with comments from the last C&lt;=&gt;
assignment.

C&lt;$where&gt;: the C&lt;Location&gt; of the assignment.

C&lt;$pretty&gt;: whether C&lt;$value&gt; should be pretty printed (one of
C&lt;VAR_ASIS&gt;, C&lt;VAR_PRETTY&gt;, C&lt;VAR_SILENT&gt;, or C&lt;VAR_SORTED&gt;, defined
by by L&lt;Automake::VarDef&gt;).  C&lt;$pretty&gt; applies only to real
assignments.  I.e., it does not apply to a C&lt;+=&gt; assignment (except
when part of it is being done as a conditional C&lt;=&gt; assignment).

This function will all run any hook registered with the C&lt;hook&gt;
function.

=cut

sub define ($$$$$$$$)
{
  my ($var, $owner, $type, $cond, $value, $comment, $where, $pretty) = @_;

  prog_error "$cond is not a reference"
    unless ref $cond;

  prog_error "$where is not a reference"
    unless ref $where;

  prog_error "pretty argument missing"
    unless defined $pretty &amp;&amp; ($pretty == VAR_ASIS
			       || $pretty == VAR_PRETTY
			       || $pretty == VAR_SILENT
			       || $pretty == VAR_SORTED);

  error $where, "bad characters in variable name '$var'"
    if $var !~ /$_VARIABLE_PATTERN/o;

  # ':='-style assignments are not acknowledged by POSIX.  Moreover it
  # has multiple meanings.  In GNU make or BSD make it means "assign
  # with immediate expansion", while in OSF make it is used for
  # conditional assignments.
  msg ('portability', $where, "':='-style assignments are not portable")
    if $type eq ':';

  check_variable_expansions ($value, $where);

  # If there's a comment, make sure it is \n-terminated.
  if ($comment)
    {
      chomp $comment;
      $comment .= "\n";
    }
  else
    {
      $comment = '';
    }

  my $self = _cvar $var;

  my $def = $self-&gt;def ($cond);
  my $new_var = $def ? 0 : 1;

  # Additional checks for Automake definitions.
  if ($owner == VAR_AUTOMAKE &amp;&amp; ! $new_var)
    {
      # An Automake variable must be consistently defined with the same
      # sign by Automake.
      if ($def-&gt;type ne $type &amp;&amp; $def-&gt;owner == VAR_AUTOMAKE)
	{
	  error ($def-&gt;location,
		 "Automake variable '$var' was set with '"
		 . $def-&gt;type . "=' here ...", partial =&gt; 1);
	  error ($where, "... and is now set with '$type=' here.");
	  prog_error ("Automake variable assignments should be consistently\n"
		      . "defined with the same sign");
	}

      # If Automake tries to override a value specified by the user,
      # just don't let it do.
      if ($def-&gt;owner != VAR_AUTOMAKE)
	{
	  if (! exists $_silent_variable_override{$var})
	    {
	      my $condmsg = ($cond == TRUE
			     ? '' : (" in condition '" . $cond-&gt;human . "'"));
	      msg_cond_var ('override', $cond, $var,
			    "user variable '$var' defined here$condmsg ...",
			    partial =&gt; 1);
	      msg ('override', $where,
		   "... overrides Automake variable '$var' defined here");
	    }
	  verb ("refusing to override the user definition of:\n"
		. $self-&gt;dump ."with '" . $cond-&gt;human . "' =&gt; '$value'");
	  return;
	}
    }

  # Differentiate assignment types.

  # 1. append (+=) to a variable defined for current condition
  if ($type eq '+' &amp;&amp; ! $new_var)
    {
      $def-&gt;append ($value, $comment);
      $self-&gt;{'last-append'} = [];

      # Only increase owners.  A VAR_CONFIGURE variable augmented in a
      # Makefile.am becomes a VAR_MAKEFILE variable.
      $def-&gt;set_owner ($owner, $where-&gt;clone)
	if $owner &gt; $def-&gt;owner;
    }
  # 2. append (+=) to a variable defined for *another* condition
  elsif ($type eq '+' &amp;&amp; ! $self-&gt;conditions-&gt;false)
    {
      # * Generally, $cond is not TRUE.  For instance:
      #     FOO = foo
      #     if COND
      #       FOO += bar
      #     endif
      #   In this case, we declare an helper variable conditionally,
      #   and append it to FOO:
      #     FOO = foo $(am__append_1)
      #     @COND_TRUE@am__append_1 = bar
      #   Of course if FOO is defined under several conditions, we add
      #   $(am__append_1) to each definitions.
      #
      # * If $cond is TRUE, we don't need the helper variable.  E.g., in
      #     if COND1
      #       FOO = foo1
      #     else
      #       FOO = foo2
      #     endif
      #     FOO += bar
      #   we can add bar directly to all definition of FOO, and output
      #     @COND_TRUE@FOO = foo1 bar
      #     @COND_FALSE@FOO = foo2 bar

      my $lastappend = [];
      # Do we need an helper variable?
      if ($cond != TRUE)
        {
	  # Can we reuse the helper variable created for the previous
	  # append?  (We cannot reuse older helper variables because
	  # we must preserve the order of items appended to the
	  # variable.)
	  my $condstr = $cond-&gt;string;
	  my $key = "$var:$condstr";
	  my ($appendvar, $appendvarcond) = @{$self-&gt;{'last-append'}};
	  if ($appendvar &amp;&amp; $condstr eq $appendvarcond)
	    {
	      # Yes, let's simply append to it.
	      $var = $appendvar;
	      $owner = VAR_AUTOMAKE;
	      $self = var ($var);
	      $def = $self-&gt;rdef ($cond);
	      $new_var = 0;
	    }
	  else
	    {
	      # No, create it.
	      my $num = ++$_appendvar;
	      my $hvar = "am__append_$num";
	      $lastappend = [$hvar, $condstr];
	      &amp;define ($hvar, VAR_AUTOMAKE, '+',
		       $cond, $value, $comment, $where, $pretty);

	      # Now HVAR is to be added to VAR.
	      $comment = '';
	      $value = "\$($hvar)";
	    }
	}

      # Add VALUE to all definitions of SELF.
      foreach my $vcond ($self-&gt;conditions-&gt;conds)
        {
	  # We have a bit of error detection to do here.
	  # This:
	  #   if COND1
	  #     X = Y
	  #   endif
	  #   X += Z
	  # should be rejected because X is not defined for all conditions
	  # where '+=' applies.
	  my $undef_cond = $self-&gt;not_always_defined_in_cond ($cond);
	  if (! $undef_cond-&gt;false)
	    {
	      error ($where,
		     "cannot apply '+=' because '$var' is not defined "
		     . "in\nthe following conditions:\n  "
		     . join ("\n  ", map { $_-&gt;human } $undef_cond-&gt;conds)
		     . "\neither define '$var' in these conditions,"
		     . " or use\n'+=' in the same conditions as"
		     . " the definitions.");
	    }
	  else
	    {
	      &amp;define ($var, $owner, '+', $vcond, $value, $comment,
		       $where, $pretty);
	    }
	}
      $self-&gt;{'last-append'} = $lastappend;
    }
  # 3. first assignment (=, :=, or +=)
  else
    {
      # There must be no previous value unless the user is redefining
      # an Automake variable or an AC_SUBST variable for an existing
      # condition.
      _check_ambiguous_condition ($self, $cond, $where)
	unless (!$new_var
		&amp;&amp; (($def-&gt;owner == VAR_AUTOMAKE &amp;&amp; $owner != VAR_AUTOMAKE)
		    || $def-&gt;owner == VAR_CONFIGURE));

      # Never decrease an owner.
      $owner = $def-&gt;owner
	if ! $new_var &amp;&amp; $owner &lt; $def-&gt;owner;

      # Assignments to a macro set its location.  We don't adjust
      # locations for '+='.  Ideally I suppose we would associate
      # line numbers with random bits of text.
      $def = new Automake::VarDef ($var, $value, $comment, $where-&gt;clone,
				   $type, $owner, $pretty);
      $self-&gt;set ($cond, $def);
      push @_var_order, $var;
    }

  # Call any defined hook.  This helps to update some internal state
  # *while* parsing the file.  For instance the handling of SUFFIXES
  # requires this (see var_SUFFIXES_trigger).
  &amp;{$_hooks{$var}}($type, $value) if exists $_hooks{$var};
}

=item C&lt;variable_delete ($varname, [@conds])&gt;

Forget about C&lt;$varname&gt; under the conditions C&lt;@conds&gt;, or completely
if C&lt;@conds&gt; is empty.

=cut

sub variable_delete ($@)
{
  my ($var, @conds) = @_;

  if (!@conds)
    {
      delete $_variable_dict{$var};
    }
  else
    {
      for my $cond (@conds)
	{
	  delete $_variable_dict{$var}{'defs'}{$cond};
	}
    }
  if ($var =~ /_([[:alnum:]]+)$/)
    {
      delete $_primary_dict{$1}{$var};
    }
}

=item C&lt;$str = variables_dump&gt;

Return a string describing all we know about all variables.
For debugging.

=cut

sub variables_dump ()
{
  my $text = "all variables:\n{\n";
  foreach my $var (variables())
    {
      $text .= $var-&gt;dump;
    }
  $text .= "}\n";
  return $text;
}


=item C&lt;$var = set_seen ($varname)&gt;

=item C&lt;$var = $var-E&lt;gt&gt;set_seen&gt;

Mark all definitions of this variable as examined, if the variable
exists.  See L&lt;Automake::VarDef::set_seen&gt;.

Return the C&lt;Variable&gt; object if the variable exists, or 0
otherwise (i.e., as the C&lt;var&gt; function).

=cut

sub set_seen ($)
{
  my ($self) = @_;
  $self = ref $self ? $self : var $self;

  return 0 unless $self;

  for my $c ($self-&gt;conditions-&gt;conds)
    {
      $self-&gt;rdef ($c)-&gt;set_seen;
    }

  return $self;
}


=item C&lt;$count = require_variables ($where, $reason, $cond, @variables)&gt;

Make sure that each supplied variable is defined in C&lt;$cond&gt;.
Otherwise, issue a warning showing C&lt;$reason&gt; (C&lt;$reason&gt; should be
the reason why these variables are required, for instance C&lt;'option foo
used'&gt;).  If we know which macro can define this variable, hint the
user.  Return the number of undefined variables.

=cut

sub require_variables ($$$@)
{
  my ($where, $reason, $cond, @vars) = @_;
  my $res = 0;
  $reason .= ' but ' unless $reason eq '';

  $configure_ac = find_configure_ac
    unless defined $configure_ac;

 VARIABLE:
  foreach my $var (@vars)
    {
      # Nothing to do if the variable exists.
      next VARIABLE
	if vardef ($var, $cond);

      my $text = "$reason'$var' is undefined\n";
      my $v = var $var;
      if ($v)
	{
	  my $undef_cond = $v-&gt;not_always_defined_in_cond ($cond);
	  next VARIABLE
	    if $undef_cond-&gt;false;
	  $text .= ("in the following conditions:\n  "
		    . join ("\n  ", map { $_-&gt;human } $undef_cond-&gt;conds)
		    . "\n");
	}

      ++$res;

      if (exists $_am_macro_for_var{$var})
	{
	  my $mac = $_am_macro_for_var{$var};
	  $text .= "  The usual way to define '$var' is to add "
	    . "'$mac'\n  to '$configure_ac' and run 'aclocal' and "
	    . "'autoconf' again.";
	  # aclocal will not warn about undefined macros unless it
	  # starts with AM_.
	  $text .= "\n  If '$mac' is in '$configure_ac', make sure\n"
	    . "  its definition is in aclocal's search path."
	    unless $mac =~ /^AM_/;
	}
      elsif (exists $_ac_macro_for_var{$var})
	{
	  $text .= "  The usual way to define '$var' is to add "
	    . "'$_ac_macro_for_var{$var}'\n  to '$configure_ac' and "
	    . "run 'autoconf' again.";
	}

      error $where, $text, uniq_scope =&gt; US_GLOBAL;
    }
  return $res;
}

=item C&lt;$count = $var-&gt;requires_variables ($reason, @variables)&gt;

Same as C&lt;require_variables&gt;, but a method of Automake::Variable.
C&lt;@variables&gt; should be defined in the same conditions as C&lt;$var&gt; is
defined.

=cut

sub requires_variables ($$@)
{
  my ($var, $reason, @args) = @_;
  my $res = 0;
  for my $cond ($var-&gt;conditions-&gt;conds)
    {
      $res += require_variables ($var-&gt;rdef ($cond)-&gt;location, $reason,
				 $cond, @args);
    }
  return $res;
}


=item C&lt;variable_value ($var)&gt;

Get the C&lt;TRUE&gt; value of a variable, warn if the variable is
conditionally defined.  C&lt;$var&gt; can be either a variable name
or a C&lt;Automake::Variable&gt; instance (this allows calls such
as C&lt;$var-E&lt;gt&gt;variable_value&gt;).

=cut

sub variable_value ($)
{
    my ($var) = @_;
    my $v = ref ($var) ? $var : var ($var);
    return () unless $v;
    $v-&gt;check_defined_unconditionally;
    my $d = $v-&gt;def (TRUE);
    return $d ? $d-&gt;value : "";
}

=item C&lt;$str = output_variables&gt;

Format definitions for all variables.

=cut

sub output_variables ()
{
  my $res = '';
  # We output variables it in the same order in which they were
  # defined (skipping duplicates).
  my @vars = uniq @_var_order;

  # Output all the Automake variables.  If the user changed one,
  # then it is now marked as VAR_CONFIGURE or VAR_MAKEFILE.
  foreach my $var (@vars)
    {
      my $v = rvar $var;
      foreach my $cond ($v-&gt;conditions-&gt;conds)
	{
	  $res .= $v-&gt;output ($cond)
	    if $v-&gt;rdef ($cond)-&gt;owner == VAR_AUTOMAKE;
	}
    }

  # Now dump the user variables that were defined.
  foreach my $var (@vars)
    {
      my $v = rvar $var;
      foreach my $cond ($v-&gt;conditions-&gt;conds)
	{
	  $res .= $v-&gt;output ($cond)
	    if $v-&gt;rdef ($cond)-&gt;owner != VAR_AUTOMAKE;
	}
    }
  return $res;
}

=item C&lt;$var-E&lt;gt&gt;traverse_recursively (&amp;fun_item, &amp;fun_collect, [cond_filter =E&lt;gt&gt; $cond_filter], [inner_expand =E&lt;gt&gt; 1], [skip_ac_subst =E&lt;gt&gt; 1])&gt;

Split the value of the Automake::Variable C&lt;$var&gt; on space, and
traverse its components recursively.

If C&lt;$cond_filter&gt; is an C&lt;Automake::Condition&gt;, process any
conditions which are true when C&lt;$cond_filter&gt; is true.  Otherwise,
process all conditions.

We distinguish two kinds of items in the content of C&lt;$var&gt;.
Terms that look like C&lt;$(foo)&gt; or C&lt;${foo}&gt; are subvariables
and cause recursion.  Other terms are assumed to be filenames.

Each time a filename is encountered, C&lt;&amp;fun_item&gt; is called with the
following arguments:

  ($var,        -- the Automake::Variable we are currently
                   traversing
   $val,        -- the item (i.e., filename) to process
   $cond,       -- the Condition for the $var definition we are
                   examining (ignoring the recursion context)
   $full_cond)  -- the full Condition, taking into account
                   conditions inherited from parent variables
                   during recursion

If C&lt;inner_expand&gt; is set, variable references occurring in filename
(as in C&lt;$(BASE).ext&gt;) are expanded before the filename is passed to
C&lt;&amp;fun_item&gt;.

If C&lt;skip_ac_subst&gt; is set, Autoconf @substitutions@ will be skipped,
i.e., C&lt;&amp;fun_item&gt; will never be called for them.

C&lt;&amp;fun_item&gt; may return a list of items, they will be passed to
C&lt;&amp;fun_store&gt; later on.  Define C&lt;&amp;fun_item&gt; or @&lt;&amp;fun_store&gt; as
C&lt;undef&gt; when they serve no purpose.

Once all items of a variable have been processed, the result (of the
calls to C&lt;&amp;fun_items&gt;, or of recursive traversals of subvariables)
are passed to C&lt;&amp;fun_collect&gt;.  C&lt;&amp;fun_collect&gt; receives three
arguments:

  ($var,         -- the variable being traversed
   $parent_cond, -- the Condition inherited from parent
                    variables during recursion
   @condlist)    -- a list of [$cond, @results] pairs
                    where each $cond appear only once, and @result
                    are all the results for this condition.

Typically you should do C&lt;$cond-&gt;merge ($parent_cond)&gt; to recompute
the C&lt;$full_cond&gt; associated to C&lt;@result&gt;.  C&lt;&amp;fun_collect&gt; may
return a list of items, that will be used as the result of
C&lt;Automake::Variable::traverse_recursively&gt; (the top-level, or its
recursive calls).

=cut

# Contains a stack of 'from' and 'to' parts of variable
# substitutions currently in force.
my @_substfroms;
my @_substtos;
sub traverse_recursively ($&amp;&amp;;%)
{
  ++$_traversal;
  @_substfroms = ();
  @_substtos = ();
  my ($var, $fun_item, $fun_collect, %options) = @_;
  my $cond_filter = $options{'cond_filter'};
  my $inner_expand = $options{'inner_expand'};
  my $skip_ac_subst = $options{'skip_ac_subst'};
  return $var-&gt;_do_recursive_traversal ($var,
					$fun_item, $fun_collect,
					$cond_filter, TRUE, $inner_expand,
					$skip_ac_subst)
}

# The guts of Automake::Variable::traverse_recursively.
sub _do_recursive_traversal ($$&amp;&amp;$$$$)
{
  my ($var, $parent, $fun_item, $fun_collect, $cond_filter, $parent_cond,
      $inner_expand, $skip_ac_subst) = @_;

  $var-&gt;set_seen;

  if ($var-&gt;{'scanned'} == $_traversal)
    {
      err_var $var, "variable '" . $var-&gt;name() . "' recursively defined";
      return ();
    }
  $var-&gt;{'scanned'} = $_traversal;

  my @allresults = ();
  my $cond_once = 0;
  foreach my $cond ($var-&gt;conditions-&gt;conds)
    {
      if (ref $cond_filter)
	{
	  # Ignore conditions that don't match $cond_filter.
	  next if ! $cond-&gt;true_when ($cond_filter);
	  # If we found out several definitions of $var
	  # match $cond_filter then we are in trouble.
	  # Tell the user we don't support this.
	  $var-&gt;check_defined_unconditionally ($parent, $parent_cond)
	    if $cond_once;
	  $cond_once = 1;
	}
      my @result = ();
      my $full_cond = $cond-&gt;merge ($parent_cond);

      my @to_process = $var-&gt;value_as_list ($cond, $parent, $parent_cond);
      while (@to_process)
	{
	  my $val = shift @to_process;
	  # If $val is a variable (i.e. ${foo} or $(bar), not a filename),
	  # handle the sub variable recursively.
	  # (Backslashes before '}' and ')' within brackets are here to
	  # please Emacs's indentation.)
	  if ($val =~ /^\$\{([^\}]*)\}$/ || $val =~ /^\$\(([^\)]*)\)$/)
	    {
	      my $subvarname = $1;

	      # If the user uses a losing variable name, just ignore it.
	      # This isn't ideal, but people have requested it.
	      next if ($subvarname =~ /\@.*\@/);

	      # See if the variable is actually a substitution reference
	      my ($from, $to);
              # This handles substitution references like ${foo:.a=.b}.
	      if ($subvarname =~ /^([^:]*):([^=]*)=(.*)$/o)
		{
		  $subvarname = $1;
		  $to = $3;
		  $from = quotemeta $2;
		}

	      my $subvar = var ($subvarname);
	      # Don't recurse into undefined variables.
	      next unless $subvar;

	      push @_substfroms, $from;
	      push @_substtos, $to;

	      my @res = $subvar-&gt;_do_recursive_traversal ($parent,
							  $fun_item,
							  $fun_collect,
							  $cond_filter,
							  $full_cond,
							  $inner_expand,
							  $skip_ac_subst);
	      push (@result, @res);

	      pop @_substfroms;
	      pop @_substtos;

	      next;
	    }
	  # Try to expand variable references inside filenames such as
	  # '$(NAME).txt'.  We do not handle ':.foo=.bar'
	  # substitutions, but it would make little sense to use this
	  # here anyway.
	  elsif ($inner_expand
		 &amp;&amp; ($val =~ /\$\{([^\}]*)\}/ || $val =~ /\$\(([^\)]*)\)/))
	    {
	      my $subvarname = $1;
	      my $subvar = var $subvarname;
	      if ($subvar)
		{
		  # Replace the reference by its value, and reschedule
		  # for expansion.
		  foreach my $c ($subvar-&gt;conditions-&gt;conds)
		    {
		      if (ref $cond_filter)
			{
			  # Ignore conditions that don't match $cond_filter.
			  next if ! $c-&gt;true_when ($cond_filter);
			  # If we found out several definitions of $var
			  # match $cond_filter then we are in trouble.
			  # Tell the user we don't support this.
			  $subvar-&gt;check_defined_unconditionally ($var,
								  $full_cond)
			    if $cond_once;
			  $cond_once = 1;
			}
		      my $subval = $subvar-&gt;rdef ($c)-&gt;value;
		      $val =~ s/\$\{$subvarname\}/$subval/g;
		      $val =~ s/\$\($subvarname\)/$subval/g;
		      unshift @to_process, split (' ', $val);
		    }
		  next;
		}
	      # We do not know any variable with this name.  Fall through
	      # to filename processing.
	    }
	  elsif ($skip_ac_subst &amp;&amp; $val =~ /^\@.+\@$/)
	    {
	      next;
	    }

	  if ($fun_item) # $var is a filename we must process
	    {
	      my $substnum=$#_substfroms;
	      while ($substnum &gt;= 0)
		{
		  $val =~ s/$_substfroms[$substnum]$/$_substtos[$substnum]/
		    if defined $_substfroms[$substnum];
		  $substnum -= 1;
		}

	      # Make sure you update the doc of
	      # Automake::Variable::traverse_recursively
	      # if you change the prototype of &amp;fun_item.
	      my @transformed = &amp;$fun_item ($var, $val, $cond, $full_cond);
	      push (@result, @transformed);
	    }
	}
      push (@allresults, [$cond, @result]) if @result;
    }

  # We only care about _recursive_ variable definitions.  The user
  # is free to use the same variable several times in the same definition.
  $var-&gt;{'scanned'} = -1;

  return ()
    unless $fun_collect;
  # Make sure you update the doc of Automake::Variable::traverse_recursively
  # if you change the prototype of &amp;fun_collect.
  return &amp;$fun_collect ($var, $parent_cond, @allresults);
}

# _hash_varname ($VAR)
# --------------------
# Compute the key associated $VAR in %_gen_varname.
# See _gen_varname() below.
sub _hash_varname ($)
{
  my ($var) = @_;
  my $key = '';
  foreach my $cond ($var-&gt;conditions-&gt;conds)
    {
      my @values = $var-&gt;value_as_list ($cond);
      $key .= "($cond)@values";
    }
  return $key;
}

# _hash_values (@VALUES)
# ----------------------
# Hash @VALUES for %_gen_varname.  @VALUES should be a list
# of pairs: ([$cond, @values], [$cond, @values], ...).
# See _gen_varname() below.
sub _hash_values (@)
{
  my $key = '';
  foreach my $pair (@_)
    {
      my ($cond, @values) = @$pair;
      $key .= "($cond)@values";
    }
  return $key;
}
# ($VARNAME, $GENERATED)
# _gen_varname ($BASE, @DEFINITIONS)
# ----------------------------------
# Return a variable name starting with $BASE, that will be
# used to store definitions @DEFINITIONS.
# @DEFINITIONS is a list of pair [$COND, @OBJECTS].
#
# If we already have a $BASE-variable containing @DEFINITIONS, reuse
# it and set $GENERATED to 0.  Otherwise construct a new name and set
# $GENERATED to 1.
#
# This way, we avoid combinatorial explosion of the generated
# variables.  Especially, in a Makefile such as:
#
# | if FOO1
# | A1=1
# | endif
# |
# | if FOO2
# | A2=2
# | endif
# |
# | ...
# |
# | if FOON
# | AN=N
# | endif
# |
# | B=$(A1) $(A2) ... $(AN)
# |
# | c_SOURCES=$(B)
# | d_SOURCES=$(B)
#
# The generated c_OBJECTS and d_OBJECTS will share the same variable
# definitions.
#
# This setup can be the case of a testsuite containing lots (&gt;100) of
# small C programs, all testing the same set of source files.
sub _gen_varname ($@)
{
  my $base = shift;
  my $key = _hash_values @_;

  return ($_gen_varname{$base}{$key}, 0)
    if exists $_gen_varname{$base}{$key};

  my $num = 1 + ($_gen_varname_n{$base} || 0);
  $_gen_varname_n{$base} = $num;
  my $name = "${base}_${num}";
  $_gen_varname{$base}{$key} = $name;

  return ($name, 1);
}

=item C&lt;$resvar = transform_variable_recursively ($var, $resvar, $base, $nodefine, $where, &amp;fun_item, [%options])&gt;

=item C&lt;$resvar = $var-E&lt;gt&gt;transform_variable_recursively ($resvar, $base, $nodefine, $where, &amp;fun_item, [%options])&gt;

Traverse C&lt;$var&gt; recursively, and create a C&lt;$resvar&gt; variable in
which each filename in C&lt;$var&gt; have been transformed using
C&lt;&amp;fun_item&gt;.  (C&lt;$var&gt; may be a variable name in the first syntax.
It must be an C&lt;Automake::Variable&gt; otherwise.)

Helper variables (corresponding to sub-variables of C&lt;$var&gt;) are
created as needed, using C&lt;$base&gt; as prefix.

Arguments are:
  $var       source variable to traverse
  $resvar    resulting variable to define
  $base      prefix to use when naming subvariables of $resvar
  $nodefine  if true, traverse $var but do not define any variable
             (this assumes &amp;fun_item has some useful side-effect)
  $where     context into which variable definitions are done
  &amp;fun_item  a transformation function -- see the documentation
             of &amp;fun_item in Automake::Variable::traverse_recursively.

This returns the string C&lt;"\$($RESVAR)"&gt;.

C&lt;%options&gt; is a list of options to pass to
C&lt;Variable::traverse_recursively&gt; (see this method).

=cut

sub transform_variable_recursively ($$$$$&amp;;%)
{
  my ($var, $resvar, $base, $nodefine, $where, $fun_item, %options) = @_;

  $var = ref $var ? $var : rvar $var;

  my $res = $var-&gt;traverse_recursively
    ($fun_item,
     # The code that defines the variable holding the result
     # of the recursive transformation of a subvariable.
     sub {
       my ($subvar, $parent_cond, @allresults) = @_;
       # If no definition is required, return anything: the result is
       # not expected to be used, only the side effect of $fun_item
       # should matter.
       return 'report-me' if $nodefine;
       # Cache $subvar, so that we reuse it if @allresults is the same.
       my $key = _hash_varname $subvar;
       $_gen_varname{$base}{$key} = $subvar-&gt;name;

       # Find a name for the variable, unless this is the top-variable
       # for which we want to use $resvar.
       my ($varname, $generated) =
	 ($var != $subvar) ? _gen_varname ($base, @allresults) : ($resvar, 1);

       # Define the variable if we are not reusing a previously
       # defined variable.  At the top-level, we can also avoid redefining
       # the variable if it already contains the same values.
       if ($generated
	   &amp;&amp; !($varname eq $var-&gt;name &amp;&amp; $key eq _hash_values @allresults))
	 {
	   # If the new variable is the source variable, we assume
	   # we are trying to override a user variable.  Delete
	   # the old variable first.
	   variable_delete ($varname) if $varname eq $var-&gt;name;
	   # Define an empty variable in condition TRUE if there is no
	   # result.
	   @allresults = ([TRUE, '']) unless @allresults;
	   # Define the rewritten variable in all conditions not
	   # already covered by user definitions.
	   foreach my $pair (@allresults)
	     {
	       my ($cond, @result) = @$pair;
	       my $var = var $varname;
	       my @conds = ($var
			    ? $var-&gt;not_always_defined_in_cond ($cond)-&gt;conds
			    : $cond);

	       foreach (@conds)
		 {
		   define ($varname, VAR_AUTOMAKE, '', $_, "@result",
			   '', $where, VAR_PRETTY);
		 }
	     }
	 }
       set_seen $varname;
       return "\$($varname)";
     },
     %options);
  return $res;
}


=back

=head1 SEE ALSO

L&lt;Automake::VarDef&gt;, L&lt;Automake::Condition&gt;,
L&lt;Automake::DisjConditions&gt;, L&lt;Automake::Location&gt;.

=cut

1;
</pre></body></html>