# Only recognize leading spaces, not leading tabs. If we recognize
# leading tabs here then we need to make the reader smarter, because
# otherwise it will think rules like `foo=bar; \' are errors.
-my $MACRO_PATTERN = '^[A-Za-z0-9_@]+' . "\$";
+my $MACRO_PATTERN = '^[.A-Za-z0-9_@]+' . "\$";
my $ASSIGNMENT_PATTERN = '^ *([^ \t=:+]*)\s*([:+]?)=\s*(.*)' . "\$";
# This pattern recognizes a Gnits version id and sets $1 if the
# release is an alpha release. We also allow a suffix which can be
# var_SUFFIXES_trigger ($TYPE, $VALUE)
# ------------------------------------
-# This is called automagically by define_macro() when SUFFIXES
+# This is called automagically by macro_define() when SUFFIXES
# is defined ($TYPE eq '') or appended ($TYPE eq '+').
# The work here needs to be performed as a side-effect of the
-# define_macro() call because SUFFIXES definitions impact
+# macro_define() call because SUFFIXES definitions impact
# on $KNOWN_EXTENSIONS_PATTERN, and $KNOWN_EXTENSIONS_PATTERN
# are used when parsing the input am file.
sub var_SUFFIXES_trigger ($$)
}
}
-# See if any _SOURCES variable were misspelled. Also, make sure that
-# EXTRA_ variables don't contain configure substitutions.
+# See if any _SOURCES variable were misspelled.
sub check_typos ()
{
# It is ok if the user sets this particular variable.
"$var: variable names starting with `_' are not portable")
if $var =~ /^_/;
+ check_variable_expansions ($value, $where);
+
$cond ||= 'TRUE';
# An Automake variable must be consistently defined with the same
return 0;
}
+# @LIST
+# &scan_variable_expansions ($TEXT)
+# ---------------------------------
+# Return the list of variable names expanded in $TEXT.
+# Note that unlike some other functions, $TEXT is not split
+# on spaces before we check for subvariables.
+sub scan_variable_expansions ($)
+{
+ my ($text) = @_;
+ my @result = ();
+
+ # Strip comments.
+ $text =~ s/#.*$//;
+
+ # Record each use of ${stuff} or $(stuff) that do not follow a $.
+ while ($text =~ /(?<!\$)\$(?:\{([^\}]*)\}|\(([^\)]*)\))/g)
+ {
+ my $var = $1 || $2;
+ # The occurent may look like $(string1[:subst1=[subst2]]) but
+ # we want only `string1'.
+ $var =~ s/:[^:=]*=[^=]*$//;
+ push @result, $var;
+ }
+ return @result;
+}
+
+# &check_variable_expansions ($TEXT, $WHERE)
+# ------------------------------------------
+# Check variable expansions in $TEXT and warn about any name that
+# does not conform to POSIX. $WHERE is the location of $TEXT for
+# the error message.
+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 !~ /$MACRO_PATTERN/)
+ {
+ # 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 =~ / /;
+ msg ('portability', $where,
+ "$var: non-POSIX variable name$gnuext");
+ }
+ }
+}
# &variable_conditions_recursive_sub ($VAR, $PARENT)
# -------------------------------------------------------
# Examine every condition under which $VAR is defined.
foreach my $vcond (keys %{$var_value{$var}})
{
- push (@this_conds, $vcond);
+ push (@this_conds, $vcond);
- # If $VAR references some other variable, then compute the
- # conditions for that subvariable.
- my @subvar_conds = ();
- foreach (split (' ', $var_value{$var}{$vcond}))
+ # If $VAR references some other variable, then compute the
+ # conditions for that subvariable.
+ my @subvar_conds = ();
+ foreach my $varname (scan_variable_expansions $var_value{$var}{$vcond})
{
- # If a comment seen, just leave.
- last if /^#/;
-
- # Handle variable substitutions.
- if (/^\$\{(.*)\}$/ || /^\$\((.*)\)$/)
+ if ($varname =~ /$SUBST_REF_PATTERN/o)
{
- my $varname = $1;
- if ($varname =~ /$SUBST_REF_PATTERN/o)
- {
- $varname = $1;
- }
-
+ $varname = $1;
+ }
- # Here we compute all the conditions under which the
- # subvariable is defined. Then we go through and add
- # $VCOND to each.
- 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);
- }
+ # Here we compute all the conditions under which the
+ # subvariable is defined. Then we go through and add
+ # $VCOND to each.
+ 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);
}
}
- # If there are no conditional subvariables, then we want to
- # return this condition. Otherwise, we want to return the
- # permutations of the subvariables, taking into account the
- # conditions of $VAR.
- if (! @subvar_conds)
+ # If there are no conditional subvariables, then we want to
+ # return this condition. Otherwise, we want to return the
+ # permutations of the subvariables, taking into account the
+ # conditions of $VAR.
+ if (! @subvar_conds)
{
- push (@new_conds, $vcond);
+ push (@new_conds, $vcond);
}
- else
+ else
{
- push (@new_conds, variable_conditions_reduce (@subvar_conds));
+ push (@new_conds, variable_conditions_reduce (@subvar_conds));
}
}
$prev_state = IN_RULE_DEF;
rule_define ($1, 0, $cond, $here);
+ check_variable_expansions ($_, $here);
$output_trailer .= $comment . $spacing;
$output_trailer .= &make_condition (@cond_stack);
# This isn't an error; it is probably a continued rule.
# In fact, this is what we assume.
$prev_state = IN_RULE_DEF;
+ check_variable_expansions ($_, $here);
$output_trailer .= $comment . $spacing;
$output_trailer .= &make_condition (@cond_stack);
$output_trailer .= $_;
--- /dev/null
+#! /bin/sh
+
+# Check that Automake warns about variables containing spaces
+# and other non-POSIX characters.
+
+. $srcdir/defs || exit 1
+
+set -e
+
+cat >Makefile.am <<'EOF'
+L01 = $(shell echo *)
+L02 = $$(not an error)
+L03 = $$(this is)$${ok too}
+L04 = $(nextvariableisbad)$(addsuffix .a, $(A))
+L05 = "$(bad boy)"
+L06 = $(this:is= ok)
+L07 = ${three errors}${on this} $(long line)
+L08$(o u c h): $(wildcard *.c)
+ ${another error}
+ echo $${ok-this is}
+L11: $(thisis) $(ok)
+ ${here}
+EOF
+
+$ACLOCAL
+# Make sure this warning is print in the `portability' category.
+$AUTOMAKE --warnings=no-error,none,portability 2>stderr
+cat stderr
+
+# Lines number are printed in error message.
+# Use them to make sure errors are diagnosed against the right lines.
+
+# No error expected for these lines.
+grep 1: stderr
+grep 2: stderr && exit 1
+grep 3: stderr && exit 1
+grep 4: stderr
+grep 5: stderr
+grep 6: stderr && exit 1
+grep 7: stderr
+grep 8: stderr
+grep 9: stderr
+grep 10: stderr && exit 1
+grep 11: stderr && exit 1
+grep 12: stderr && exit 1
+
+# Now check some individual values.
+grep 'shell echo' stderr
+grep 'nextvariableisbad' stderr && exit 1
+grep 'addsuffix' stderr
+grep 'bad boy' stderr
+grep 'ok' stderr && exit 1
+grep 'three errors' stderr
+grep 'on this' stderr
+grep 'long line' stderr
+grep 'o u c h' stderr
+grep 'wildcard' stderr
+grep 'another error' stderr
+grep 'thisis' stderr && exit 1
+grep 'here' stderr && exit 1
+
+# None of these errors be diagnosed with -Wno-portability
+$AUTOMAKE -Wno-portability
+
+# Likewise if we add this in the Makefile.am
+# (although this makes some difference internally: AUTOMAKE_OPTIONS is
+# processed far later).
+echo 'AUTOMAKE_OPTIONS = -Wno-portability' >> Makefile.am
+$AUTOMAKE