From 0b13d3e296faed005650241f6c4aa441e78125e7 Mon Sep 17 00:00:00 2001 From: Matthias Paulmier Date: Mon, 18 Jun 2018 17:00:46 +0200 Subject: VarAppend: New module This module is used in Automake::Variable::define to append values to variables. * VarAppend: New module with helper functions for appending to variables. --- lib/Automake/VarAppend.pm | 124 ++++++++++++++++++++ lib/Automake/Variable.pm | 291 +++++++++++++++++++--------------------------- lib/Automake/local.mk | 1 + 3 files changed, 246 insertions(+), 170 deletions(-) create mode 100644 lib/Automake/VarAppend.pm diff --git a/lib/Automake/VarAppend.pm b/lib/Automake/VarAppend.pm new file mode 100644 index 000000000..8f114cfef --- /dev/null +++ b/lib/Automake/VarAppend.pm @@ -0,0 +1,124 @@ +# Copyright (C) 2018 Matthias Paulmier + +# 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 +# of the License, 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 . + +package Automake::VarAppend; + +use strict; +use 5.006; + +use Automake::Channels; +use Automake::Condition qw (TRUE FALSE); +use Automake::Item; +use Automake::VarDef; +use Exporter; + +use vars '@ISA', '@EXPORT'; + +@ISA = qw (Automake::Item Exporter); + +@EXPORT = qw (append_var_cur_cond first_assign_var am_check_definitions); + +=head1 NAME + +Automake::VarAppend - Helper methods for appending to variables + +=head1 DESCRIPTION + +This package provides methods for appending values to variables. + +It is used by the C class in its C function. + +=head2 Functions + +=item C + +Append $value to an existing $var defined for the current condition. This is +only used in the define method of this file. + +=cut + +sub append_var_cur_cond ($$$$$$$) +{ + my ($self, $var, $owner, $where, $def, $value, $comment) = @_; + $def->append ($value, $comment); + $self->{'last-append'} = []; + + # Only increase owners. A VAR_CONFIGURE variable augmented in a + # Makefile.am becomes a VAR_MAKEFILE variable. + $def->set_owner ($owner, $where->clone) + if $owner > $def->owner; +} + + +=item C + +Method that assign a value to a variable for the first time or for total +redefinition of an Automake variable or an AC_SUBST variable for an existing +condition. + +=cut + +sub first_assign_var ($$$$$$$$$$$) +{ + my ($self, $var, $cond, $owner, $where, $def, $value, $pretty, $comment, $new_var, $type) = @_; + + # Never decrease an owner. + $owner = $def->owner + if ! $new_var && $owner < $def->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->clone, + $type, $owner, $pretty); + $self->set ($cond, $def); +} + + +=item am_check_definitions ($var, $cond, $def, $type, $where) + +Additional checks for Automake definitions + +=cut + +sub am_check_definitions ($$$$$) +{ + my ($var, $cond, $def, $type, $where) = @_; + # An Automake variable must be consistently defined with the same + # sign by Automake. + if ($def->type ne $type && $def->owner == VAR_AUTOMAKE) + { + error ($def->location, + "Automake variable '$var' was set with '" + . $def->type . "=' here ...", partial => 1); + error ($where, "... and is now set with '$type=' here."); + prog_error ("Automake variable assignments should be consistently\n" + . "defined with the same sign"); + } +} + + +=back + +=head1 SEE ALSO + +L, L, +L, L, +L. + +=cut + +1; diff --git a/lib/Automake/Variable.pm b/lib/Automake/Variable.pm index 5dea97e7f..7dc485dde 100644 --- a/lib/Automake/Variable.pm +++ b/lib/Automake/Variable.pm @@ -30,6 +30,7 @@ use Automake::General 'uniq'; use Automake::Global; use Automake::Item; use Automake::Location; +use Automake::VarAppend; use Automake::VarDef; use Automake::Wrap 'makefile_wrap'; require Exporter; @@ -146,6 +147,8 @@ my $_VARIABLE_PATTERN = '^' . $_VARIABLE_CHARACTERS . "\$"; my $_VARIABLE_RECURSIVE_PATTERN = '^([.A-Za-z0-9_@]|\$[({]' . $_VARIABLE_CHARACTERS . '[})]?)+' . "\$"; +# Count of helper variables used to implement conditional '+='. +my $_appendvar; # The order in which variables should be output. (May contain # duplicates -- only the first occurrence matters.) my @_var_order; @@ -180,7 +183,7 @@ my %_am_macro_for_var = pyexecdir => 'AM_PATH_PYTHON', PYTHON => 'AM_PATH_PYTHON', pythondir => 'AM_PATH_PYTHON', - ); + ); # Macros shipped with Autoconf. my %_ac_macro_for_var = @@ -202,7 +205,7 @@ my %_ac_macro_for_var = UPC => 'AM_PROG_UPC', UPCFLAGS => 'AM_PROG_UPC', YACC => 'AC_PROG_YACC', - ); + ); # The name of the configure.ac file. my $configure_ac; @@ -216,9 +219,6 @@ my %_silent_variable_override = JAVAC => 1, JAVAROOT => 1); -# Count of helper variables used to implement conditional '+='. -my $_appendvar; - # Each call to C gets an # unique label. This is used to detect recursively defined variables. my $_traversal = 0; @@ -354,9 +354,9 @@ other internal data. sub reset () { + $_appendvar = 0; %_variable_dict = (); %_primary_dict = (); - $_appendvar = 0; @_var_order = (); %_gen_varname = (); %_gen_varname_n = (); @@ -804,159 +804,6 @@ sub check_variable_expansions ($$) } -# _append_var_cur_cond ($self, $var, $owner, $where, $def, $value, $comment) -# -------------------------------------------------- -# Append $value to an existing $var defined for the current condition. -# This is only used in the define method of this file. -sub _append_var_cur_cond ($$$$$$$) -{ - my ($self, $var, $owner, $where, $def, $value, $comment) = @_; - $def->append ($value, $comment); - $self->{'last-append'} = []; - - # Only increase owners. A VAR_CONFIGURE variable augmented in a - # Makefile.am becomes a VAR_MAKEFILE variable. - $def->set_owner ($owner, $where->clone) - if $owner > $def->owner; -} - -# _append_var_other_cond ($self, $var, $cond, $owner, $where, $def, $value, $pretty, $comment, $new_var) -# ----------------------------------------------------------------------------- -# We declare a helper variable conditionally when $cond is not TRUE. -# Example for: -# FOO = foo -# if COND -# FOO += bar -# endif -# We do: -# FOO = foo $(am__append_1) -# @COND_TRUE@am__append_1 = bar -# When $cond is TRUE however, we do not need this variable. - -sub _append_var_other_cond ($$$$$$$$$$) -{ - my ($self, $var, $cond, $owner, $where, $def, $value, $pretty, $comment, $new_var) = @_; - 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->string; - my $key = "$var:$condstr"; - my ($appendvar, $appendvarcond) = @{$self->{'last-append'}}; - if ($appendvar && $condstr eq $appendvarcond) - { - $var = $appendvar; - $owner = VAR_AUTOMAKE; - $self = var ($var); - $def = $self->rdef ($cond); - $new_var = 0; - } - else - { - my $num = ++$_appendvar; - my $hvar = "am__append_$num"; - $lastappend = [$hvar, $condstr]; - &define ($hvar, VAR_AUTOMAKE, '+', - $cond, $value, $comment, $where, $pretty); - - $comment = ''; - $value = "\$($hvar)"; - } - } - - foreach my $vcond ($self->conditions->conds) - { - my $undef_cond = $self->not_always_defined_in_cond ($cond); - if (! $undef_cond->false) - { - error ($where, - "cannot apply '+=' because '$var' is not defined " - . "in\nthe following conditions:\n " - . join ("\n ", map { $_->human } $undef_cond->conds) - . "\neither define '$var' in these conditions," - . " or use\n'+=' in the same conditions as" - . " the definitions."); - } - else - { - &define ($var, $owner, '+', $vcond, $value, $comment, - $where, $pretty); - } - } - $self->{'last-append'} = $lastappend; - # We return the variables we modified - return ($self, $var, $value); -} - -# _first_assign_var ($sefl, $var, $cond, $owner, $where, $def, $value, $pretty, $comment, $new_var, $type) -# --------------------------------------------------- -# Method that assign a value to a variable for the first time or for -# total redefinition of an Automake variable or an AC_SUBST variable for -# an existing condition. - -sub _first_assign_var ($$$$$$$$$$$) -{ - my ($self, $var, $cond, $owner, $where, $def, $value, $pretty, $comment, $new_var, $type) = @_; - _check_ambiguous_condition ($self, $cond, $where) - unless (!$new_var - && (($def->owner == VAR_AUTOMAKE && $owner != VAR_AUTOMAKE) - || $def->owner == VAR_CONFIGURE)); - - # Never decrease an owner. - $owner = $def->owner - if ! $new_var && $owner < $def->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->clone, - $type, $owner, $pretty); - $self->set ($cond, $def); - push @_var_order, $var; -} - -# _am_check_definitions ($self, $var, $cond, $def, $value, $type, $where) -# ---------- -# Additional checks for Automake definitions -sub _am_check_definitions ($$$$$$$) -{ - my ($self, $var, $cond, $def, $value, $type, $where) = @_; - # An Automake variable must be consistently defined with the same - # sign by Automake. - if ($def->type ne $type && $def->owner == VAR_AUTOMAKE) - { - error ($def->location, - "Automake variable '$var' was set with '" - . $def->type . "=' here ...", partial => 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->owner != VAR_AUTOMAKE) - { - if (! exists $_silent_variable_override{$var}) - { - my $condmsg = ($cond == TRUE - ? '' : (" in condition '" . $cond->human . "'")); - msg_cond_var ('override', $cond, $var, - "user variable '$var' defined here$condmsg ...", - partial => 1); - msg ('override', $where, - "... overrides Automake variable '$var' defined here"); - } - verb ("refusing to override the user definition of:\n" - . $self->dump ."with '" . $cond->human . "' => '$value'"); - return -1; - } - return 0; -} =item C Define or append to a new variable. @@ -1039,32 +886,136 @@ sub define ($$$$$$$$) # Additional checks for Automake definitions. if ($owner == VAR_AUTOMAKE && ! $new_var) { - return if (-1 == _am_check_definitions ($self, $var, $cond, $def, $value, $type, - $where)); + am_check_definitions ($var, $cond, $def, $type, $where); + # If Automake tries to override a value specified by the user, + # just don't let it do. + if ($def->owner != VAR_AUTOMAKE) + { + if (! exists $_silent_variable_override{$var}) + { + my $condmsg = ($cond == TRUE + ? '' : (" in condition '" . $cond->human . "'")); + msg_cond_var ('override', $cond, $var, + "user variable '$var' defined here$condmsg ...", + partial => 1); + msg ('override', $where, + "... overrides Automake variable '$var' defined here"); + } + verb ("refusing to override the user definition of:\n" + . $self->dump ."with '" . $cond->human . "' => '$value'"); + return; + } } - # Differentiate assignment types. # 1. append (+=) to a variable defined for current condition if ($type eq '+' && ! $new_var) { - _append_var_cur_cond ($self, $var, $owner, $where, $def, $value, + append_var_cur_cond ($self, $var, $owner, $where, $def, $value, $comment); } # 2. append (+=) to a variable defined for *another* condition elsif ($type eq '+' && ! $self->conditions->false) { - # We get back some variables that will be needed later - ($var, $value) = _append_var_other_cond ($self, $var, $cond, - $owner, $where, $def, - $value, $pretty, - $comment, $new_var); + # * 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->string; + my $key = "$var:$condstr"; + my ($appendvar, $appendvarcond) = @{$self->{'last-append'}}; + if ($appendvar && $condstr eq $appendvarcond) + { + # Yes, let's simply append to it. + $var = $appendvar; + $owner = VAR_AUTOMAKE; + $self = var ($var); + $def = $self->rdef ($cond); + $new_var = 0; + } + else + { + # No, create it. + my $num = ++$_appendvar; + my $hvar = "am__append_$num"; + $lastappend = [$hvar, $condstr]; + &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->conditions->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->not_always_defined_in_cond ($cond); + if (! $undef_cond->false) + { + error ($where, + "cannot apply '+=' because '$var' is not defined " + . "in\nthe following conditions:\n " + . join ("\n ", map { $_->human } $undef_cond->conds) + . "\neither define '$var' in these conditions," + . " or use\n'+=' in the same conditions as" + . " the definitions."); + } + else + { + &define ($var, $owner, '+', $vcond, $value, $comment, + $where, $pretty); + } + } + $self->{'last-append'} = $lastappend; } # 3. first assignment (=, :=, or +=) else { - _first_assign_var ($self, $var, $cond, $owner, $where, $def, - $value, $pretty, $comment, $new_var, $type); + _check_ambiguous_condition ($self, $cond, $where) + unless (!$new_var + && (($def->owner == VAR_AUTOMAKE && $owner != VAR_AUTOMAKE) + || $def->owner == VAR_CONFIGURE)); + + first_assign_var ($self, $var, $cond, $owner, $where, $def, + $value, $pretty, $comment, $new_var, $type); + push @_var_order, $var; } # Call any defined hook. This helps to update some internal state diff --git a/lib/Automake/local.mk b/lib/Automake/local.mk index a0b2ea462..16c56d819 100644 --- a/lib/Automake/local.mk +++ b/lib/Automake/local.mk @@ -41,6 +41,7 @@ dist_perllib_DATA = \ %D%/Rule.pm \ %D%/RuleDef.pm \ %D%/Utils.pm \ + %D%/VarAppend.pm \ %D%/Variable.pm \ %D%/VarDef.pm \ %D%/Version.pm \ -- cgit v1.2.1