diff options
author | Zack Weinberg <zackw@panix.com> | 2020-11-15 13:56:18 -0500 |
---|---|---|
committer | Zack Weinberg <zackw@panix.com> | 2020-11-15 14:16:16 -0500 |
commit | fd633e92cb63f7bb592dcffd2f2871fa28ab189d (patch) | |
tree | 497c04f7446f90867b334b8adbb81e5e5d89f10c /lib | |
parent | 996f608165814d578bdcb0ab2f3382edb8918690 (diff) | |
download | autoconf-fd633e92cb63f7bb592dcffd2f2871fa28ab189d.tar.gz |
AS_IF: Handle else clause being empty after macro expansion (#110369)
AS_IF can emit a syntactically invalid shell if-then-else,
if CONDITION
then :
# ...
else
fi
when its IF-FALSE argument consists of macros that don’t produce any
shell code. This was a documented limitation in AS_IF, but it’s a bad
limitation to have, because macros that *used* to expand to shell
commands might start expanding to nothing in future releases. For
instance, this broke the libzmq configure script, which did
AC_PROG_CC
AX_CHECK_COMPILE_FLAG([-std=gnu11],
[CFLAGS+=" -std=gnu11"],
[AC_PROG_CC_C99])
Perfectly valid in 2.69, but in 2.70 AC_PROG_CC_C99 doesn’t produce
any shell code and the script crashes.
We had that limitation for good reason: we can’t just put ‘:’ at the
beginning of the else-clause, like we do for the then-clause, because
that would clobber $? and the IF-FALSE commands might want to inspect
it. (This doesn’t matter for the then-clause, because $? is always
zero at the beginning of a then-clause anyway.) The simplest and
least inefficient shell construct I can find that works in this
context is a shell function that does ‘return $?’. Due to awkward
M4sh initialization ordering constraints (AS_IF gets used before we
can safely use shell functions) an indirection through a shell
variable is necessary. The structure of a m4sh script is now
#! /bin/sh
## M4sh Initialization
as_nop=:
...
## M4sh Shell Functions
as_fn_nop () { return $?; }
as_nop=as_fn_nop
...
and AS_IF emits
if CONDITION
then :
# ...
else $as_nop
# ...
fi
The uses of AS_IF that appear before the beginning of the M4sh Shell
Functions section are all under our control and they don’t need to
look at $?.
If anyone has a better idea for how to make this work I will be glad
to hear it.
Fixes bug #110369.
* lib/m4sugar/m4sh.m4
(_AS_IF_ELSE): When $1 is nonempty, invoke _AS_EMPTY_ELSE_PREPARE.
Emit $as_nop at beginning of else clause.
(_AS_BOURNE_COMPATIBLE): Initialize as_nop to ‘:’.
(_AS_EMPTY_ELSE_PREPARE): New macro which emits a definition of
as_fn_nop and resets as_nop to as_fn_nop.
(AS_PREPARE, _AS_PREPARE): Invoke _AS_EMPTY_ELSE_PREPARE.
(_AS_UNSET_PREPARE): Tweak white space.
* tests/m4sh.at (AS_IF and AS_CASE): Test AS_IF’s IF-FALSE argument
being empty after macro expansion.
* doc/autoconf.texi (AS_IF): Remove warning about use with
‘run-if-false’ argument empty after macro expansion.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/m4sugar/m4sh.m4 | 39 |
1 files changed, 33 insertions, 6 deletions
diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4 index 8f96eb7b..81b3f82e 100644 --- a/lib/m4sugar/m4sh.m4 +++ b/lib/m4sugar/m4sh.m4 @@ -98,9 +98,10 @@ _$0 # _AS_BOURNE_COMPATIBLE # --------------------- # This is the part of AS_BOURNE_COMPATIBLE which has to be repeated inside -# each instance. +# each instance. See _AS_EMPTY_ELSE_PREPARE for explanation of as_nop. m4_define([_AS_BOURNE_COMPATIBLE], -[AS_IF([test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1], +[as_nop=: +AS_IF([test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1], [emulate sh NULLCMD=: [#] Pre-4.2 versions of Zsh do word splitting on ${1+"$[@]"}, which @@ -351,7 +352,8 @@ m4_defun([_AS_PREPARE], [m4_pushdef([AS_MESSAGE_LOG_FD], [-1])]dnl [_AS_ERROR_PREPARE _m4_popdef([AS_MESSAGE_LOG_FD])]dnl -[_AS_EXIT_PREPARE +[_AS_EMPTY_ELSE_PREPARE +_AS_EXIT_PREPARE _AS_UNSET_PREPARE _AS_VAR_APPEND_PREPARE _AS_VAR_ARITH_PREPARE @@ -387,6 +389,7 @@ AS_REQUIRE([_AS_CR_PREPARE]) AS_REQUIRE([_AS_LINENO_PREPARE]) AS_REQUIRE([_AS_ECHO_N_PREPARE]) AS_REQUIRE([_AS_EXIT_PREPARE]) +AS_REQUIRE([_AS_EMPTY_ELSE_PREPARE]) AS_REQUIRE([_AS_LN_S_PREPARE]) AS_REQUIRE([_AS_MKDIR_P_PREPARE]) AS_REQUIRE([_AS_TEST_PREPARE]) @@ -669,14 +672,27 @@ done[]_m4_popdef([$1])]) # | fi # with simplifications when IF-TRUE1 and/or IF-FALSE are empty. # +# Note: IF-TRUEn and IF_FALSE may be nonempty but, after further macro +# expansion, leave no actual shell code. We can't detect this, so we +# include a no-op statement in each clause to prevent it becoming a shell +# syntax error. For the IF-TRUEn this can simply be `:' at the beginning of +# the clause. IF-FALSE is harder because it must preserve the value of $? +# from the conditional expression. The most practical way to do this is +# with a shell function whose body is `return $?' but AS_IF is used before +# it's safe to use shell functions. To deal with *that*, there is a shell +# variable $as_fn_nop that expands to `:' before the nop shell function is +# defined, and invokes the nop shell function afterward. Early uses of +# AS_IF (which are all under our control) must not use the value of $? from +# the conditional expression in an else clause. m4_define([_AS_IF], [elif $1 then : $2 ]) -m4_define([_AS_IF_ELSE], +m4_defun([_AS_IF_ELSE], [m4_ifnblank([$1], -[else +[m4_append_uniq([_AS_CLEANUP], [AS_REQUIRE([_AS_EMPTY_ELSE_PREPARE])])]dnl +[else $as_nop $1 ])]) @@ -687,6 +703,16 @@ then : m4_map_args_pair([_$0], [_$0_ELSE], m4_shift2($@))]dnl [fi[]])# AS_IF +m4_defun([_AS_EMPTY_ELSE_PREPARE], +[m4_divert_text([M4SH-INIT-FN], +[AS_FUNCTION_DESCRIBE([as_fn_nop], [], + [Do nothing but, unlike ":", preserve the value of $][?.]) +as_fn_nop () +{ + return $[]? +} +as_nop=as_fn_nop])]) + # AS_SET_STATUS(STATUS) # --------------------- @@ -705,7 +731,8 @@ as_fn_unset () { AS_UNSET([$[1]]) } -as_unset=as_fn_unset]) +as_unset=as_fn_unset +]) # AS_UNSET(VAR) |