use Automake::XFile;
use Automake::Channels;
use Automake::Location;
+use Automake::Conditional;
use File::Basename;
+use Tie::RefHash;
use Carp;
## ----------- ##
# We can't always associate a location to a variable or a rule,
# when its defined by Automake. We use INTERNAL in this case.
use constant INTERNAL => new Automake::Location;
+
+# The TRUE and FALSE conditionals.
+use constant TRUE => new Automake::Conditional;
+use constant FALSE => new Automake::Conditional "FALSE";
\f
## ---------------------------------- ##
# - $var_type{$VAR}{$COND} is how it has been defined (`', `+', or `:'),
# - $var_owner{$VAR}{$COND} tells who owns the variable (VAR_AUTOMAKE,
# VAR_CONFIGURE, or VAR_MAKEFILE).
-my %var_value;
-my %var_location;
-my %var_comment;
-my %var_type;
-my %var_owner;
+my %var_value; tie %var_value, 'Tie::RefHash::Nestable';
+my %var_location; tie %var_location, 'Tie::RefHash::Nestable';
+my %var_comment; tie %var_comment, 'Tie::RefHash::Nestable';
+my %var_type; tie %var_type, 'Tie::RefHash::Nestable';
+my %var_owner; tie %var_owner, 'Tie::RefHash::Nestable';
# Possible values for var_owner. Defined so that the owner of
# a variable can only be increased (e.g Automake should not
# override a configure or Makefile variable).
# %contents. $targets{TARGET}{COND} is the location of the definition
# of TARGET for condition COND. TARGETs should not include
# a trailing $(EXEEXT), we record this in %target_name.
-my %targets;
+my %targets; tie %targets, 'Tie::RefHash::Nestable';
# $target_source{TARGET}{COND} is the filename where TARGET
# were defined for condition COND. Note this must be a
# filename, *without* any line number.
-my %target_source;
+my %target_source; tie %target_source, 'Tie::RefHash::Nestable';
# $target_name{TARGET}{COND} is the real name of TARGET (in condition COND).
# The real name is often TARGET or TARGET$(EXEEXT), and TARGET never
# contain $(EXEEXT)
-my %target_name;
+my %target_name; tie %target_name, 'Tie::RefHash::Nestable';
# $target_owner{TARGET}{COND} the owner of TARGET in condition COND.
-my %target_owner;
+my %target_owner; tie %target_owner, 'Tie::RefHash::Nestable';
use constant TARGET_AUTOMAKE => 0; # Target defined by Automake.
use constant TARGET_USER => 1; # Target defined in the user's Makefile.am.
if (exists $var_owner{$var})
{
prog_error "\$var_owner{$var}{TRUE} doesn't exist"
- unless exists $var_owner{$var}{'TRUE'};
+ unless exists $var_owner{$var}{&TRUE};
reject_var $var, "`$var' should not be defined"
- if $var_owner{$var}{'TRUE'} != VAR_AUTOMAKE;
+ if $var_owner{$var}{&TRUE} != VAR_AUTOMAKE;
}
}
# Re-init SOURCES. FIXME: other code shouldn't depend on this
# (but currently does).
- macro_define ('SOURCES', VAR_AUTOMAKE, '', 'TRUE', "@sources", INTERNAL);
- define_pretty_variable ('DIST_SOURCES', '', INTERNAL, @dist_sources);
+ macro_define ('SOURCES', VAR_AUTOMAKE, '', TRUE, "@sources", INTERNAL);
+ define_pretty_variable ('DIST_SOURCES', TRUE, INTERNAL, @dist_sources);
&handle_multilib;
&handle_texinfo;
# FIXME: We should disallow conditional deffinitions of AUTOMAKE_OPTIONS.
my $where = ($config ?
$seen_init_automake :
- $var_location{'AUTOMAKE_OPTIONS'}{'TRUE'});
+ $var_location{'AUTOMAKE_OPTIONS'}{&TRUE});
foreach (@list)
{
if (variable_defined ('AUTOMAKE_OPTIONS'))
{
- if (&process_option_list (0, &variable_value_as_list_recursive ('AUTOMAKE_OPTIONS', '')))
+ if (&process_option_list (0, &variable_value_as_list_recursive ('AUTOMAKE_OPTIONS', TRUE)))
{
return 1;
}
# We define this as a conditional variable because BSD
# make can't handle backslashes for continuing comments on
# the following line.
- define_pretty_variable ('DEP_FILES', 'AMDEP_TRUE', INTERNAL,
- @deplist);
+ define_pretty_variable ('DEP_FILES',
+ new Automake::Conditional ('AMDEP_TRUE'),
+ INTERNAL, @deplist);
# Generate each `include' individually. Irix 6 make will
# not properly include several files resulting from a
if ($lang->link);
require_variables ("$am_file.am", $lang->Name . " source seen",
- 'TRUE', @{$lang->config_vars});
+ TRUE, @{$lang->config_vars});
# Call the finisher.
$lang->finish;
if (defined $vars_scanned{$var})
{
err_var $var, "variable `$var' recursively defined";
- return "";
+ return ("", $objvar || "ERROR");
}
$vars_scanned{$var} = 1;
# and add this variable to DIST_SOURCES.
my $distvar = "$var";
my @conds = variable_conditions_recursive ($var);
- if (@conds && $conds[0] ne 'TRUE')
+ if (@conds && $conds[0] != TRUE)
{
$distvar = "am__${var}_DIST";
my @files =
uniq (variable_value_as_list_recursive ($var, 'all'));
- define_pretty_variable ($distvar, '', $where, @files);
+ define_pretty_variable ($distvar, TRUE, $where, @files);
}
push @dist_sources, "\$($distvar)"
}
$one_file, $obj,
"$unxformed.c");
$linker ||= &resolve_linker (%linkers_used);
- define_pretty_variable ($one_file . '_OBJECTS', '', $where, @result)
+ define_pretty_variable ($one_file . '_OBJECTS', TRUE, $where, @result);
}
else
{
grep ($_ = '$(' . $_ . $one_file . '_OBJECTS)', @keys);
- define_pretty_variable ($one_file . '_OBJECTS', '', $where, @keys);
+ define_pretty_variable ($one_file . '_OBJECTS', TRUE, $where, @keys);
}
# If we want to use `LINK' we must make sure it is defined.
{
# Only require ansi2knr files if they should appear in
# this directory.
- require_file_with_macro ('TRUE', 'AUTOMAKE_OPTIONS', FOREIGN,
+ require_file_with_macro (TRUE, 'AUTOMAKE_OPTIONS', FOREIGN,
'ansi2knr.c', 'ansi2knr.1');
# ansi2knr needs to be built before subdirs, so unshift it.
return unless variable_defined ('LIBTOOL');
# Libtool requires some files, but only at top level.
- require_conf_file_with_macro ('TRUE', 'LIBTOOL', FOREIGN, @libtool_files)
+ require_conf_file_with_macro (TRUE, 'LIBTOOL', FOREIGN, @libtool_files)
if $relative_dir eq '.';
my @libtool_rms;
# This is ugly, but it is our historical practice.
if ($config_aux_dir_set_in_configure_in)
{
- require_conf_file_with_macro ('TRUE', 'info_TEXINFOS', FOREIGN,
+ require_conf_file_with_macro (TRUE, 'info_TEXINFOS', FOREIGN,
'mdate-sh');
}
else
{
- require_file_with_macro ('TRUE', 'info_TEXINFOS',
+ require_file_with_macro (TRUE, 'info_TEXINFOS',
FOREIGN, 'mdate-sh');
}
{
if ($need_texi_file > 1)
{
- require_conf_file_with_macro ('TRUE', 'info_TEXINFOS', FOREIGN,
+ require_conf_file_with_macro (TRUE, 'info_TEXINFOS', FOREIGN,
'texinfo.tex');
}
else
{
- require_file_with_macro ('TRUE', 'info_TEXINFOS', FOREIGN,
+ require_file_with_macro (TRUE, 'info_TEXINFOS', FOREIGN,
'texinfo.tex');
}
}
# Files to distributed. Don't use &variable_value_as_list_recursive
# as it recursively expands `$(dist_pkgdata_DATA)' etc.
check_variable_defined_unconditionally ('DIST_COMMON');
- my @dist_common = split (' ', variable_value ('DIST_COMMON', 'TRUE'));
+ my @dist_common = split (' ', variable_value ('DIST_COMMON', TRUE));
@dist_common = uniq (sort for_dist_common (@dist_common));
pretty_print ('DIST_COMMON = ', "\t", @dist_common);
# appropriate condition. This is meaningful if the nature of
# the distribution should depend upon the configure options
# used.
- foreach (&variable_value_as_list_recursive ('EXTRA_DIST', ''))
+ foreach (&variable_value_as_list_recursive ('EXTRA_DIST', 'all'))
{
next if /^\@.*\@$/;
next unless s,/+[^/]+$,,;
if (! variable_defined ('DIST_SUBDIRS'))
{
define_pretty_variable
- ('DIST_SUBDIRS', '', INTERNAL,
+ ('DIST_SUBDIRS', TRUE, INTERNAL,
uniq (&variable_value_as_list_recursive ('SUBDIRS', 'all')));
}
}
$dist_subdir_name = 'SUBDIRS';
# We always define this because that is what `distclean'
# wants.
- define_pretty_variable ('DIST_SUBDIRS', '', INTERNAL,
+ define_pretty_variable ('DIST_SUBDIRS', TRUE, INTERNAL,
'$(SUBDIRS)');
}
}
$output_rules .= &file_contents ('subdirs', new Automake::Location);
- variable_pretty_output ('RECURSIVE_TARGETS', 'TRUE');
+ variable_pretty_output ('RECURSIVE_TARGETS', TRUE);
}
# Scan all -I directories for m4 files. These are our
# dependencies.
my $examine_next = 0;
- foreach my $amdir (&variable_value_as_list_recursive ('ACLOCAL_AMFLAGS', ''))
+ foreach my $amdir (&variable_value_as_list_recursive ('ACLOCAL_AMFLAGS', TRUE))
{
if ($examine_next)
{
}
# These files get removed by "make clean".
- define_pretty_variable ('CONFIG_CLEAN_FILES', '', INTERNAL,
+ define_pretty_variable ('CONFIG_CLEAN_FILES', TRUE, INTERNAL,
@actual_other_files);
}
# actual suffixes, and not $(SUFFIXES). Some versions of make
# do not like variable substitutions on the .SUFFIXES line.
my @user_suffixes = (variable_defined ('SUFFIXES')
- ? &variable_value_as_list_recursive ('SUFFIXES', '')
+ ? &variable_value_as_list_recursive ('SUFFIXES',
+ 'all')
: ());
my %suffixes = map { $_ => 1 } @suffixes;
# Generate .elc files.
my @elcfiles = map { $_->[1] . 'c' } @elfiles;
- define_pretty_variable ('ELCFILES', '', INTERNAL, @elcfiles);
+ define_pretty_variable ('ELCFILES', TRUE, INTERNAL, @elcfiles);
push (@all, '$(ELCFILES)');
- require_variables ($elfiles[0][0], "Emacs Lisp sources seen", 'TRUE',
+ require_variables ($elfiles[0][0], "Emacs Lisp sources seen", TRUE,
'EMACS', 'lispdir');
require_conf_file ($elfiles[0][0], FOREIGN, 'elisp-comp');
&define_variable ('elisp_comp', $config_aux_dir . '/elisp-comp', INTERNAL);
'noinst');
return if ! @pyfiles;
- require_variables ($pyfiles[0][0], "Python sources seen", 'TRUE', 'PYTHON');
+ require_variables ($pyfiles[0][0], "Python sources seen", TRUE, 'PYTHON');
require_conf_file ($pyfiles[0][0], FOREIGN, 'py-compile');
&define_variable ('py_compile', $config_aux_dir . '/py-compile', INTERNAL);
}
{
# This means we have an alpha release. See
# GNITS_VERSION_PATTERN for details.
- require_file_with_macro ('TRUE', 'AUTOMAKE_OPTIONS',
+ require_file_with_macro (TRUE, 'AUTOMAKE_OPTIONS',
FOREIGN, 'README-alpha');
}
}
if -f $config_aux_path[0] . '/install.sh';
# Preserve dist_common for later.
- $configure_dist_common = variable_value ('DIST_COMMON', 'TRUE') || '';
+ $configure_dist_common = variable_value ('DIST_COMMON', TRUE) || '';
}
################################################################
################################################################
-# $STRING
-# &conditional_string(@COND-STACK)
-# --------------------------------
-# Build a string which denotes the conditional in @COND-STACK. Some
-# simplifications are done: `TRUE' entries are elided, and any `FALSE'
-# entry results in a return of `FALSE'.
-sub conditional_string
-{
- my (@stack) = @_;
-
- if (grep (/^FALSE$/, @stack))
- {
- return 'FALSE';
- }
- else
- {
- return join (' ', uniq sort grep (!/^TRUE$/, @stack));
- }
-}
-
-
-# $BOOLEAN
-# &conditional_true_when ($COND, $WHEN)
-# -------------------------------------
-# 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.
-# For instance with $COND = `BAR FOO', and $WHEN = `BAR BAZ FOO',
-# obviously return 1, and 0 when, for instance, $WHEN = `FOO'.
-sub conditional_true_when ($$)
-{
- my ($cond, $when) = @_;
-
- # Make a hash holding all the values from $WHEN.
- my %cond_vals = map { $_ => 1 } split (' ', $when);
-
- # Nothing is true when FALSE (not even FALSE itself, but it
- # shouldn't hurt if you decide to change that).
- return 0 if exists $cond_vals{'FALSE'};
-
- # Check each component of $cond, which looks `COND1 COND2'.
- foreach my $comp (split (' ', $cond))
- {
- # TRUE is always true.
- next if $comp eq 'TRUE';
- return 0 if ! defined $cond_vals{$comp};
- }
-
- return 1;
-}
-
-
-# $BOOLEAN
-# &conditional_is_redundant ($COND, @WHENS)
-# ----------------------------------------
-# Determine whether $COND is redundant with respect to @WHENS.
-#
-# Returns true if $COND is true for any of the conditions in @WHENS.
-#
-# If there are no @WHENS, then behave as if @WHENS contained a single empty
-# condition.
-sub conditional_is_redundant ($@)
-{
- my ($cond, @whens) = @_;
-
- @whens = ("") if @whens == 0;
-
- foreach my $when (@whens)
- {
- return 1 if conditional_true_when ($cond, $when);
- }
- return 0;
-}
-
-
-# $BOOLEAN
-# &conditional_implies_any ($COND, @CONDS)
-# ----------------------------------------
-# Returns true iff $COND implies any of the conditions in @CONDS.
-sub conditional_implies_any ($@)
-{
- my ($cond, @conds) = @_;
-
- @conds = ("") if @conds == 0;
-
- foreach my $c (@conds)
- {
- return 1 if conditional_true_when ($c, $cond);
- }
- return 0;
-}
-
-
# $NEGATION
# condition_negate ($COND)
# ------------------------
sub by_condition
{
# Be careful we might be comparing `' or `#'.
- $a =~ /^(.*)_(TRUE|FALSE)$/;
+ $a->string =~ /^(.*)_(TRUE|FALSE)$/;
my ($aname, $abool) = ($1 || '', $2 || '');
- $b =~ /^(.*)_(TRUE|FALSE)$/;
+ $b->string =~ /^(.*)_(TRUE|FALSE)$/;
my ($bname, $bbool) = ($1 || '', $2 || '');
return ($aname cmp $bname
# Don't bother with IFs, given that TRUE is after FALSE
}
-# &make_condition (@CONDITIONS)
-# -----------------------------
-# Transform a list of conditions (themselves can be an internal list
-# of conditions, e.g., @CONDITIONS = ('cond1 cond2', 'cond3')) into a
-# Make conditional (a pattern for AC_SUBST).
-# Correctly returns the empty string when there are no conditions.
-sub make_condition
-{
- my $res = conditional_string (@_);
-
- # There are no conditions.
- if ($res eq '')
- {
- # Nothing to do.
- }
- # It's impossible.
- elsif ($res eq 'FALSE')
- {
- $res = '#';
- }
- # Build it.
- else
- {
- $res = '@' . $res . '@';
- $res =~ s/ /@@/g;
- }
-
- return $res;
-}
-
-
-
## ------------------------------ ##
## Handling the condition stack. ##
## ------------------------------ ##
-# $COND_STRING
+# $COND
# cond_stack_if ($NEGATE, $COND, $WHERE)
# --------------------------------------
sub cond_stack_if ($$$)
push (@cond_stack, $cond);
- return conditional_string (@cond_stack);
+ return new Automake::Conditional (@cond_stack);
}
-# $COND_STRING
+# $COND
# cond_stack_else ($NEGATE, $COND, $WHERE)
# ----------------------------------------
sub cond_stack_else ($$$)
if $cond_stack[$#cond_stack] ne $cond;
}
- return conditional_string (@cond_stack);
+ return new Automake::Conditional (@cond_stack);
}
-# $COND_STRING
+# $COND
# cond_stack_endif ($NEGATE, $COND, $WHERE)
# -----------------------------------------
sub cond_stack_endif ($$$)
pop @cond_stack;
- return conditional_string (@cond_stack);
+ return new Automake::Conditional (@cond_stack);
}
{
return ("$var multiply defined in condition $cond", $vcond);
}
- elsif (&conditional_true_when ($vcond, $cond))
+ elsif ($vcond->true_when ($cond))
{
return ("$var was already defined in condition $vcond, "
. "which implies condition $cond", $vcond);
}
- elsif (&conditional_true_when ($cond, $vcond))
+ elsif ($cond->true_when ($vcond))
{
return ("$var was already defined in condition $vcond, "
. "which is implied by condition $cond", $vcond);
# endif
# C = mumble
#
-# we should have:
+# we should have (we display result as conditional strings in this
+# illustration, but we really return Conditional objects):
# variable_not_always_defined_in_cond ('A', 'COND1_TRUE COND2_TRUE')
# => ()
# variable_not_always_defined_in_cond ('A', 'COND1_TRUE')
# => ()
# variable_not_always_defined_in_cond ('Z', 'TRUE')
# => ("TRUE")
-#
sub variable_not_always_defined_in_cond ($$)
{
my ($var, $cond) = @_;
# It's easy to answer if the variable is not defined.
- return ("TRUE",) unless exists $var_value{$var};
+ return (TRUE,) unless exists $var_value{$var};
# How does it work? Let's take the second example:
#
if exists $var_value{$var}{$icond};
push @res, $icond
- if (conditional_true_when ($cond, $icond)); # (3)
+ if ($cond->true_when ($icond)); # (3)
}
return @res;
}
{
my ($var, $owner, $type, $cond, $value, $where) = @_;
+ prog_error "$cond is not a reference"
+ unless ref $where;
+
prog_error "$where is not a reference"
unless ref $where;
check_variable_expansions ($value, $where);
- $cond ||= 'TRUE';
-
# An Automake variable must be consistently defined with the same
# sign by Automake. A user variable must be set by either `=' or
# `:=', and later promoted to `+='.
# @COND_FALSE@FOO = foo2 bar
# Do we need an helper variable?
- if ($cond ne 'TRUE')
+ if ($cond != TRUE)
{
# Does the helper variable already exists?
- my $key = "$var:$cond";
+ my $key = "$var:" . $cond->string;
if (exists $appendvar{$key})
{
# Yes, let's simply append to it.
err ($where,
"Cannot apply `+=' because `$var' is not defined "
. "in\nthe following conditions:\n "
- . join ("\n ", @undef_cond)
+ . join ("\n ", map { $_->string } @undef_cond)
. "\nEither define `$var' in these conditions,"
. " or use\n`+=' in the same conditions as"
. " the definitions.");
{
verb ("refusing to override the user definition of:\n"
. macro_dump ($var)
- ."with `$cond' => `$value'");
+ ."with `$cond->string' => `$value'");
}
else
{
return 1
if variable_defined $var;
- require_variables ($where, "variable `$var' is used", 'TRUE', $var);
+ require_variables ($where, "variable `$var' is used", TRUE, $var);
return 0;
}
# because we will need to define the variable under both conditions.
sub variable_conditions_recursive ($)
{
- my ($var) = @_;
+ my ($var) = @_;
- %vars_scanned = ();
+ %vars_scanned = ();
- my @new_conds = variable_conditions_recursive_sub ($var, '');
+ my @new_conds = variable_conditions_recursive_sub ($var, TRUE);
- # Now we want to return all permutations of the subvariable
- # conditions.
- my %allconds = ();
+ # Now we want to return all permutations of the subvariable
+ # conditions.
+ my %allconds = ();
foreach my $item (@new_conds)
- {
- foreach (split (' ', $item))
- {
+ {
+ foreach ($item->conds)
+ {
s/^(.*)_(TRUE|FALSE)$/$1_TRUE/;
$allconds{$_} = 1;
- }
- }
- @new_conds = variable_conditions_permutations (sort keys %allconds);
-
- my %uniqify;
- foreach my $cond (@new_conds)
- {
- my $reduce = variable_conditions_reduce (split (' ', $cond));
- next
- if $reduce eq 'FALSE';
- $uniqify{$cond} = 1;
- }
+ }
+ }
+ @new_conds = variable_conditions_permutations (sort keys %allconds);
- # Note we cannot just do `return sort keys %uniqify', because this
- # function is sometimes used in a scalar context.
- my @uniq_list = sort by_condition keys %uniqify;
- return @uniq_list;
+ # Note we cannot just do `return sort @new_conds', because this
+ # function is sometimes used in a scalar context.
+ my @uniq_list = sort by_condition @new_conds;
+ return @uniq_list;
}
my @svc = variable_conditions_recursive_sub ($varname, $var);
foreach my $item (@svc)
{
- my $val = conditional_string ($vcond, split (' ', $item));
- $val ||= 'TRUE';
- push (@subvar_conds, $val);
+ push (@subvar_conds, $vcond->merge ($item));
}
}
foreach my $this_cond (@this_conds)
{
my @perms =
- variable_conditions_permutations (split (' ', $this_cond));
+ variable_conditions_permutations ($this_cond->conds);
foreach my $perm (@perms)
{
my $ok = 1;
foreach my $scan (@this_conds)
{
- if (&conditional_true_when ($perm, $scan)
- || &conditional_true_when ($scan, $perm))
+ if ($perm->true_when ($scan) || $scan->true_when ($perm))
{
$ok = 0;
last;
# If the list is empty, return TRUE
sub variable_conditions_reduce
{
- my (@conds) = @_;
- my @ret = ();
- my $cond;
- while(@conds > 0)
+ my (@conds) = @_;
+ my @ret = ();
+ my $cond;
+ while (@conds > 0)
{
- $cond = shift(@conds);
+ $cond = shift @conds;
- # FALSE is absorbent.
- return 'FALSE'
- if $cond eq 'FALSE';
+ # FALSE is absorbent.
+ return FALSE
+ if $cond == FALSE;
- if (!conditional_is_redundant ($cond, @ret, @conds))
- {
- push (@ret, $cond);
- }
+ if (! $cond->redundant_wrt (@ret, @conds))
+ {
+ push (@ret, $cond);
+ }
}
- return "TRUE" if @ret == 0;
- return @ret;
+ return TRUE if @ret == 0;
+ return @ret;
}
# @CONDS
my @notconds = ();
# Generate all permutation for all inputs.
- my @perm =
- map { variable_conditions_permutations (split(' ', $_)); } @conds;
+ my @perm = map { variable_conditions_permutations ($_->conds); } @conds;
# Remove redundant conditions.
@perm = variable_conditions_reduce @perm;
foreach my $perm (@perm)
{
push @notconds, $perm
- if ! conditional_implies_any ($perm, @conds);
+ if ! $perm->implies_any (@conds);
}
return @notconds;
}
# => ("TRUE")
sub variable_conditions_permutations
{
- my (@comps) = @_;
- return ()
- if ! @comps;
- my $comp = shift (@comps);
- return variable_conditions_permutations (@comps)
- if $comp eq '';
- my $neg = condition_negate ($comp);
+ my (@comps) = @_;
+ return ()
+ if ! @comps;
+ my $comp = shift (@comps);
+ return variable_conditions_permutations (@comps)
+ if $comp eq '';
+ my $neg = condition_negate ($comp);
- my @ret;
- foreach my $sub (variable_conditions_permutations (@comps))
+ my @ret;
+ foreach my $sub (variable_conditions_permutations (@comps))
{
- push (@ret, "$comp $sub") if $comp ne 'FALSE';
- push (@ret, "$neg $sub") if $neg ne 'FALSE';
+ push (@ret, $sub->merge_conds ($comp)) if $comp ne 'FALSE';
+ push (@ret, $sub->merge_conds ($neg)) if $neg ne 'FALSE';
}
- if (! @ret)
+ if (! @ret)
{
- push (@ret, $comp) if $comp ne 'FALSE';
- push (@ret, $neg) if $neg ne 'FALSE';
+ push (@ret, new Automake::Conditional $comp) if $comp ne 'FALSE';
+ push (@ret, new Automake::Conditional $neg) if $neg ne 'FALSE';
}
- return @ret;
+ return @ret;
}
foreach my $cond (keys %{$var_value{$var}})
{
next
- if $cond =~ /^TRUE|FALSE$/;
+ if $cond->true || $cond->false;
if ($parent)
{
{
my ($var) = @_;
&check_variable_defined_unconditionally ($var);
- return $var_value{$var}{'TRUE'};
+ return $var_value{$var}{&TRUE};
}
unless variable_assert $var, $parent;
# Get value for given condition
- $cond ||= 'TRUE';
my $onceflag;
foreach my $vcond (keys %{$var_value{$var}})
{
my $val = $var_value{$var}{$vcond};
- if (&conditional_true_when ($vcond, $cond))
+ if ($vcond->true_when ($cond))
{
# Unless variable is not defined conditionally, there should only
# be one value of $vcond true when $cond.
}
else
{
- $cond ||= 'TRUE';
$vars_scanned{$var} = 1;
my $onceflag;
foreach my $vcond (keys %{$var_value{$var}})
{
my $val = $var_value{$var}{$vcond};
my $where = $var_location{$var}{$vcond};
- if (&conditional_true_when ($vcond, $cond))
+ if ($vcond->true_when ($cond))
{
# Warn if we have an ambiguity. It's hard to know how
# to handle this case correctly.
my $val = $var_value{$var}{$cond};
my $equals = $var_type{$var}{$cond} eq ':' ? ':=' : '=';
my $output_var = "$var $equals $val";
- $output_var =~ s/^/make_condition ($cond)/meg;
+ my $str = $cond->subst_string;
+ $output_var =~ s/^/$str/meg;
$output_vars .= $output_var . "\n";
}
}
my $val = $var_value{$var}{$cond};
my $equals = $var_type{$var}{$cond} eq ':' ? ':=' : '=';
- my $make_condition = make_condition ($cond);
+ my $make_condition = $cond->subst_string;
$output_vars .= pretty_print_internal ("$make_condition$var $equals",
"$make_condition\t",
split (' ' , $val));
{
my ($var, $cond, $where, @value) = @_;
- # Beware that an empty $cond has a different semantics for
- # macro_define and variable_pretty_output.
- $cond ||= 'TRUE';
-
if (! variable_defined ($var, $cond))
{
macro_define ($var, VAR_AUTOMAKE, '', $cond, "@value", $where);
- variable_pretty_output ($var, $cond || 'TRUE');
+ variable_pretty_output ($var, $cond);
$content_seen{$var} = 1;
}
}
sub define_variable ($$$)
{
my ($var, $value, $where) = @_;
- define_pretty_variable ($var, 'TRUE', $where, $value);
+ define_pretty_variable ($var, TRUE, $where, $value);
}
sub define_configure_variable ($)
{
my ($var) = @_;
- if (! variable_defined ($var, 'TRUE')
+ if (! variable_defined ($var, TRUE)
# Explicitly avoid ANSI2KNR -- we AC_SUBST that in
# protos.m4, but later define it elsewhere. This is
# pretty hacky. We also explicitly avoid AMDEPBACKSLASH:
# appreciated by Make.
&& ! grep { $_ eq $var } (qw(ANSI2KNR AMDEPBACKSLASH)))
{
- macro_define ($var, VAR_CONFIGURE, '', 'TRUE',
+ macro_define ($var, VAR_CONFIGURE, '', TRUE,
subst $var, $configure_vars{$var});
- variable_pretty_output ($var, 'TRUE');
+ variable_pretty_output ($var, TRUE);
}
}
prog_error "$where is not a reference"
unless ref $where;
+ prog_error "$cond is not a reference"
+ unless ref $cond;
# Don't even think about defining a rule in condition FALSE.
- return () if $cond eq 'FALSE';
+ return () if $cond == FALSE;
# For now `foo:' will override `foo$(EXEEXT):'. This is temporary,
# though, so we emit a warning.
my $oldowner = $target_owner{$target}{$cond};
# Don't mention true conditions in diagnostics.
- my $condmsg = $cond ne 'TRUE' ? " in condition `$cond'" : '';
+ my $condmsg = $cond == TRUE ? " in condition `$cond'" : '';
if ($owner == TARGET_USER)
{
@conds = ();
for my $undefined_cond (invert_conditions(@defined_conds))
{
- push @conds, make_condition ($cond, $undefined_cond);
+ push @conds, $cond->merge ($undefined_cond);
}
# No conditions left to define the rule.
# Warn, because our workaround is meaningless in this case.
register_suffix_rule ($where, $1, $2);
}
- # Return "" instead of TRUE so it can be used with make_paragraphs
- # directly.
- return "" if 1 == @conds && $conds[0] eq 'TRUE';
return @conds;
}
# sure it is the same on exit. This lets us conditonally include
# other files.
my @saved_cond_stack = @cond_stack;
- my $cond = conditional_string (@cond_stack);
+ my $cond = new Automake::Conditional (@cond_stack);
my $last_var_name = '';
my $last_var_type = '';
{
if ($prev_state == IN_RULE_DEF)
{
- $output_trailer .= &make_condition (@cond_stack);
- $output_trailer .= $_;
+ my $cond = new Automake::Conditional @cond_stack;
+ $output_trailer .= $cond->subst_string;
+ $output_trailer .= $_;
}
elsif ($prev_state == IN_COMMENT)
{
if (!/\\$/)
{
- append_comments ($cond || 'TRUE',
- $last_var_name, $spacing, $comment);
+ append_comments ($cond, $last_var_name, $spacing, $comment);
$comment = $spacing = '';
macro_define ($last_var_name, VAR_MAKEFILE,
$last_var_type, $cond,
$last_var_value, $where)
- if $cond ne 'FALSE';
+ if $cond != FALSE;
push (@var_list, $last_var_name);
}
}
# For now we have to output all definitions of user rules
# and can't diagnose duplicates (see the comment in
# rule_define). So we go on and ignore the return value.
- rule_define ($1, $amfile, TARGET_USER, $cond || 'TRUE', $where);
+ rule_define ($1, $amfile, TARGET_USER, $cond, $where);
check_variable_expansions ($_, $where);
$output_trailer .= $comment . $spacing;
- $output_trailer .= &make_condition (@cond_stack);
+ my $cond = new Automake::Conditional @cond_stack;
+ $output_trailer .= $cond->subst_string;
$output_trailer .= $_;
$comment = $spacing = '';
}
if (!/\\$/)
{
# Accumulating variables must not be output.
- append_comments ($cond || 'TRUE',
- $last_var_name, $spacing, $comment);
+ append_comments ($cond, $last_var_name, $spacing, $comment);
$comment = $spacing = '';
macro_define ($last_var_name, VAR_MAKEFILE,
$last_var_type, $cond,
$last_var_value, $where)
- if $cond ne 'FALSE';
+ if $cond != FALSE;
push (@var_list, $last_var_name);
}
}
$prev_state = IN_RULE_DEF;
check_variable_expansions ($_, $where);
$output_trailer .= $comment . $spacing;
- $output_trailer .= &make_condition (@cond_stack);
+ my $cond = new Automake::Conditional @cond_stack;
+ $output_trailer .= $cond->subst_string;
$output_trailer .= $_;
$comment = $spacing = '';
err $where, "`#' comment at start of rule is unportable"
# sure it is the same on exit. This lets us conditonally include
# other files.
my @saved_cond_stack = @cond_stack;
- my $cond = conditional_string (@cond_stack);
+ my $cond = new Automake::Conditional (@cond_stack);
foreach (make_paragraphs ($file, %transform))
{
# Handle inclusion of other files.
elsif (/$INCLUDE_PATTERN/o)
{
- if ($cond ne 'FALSE')
+ if ($cond != FALSE)
{
my $file = ($is_am ? "$libdir/am/" : '') . $1;
$where->push_context ("`$file' included from here");
# `foo:' after `foo bar:'.
# Output only if not in FALSE.
- if (defined $dependencies{$_} && $cond ne 'FALSE')
+ if (defined $dependencies{$_} && $cond != FALSE)
{
&depend ($_, @deps);
$actions{$_} .= $actions;
my @undefined_conds =
rule_define ($targets, $file,
$is_am ? TARGET_AUTOMAKE : TARGET_USER,
- $cond || 'TRUE', $where);
+ $cond, $where);
for my $undefined_cond (@undefined_conds)
{
my $condparagraph = $paragraph;
- $condparagraph =~ s/^/$undefined_cond/gm;
+ $condparagraph =~ s/^/$undefined_cond->subst_string/gme;
$result_rules .= "$spacing$comment$condparagraph\n";
}
if (scalar @undefined_conds == 0)
$is_rule = 0;
# Accumulating variables must not be output.
- append_comments ($cond || 'TRUE', $var, $spacing, $comment);
+ append_comments ($cond, $var, $spacing, $comment);
macro_define ($var, $is_am ? VAR_AUTOMAKE : VAR_MAKEFILE,
$type, $cond, $val, $where)
- if $cond ne 'FALSE';
+ if $cond != FALSE;
push (@var_list, $var);
# If the user has set some variables we were in charge
# of (which is detected by the first reading of
# `header-vars.am'), we must not output them.
$result_vars .= "$spacing$comment$_\n"
- if ($cond ne 'FALSE' && $type ne '+'
- && exists $var_owner{$var}{$cond || 'TRUE'}
- && $var_owner{$var}{$cond || 'TRUE'} == VAR_AUTOMAKE);
+ if ($cond != FALSE && $type ne '+'
+ && exists $var_owner{$var}{$cond}
+ && $var_owner{$var}{$cond} == VAR_AUTOMAKE);
$comment = $spacing = '';
}
# This isn't an error; it is probably some tokens which
# configure is supposed to replace, such as `@SET-MAKE@',
# or some part of a rule cut by an if/endif.
- if ($cond ne 'FALSE' && ! ($is_rule && $discard_rule))
+ if (! $cond->false && ! ($is_rule && $discard_rule))
{
- s/^/make_condition (@cond_stack)/gme;
+ s/^/$cond->subst_string/gme;
$result_rules .= "$spacing$comment$_\n";
}
$comment = $spacing = '';
# conditionally defined (or else adjust the condition below).
next
if (exists $var_owner{$varname}
- && exists $var_owner{$varname}{'TRUE'}
- && $var_owner{$varname}{'TRUE'} != VAR_MAKEFILE);
+ && exists $var_owner{$varname}{&TRUE}
+ && $var_owner{$varname}{&TRUE} != VAR_MAKEFILE);
if ($varname =~ /^(nobase_)?(dist_|nodist_)?(.*)_$primary$/)
{
if (@used && $primary ne 'JAVA' && $primary ne 'PYTHON')
{
# Define it.
- define_pretty_variable ($primary, '', INTERNAL, @used);
+ define_pretty_variable ($primary, TRUE, INTERNAL, @used);
$output_vars .= "\n";
}
{
prog_error "push_dist_common run after handle_dist"
if $handle_dist_run;
- macro_define ('DIST_COMMON', VAR_AUTOMAKE, '+', '', "@_", INTERNAL);
+ macro_define ('DIST_COMMON', VAR_AUTOMAKE, '+', TRUE, "@_", INTERNAL);
}
unless @undef_cond;
my $text = "$reason`$var' is undefined\n";
- if (@undef_cond && $undef_cond[0] ne 'TRUE')
+ if (@undef_cond && $undef_cond[0] != TRUE)
{
$text .= ("in the following conditions:\n "
- . join ("\n ", @undef_cond));
+ . join ("\n ", map { $_->string } @undef_cond));
}
++$res;
--- /dev/null
+# Copyright (C) 1997, 2001, 2002 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+# 02111-1307, USA.
+
+package Automake::Conditional;
+use strict;
+use Carp;
+
+=head1 NAME
+
+Automake::Conditional - record a conjunction of conditions
+
+=head1 SYNOPSIS
+
+ use Automake::Conditional;
+
+ # Create a conditional to represent "COND1 and not COND2".
+ my $cond = new Automake::Conditional "COND1_TRUE", "COND2_FALSE";
+ # Create a conditional to represent "not COND3".
+ my $other = new Automake::Conditional "COND3_FALSE";
+
+ # Create a conditional to represent
+ # "COND1 and not COND2 and not COND3".
+ my $both = $cond->merge ($other);
+
+ # Likewise, but using a list of atomica conditional strings
+ my $both2 = $cond->merge_conds ("COND3_FALSE");
+
+ # Return the list of conditions ("COND1_TRUE", "COND2_FALSE"):
+ my @conds = $cond->conds;
+
+ # Is $cond always true? (Not in this example)
+ if ($cond->true) { ... }
+
+ # Is $cond always false? (Not in this example)
+ if ($cond->false) { ... }
+
+ # Return the list of conditions as a string:
+ # "COND1_TRUE COND2_FALSE"
+ my $str = $cond->string;
+
+ # Return the list of conditions as a AC_SUBST-style string:
+ # "@COND1_TRUE@@COND2_FALSE@"
+ my $subst = $cond->subst_string;
+
+ # Is $cond true when $both is true? (Yes in this example)
+ if ($cond->true_when ($both)) { ... }
+
+ # Is $cond redundant w.r.t. {$other, $both}?
+ # (Yes in this example)
+ if ($cond->redundant_wrt ($other, $both)) { ... }
+
+ # Does $cond imply any of {$other, $both}?
+ # (Not in this example)
+ if ($cond->implies_any ($other, $both)) { ... }
+
+=head1 DESCRIPTION
+
+A C<Conditional> is a conjunction of atomic conditions. In Automake they
+are used to represent the conditions into which Makefile variables and
+Makefile rules are defined.
+
+If the variable C<VAR> is defined as
+
+ if COND1
+ if COND2
+ VAR = value
+ endif
+ endif
+
+then it will be associated a C<Conditional> created with
+the following statement.
+
+ new Automake::Conditional "COND1_TRUE", "COND2_TRUE";
+
+Remember that a C<Conditional> is a I<conjunction> of conditions, so
+the above C<Conditional> means C<VAR> is defined when C<COND1> is true
+B<and> C<COND2> are true. There is no way to express disjunctions
+(i.e., I<or>s) with this class.
+
+Another point worth to mention is that each C<Conditional> object is
+unique with respect to its conditions. Two C<Conditional> objects
+created for the same set of conditions will have the same adress.
+This makes it easy to compare C<Conditional>s, just compare the
+references.
+
+ my $c1 = new Automake::Conditional "COND1_TRUE", "COND2_TRUE";
+ my $c2 = new Automake::Conditional "COND1_TRUE", "COND2_TRUE";
+ $c1 == $c2; # True!
+
+=head2 Methods
+
+=over 4
+
+=item C<$cond = new Automake::Conditional [@conds]>
+
+Return a C<Conditional> objects for the conjunctions of conditions
+listed in C<@conds> as strings.
+
+An item in C<@conds> should be either C<"FALSE">, C<"TRUE">, or have
+the form C<"NAME_FALSE"> or C<"NAME_TRUE"> where C<NAME> can be
+anything (in practice C<NAME> should be the name of a conditional
+declared in F<configure.ac> with C<AM_CONDITIONAL>, but it's not
+C<Automake::Conditional>'s responsability to ensure this).
+
+An empty C<@conds> means C<"TRUE">.
+
+As explained previously, the reference (object) returned is unique
+with respect to C<@conds>. For this purpose, duplicate elements are
+ignored, and C<@conds> is rewriten as C<("FALSE")> if it contains
+C<"FALSE"> or two contradictory conditions (such as C<"NAME_FALSE">
+and C<"NAME_TRUE">.)
+
+Therefore the following two statements create the same object (they
+both create the C<"FALSE"> conditional).
+
+ my $c3 = new Automake::Conditional "COND1_TRUE", "COND1_FALSE";
+ my $c4 = new Automake::Conditional "COND2_TRUE", "FALSE";
+ $c3 == $c4; # True!
+ $c3 == FALSE; # True!
+
+=cut
+
+# Keys in this hash are conditionnal strings. Values are the
+# associated object conditions. This is used by `new' to reuse
+# Conditional objects with identical conditions.
+use vars '%_conditional_singletons';
+%_conditional_singletons = ();
+
+sub new ($;@)
+{
+ my ($class, @conds) = @_;
+ my $self = {
+ hash => {},
+ };
+ bless $self, $class;
+
+ # Accept strings like "FOO BAR" as shorthand for ("FOO", "BAR").
+ @conds = map { split (' ', $_) } @conds;
+
+ for my $cond (@conds)
+ {
+ next if $cond eq 'TRUE';
+
+ # Catch some common programming errors:
+ # - A conditional passed to new
+ confess "`$cond' is a reference, expected a string" if ref $cond;
+ # - A conditional passed as a string to new
+ confess "`$cond' doesn't look like a condition" if $cond =~ /::/;
+
+ # Detect cases when @conds can be simplified to FALSE.
+ if (($cond eq 'FALSE' && $#conds > 0)
+ || ($cond =~ /^(.*)_TRUE$/ && exists $self->{'hash'}{"${1}_FALSE"})
+ || ($cond =~ /^(.*)_FALSE$/ && exists $self->{'hash'}{"${1}_TRUE"}))
+ {
+ return new Automake::Conditional 'FALSE';
+ }
+
+ $self->{'hash'}{$cond} = 1;
+ }
+
+ my $key = $self->string;
+ if (exists $_conditional_singletons{$key})
+ {
+ return $_conditional_singletons{$key};
+ }
+ $_conditional_singletons{$key} = $self;
+ return $self;
+}
+
+=item C<$newcond = $cond-E<gt>merge ($othercond)>
+
+Return a new condition which is the conjunction of
+C<$cond> and C<$othercond>.
+
+=cut
+
+sub merge ($$)
+{
+ my ($self, $other) = @_;
+ new Automake::Conditional $self->conds, $other->conds;
+}
+
+=item C<$newcond = $cond-E<gt>merge_conds (@conds)>
+
+Return a new condition which is the conjunction of C<$cond> and
+C<@conds>, where C<@conds> is a list of atomic condition strings, as
+passed to C<new>.
+
+=cut
+
+sub merge_conds ($@)
+{
+ my ($self, @conds) = @_;
+ new Automake::Conditional $self->conds, @conds;
+}
+
+=item C<@list = $cond-E<gt>conds>
+
+Return the set of conditions defining C<$cond>, as strings. Note that
+this might not be exactly the list passed to C<new> (or a
+concatenation of such lists if C<merge> was used), because of the
+cleanup mentioned in C<new>'s description.
+
+For instance C<$c3-E<gt>conds> will simply return C<("FALSE")>.
+
+=cut
+
+sub conds ($ )
+{
+ my ($self) = @_;
+ my @conds = keys %{$self->{'hash'}};
+ return ("TRUE") unless @conds;
+ return sort @conds;
+}
+
+# Undocumented, shouldn't be needed out of this class.
+sub has ($$)
+{
+ my ($self, $cond) = @_;
+ if (exists $self->{'hash'}{$cond})
+ {
+ return $self->{'hash'}{$cond};
+ }
+ return 0;
+}
+
+=item C<$cond-E<gt>false>
+
+Return 1 iff this condition is always false.
+
+=cut
+
+sub false ($ )
+{
+ my ($self) = @_;
+ return $self->has ('FALSE');
+}
+
+=item C<$cond-E<gt>true>
+
+Return 1 iff this condition is always true.
+
+=cut
+
+sub true ($ )
+{
+ my ($self) = @_;
+ return 0 == keys %{$self->{'hash'}};
+}
+
+=item C<$cond-E<gt>string>
+
+Build a string which denotes the conditional.
+
+For instance using the C<$cond> definition from L<SYNOPSYS>,
+C<$cond-E<gt>string> will return C<"COND1_TRUE COND2_FALSE">.
+
+=cut
+
+sub string ($ )
+{
+ my ($self) = @_;
+
+ return $self->{'string'} if defined $self->{'string'};
+
+ my $res = '';
+ if ($self->false)
+ {
+ $res = 'FALSE';
+ }
+ else
+ {
+ $res = join (' ', sort $self->conds);
+ }
+ $self->{'string'} = $res;
+ return $res;
+}
+
+=item C<$cond-E<gt>subst_string>
+
+Build a C<AC_SUBST>-style string for output in F<Makefile.in>.
+
+For instance using the C<$cond> definition from L<SYNOPSYS>,
+C<$cond-E<gt>subst_string> will return C<"@COND1_TRUE@@COND2_FALSE@">.
+
+=cut
+
+sub subst_string ($ )
+{
+ my ($self) = @_;
+
+ return $self->{'subst_string'} if defined $self->{'subst_string'};
+
+ my $res = '';
+ if ($self->false)
+ {
+ $res = '#';
+ }
+ elsif (! $self->true)
+ {
+ $res = '@' . join ('@@', sort $self->conds) . '@';
+ }
+ $self->{'subst_string'} = $res;
+ return $res;
+}
+
+=item C<$cond-E<gt>true_when ($when)>
+
+Return 1 iff C<$cond> is true when C<$when> is true.
+Return 0 otherwise.
+
+Using the definitions from L<SYNOPSYS>, C<$cond> is true
+when C<$both> is true, but the converse is wrong.
+
+=cut
+
+sub true_when ($$)
+{
+ my ($self, $when) = @_;
+
+ # Nothing is true when FALSE (not even FALSE itself, but it
+ # shouldn't hurt if you decide to change that).
+ return 0 if $self->false || $when->false;
+
+ # If we are true, we stay true when $when is true :)
+ return 1 if $self->true;
+
+ # $SELF is true under $WHEN if each conditional component of $SELF
+ # exists in $WHEN.
+ foreach my $cond ($self->conds)
+ {
+ return 0 unless $when->has ($cond);
+ }
+ return 1;
+}
+
+=item C<$cond-E<gt>redundant_wrt (@conds)>
+
+Return 1 iff C<$cond> is true for any condition in C<@conds>.
+If @conds is empty, return 1 iff C<$cond> is C<FALSE>.
+Return 0 otherwise.
+
+=cut
+
+sub redundant_wrt ($@)
+{
+ my ($self, @conds) = @_;
+
+ foreach my $cond (@conds)
+ {
+ return 1 if $self->true_when ($cond);
+ }
+ return $self->false;
+}
+
+=item C<$cond-E<gt>implies_any (@conds)>
+
+Return 1 iff C<$cond> implies any of the conditions in C<@conds>.
+Return 0 otherwise.
+
+=cut
+
+# $BOOLEAN
+# &conditional_implies_any ($COND, @CONDS)
+# ----------------------------------------
+# Returns true iff $COND implies any of the conditions in @CONDS.
+sub implies_any ($@)
+{
+ my ($self, @conds) = @_;
+
+ foreach my $cond (@conds)
+ {
+ return 1 if $cond->true_when ($self);
+ }
+ return 0;
+}
+
+=head1 HISTORY
+
+C<AM_CONDITIONAL>s and supporting code were added to Automake 1.1o by
+Ian Lance Taylor <ian@cygnus.org> in 1997. Since then it has been
+improved by Tom Tromey <tromey@redhat.com>, Richard Boulton
+<richard@tartarus.org>, Raja R Harinath <harinath@cs.umn.edu>, and
+Akim Demaile <akim@epita.fr>. Alexandre Duret-Lutz <adl@gnu.org>
+extracted the code out of Automake to create this package in 2002.
+
+=cut
+
+1;
+
+### Setup "GNU" style for perl-mode and cperl-mode.
+## Local Variables:
+## perl-indent-level: 2
+## perl-continued-statement-offset: 2
+## perl-continued-brace-offset: 0
+## perl-brace-offset: 0
+## perl-brace-imaginary-offset: 0
+## perl-label-offset: -2
+## cperl-indent-level: 2
+## cperl-brace-offset: 0
+## cperl-continued-brace-offset: 0
+## cperl-label-offset: -2
+## cperl-extra-newline-before-brace: t
+## cperl-merge-trailing-else: nil
+## cperl-continued-statement-offset: 2
+## End: