After a long thread crossing between gnulib and bug-autoconf, here's the patch that I am applying which improves AC_DEFUN_ONCE semantics. I'm now in the process of filtering through the list of macros which turn up during "git grep @defmac'[^(]*$' doc", to see which of them should be converted to AC_DEFUN_ONCE.
The new definition of m4_defun_once (which is an alias to AC_DEFUN_ONCE) used in this patch exploits internal knowledge of m4sugar primitives for optimal m4 speed; the version of the patch that I am submitting to gnulib does the same thing, but with a bit more overhead, because it only uses public API available since at least 2.59. From: Eric Blake <[email protected]> Date: Mon, 26 Jan 2009 09:55:57 -0700 Subject: [PATCH] Improve AC_DEFUN_ONCE semantics. * lib/m4sugar/m4sugar.m4 (m4_defun_once): Rewrite to be no-op, rather than warning, on second use, and make sure first use never occurs out of order. * tests/m4sugar.at (m4@&t...@_require: one-shot initialization): Enhance test. * tests/base.at (AC_REQUIRE & AC_DEFUN_ONCE: [Require, expand], (AC_REQUIRE & AC_DEFUN_ONCE: [Expand, require]): Adjust tests. * NEWS: Document this. * doc/autoconf.texi (Macro Definitions) <AC_DEFUN>: Mention AC_DEFUN_ONCE. (Prerequisite Macros) <AC_REQUIRE>: Likewise. (Expanded Before Required): Likewise. (One-Shot Macros) <AC_DEFUN_ONCE>: Document new semantics. Reported by Bruno Haible, with suggestion by Paolo Bonzini. Signed-off-by: Eric Blake <[email protected]> --- ChangeLog | 18 ++++++++++++++++++ NEWS | 6 ++++++ doc/autoconf.texi | 31 +++++++++++++++++++++---------- lib/m4sugar/m4sugar.m4 | 17 ++++++++++------- tests/base.at | 18 +++++++++--------- tests/m4sugar.at | 30 +++++++++++++++++++++++++++++- 6 files changed, 93 insertions(+), 27 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3881591..e7d833f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,21 @@ +2009-01-26 Eric Blake <[email protected]> + + Improve AC_DEFUN_ONCE semantics. + * lib/m4sugar/m4sugar.m4 (m4_defun_once): Rewrite to be no-op, + rather than warning, on second use, and make sure first use never + occurs out of order. + * tests/m4sugar.at (m4@&t...@_require: one-shot initialization): + Enhance test. + * tests/base.at (AC_REQUIRE & AC_DEFUN_ONCE: [Require, expand], + (AC_REQUIRE & AC_DEFUN_ONCE: [Expand, require]): Adjust tests. + * NEWS: Document this. + * doc/autoconf.texi (Macro Definitions) <AC_DEFUN>: Mention + AC_DEFUN_ONCE. + (Prerequisite Macros) <AC_REQUIRE>: Likewise. + (Expanded Before Required): Likewise. + (One-Shot Macros) <AC_DEFUN_ONCE>: Document new semantics. + Reported by Bruno Haible, with suggestion by Paolo Bonzini. + 2009-01-24 Eric Blake <[email protected]> Fix typos in recent patches. diff --git a/NEWS b/NEWS index 5e43abc..d5c7ed2 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,12 @@ GNU Autoconf NEWS - User visible changes. the bug in earlier autoconf versions and avoid increased script size in the current version. +** AC_DEFUN_ONCE has improved semantics. Previously, a macro declared + with AC_DEFUN_ONCE warned on a second invocation; and out-of-order + expansion was still possible. Now, dependencies are guaranteed, + and subsequent invocations are a silent no-op. This makes + AC_DEFUN_ONCE an ideal macro for silencing AC_REQUIRE warnings. + ** AC_LANG_ERLANG works once again (regression introduced in 2.61a). ** AC_HEADER_ASSERT is fixed so that './configure --enable-assert' no diff --git a/doc/autoconf.texi b/doc/autoconf.texi index 4857eeb..fc7d42b 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -12830,7 +12830,8 @@ Macro Definitions which are shorthand for a fixed block of text, and therefore do not take arguments. For this category of macros, directly expanding the macro multiple times results in redundant output, so it is more common to use -the macro as the argument to @code{AC_REQUIRE}. +the macro as the argument to @code{AC_REQUIRE}, or to declare the macro +with @code{AC_DEFUN_ONCE} (@pxref{One-Shot Macros}). Be sure to properly quote both the @var{macro-body} @emph{and} the @var{macro-name} to avoid any problems if the macro happens to have @@ -13171,7 +13172,9 @@ Prerequisite Macros The bug is not in Autoconf, but in the macro definitions. If you ever pass a particular macro name to @code{AC_REQUIRE}, then you are implying -that the macro only needs to be expanded once. But to enforce this, all +that the macro only needs to be expanded once. But to enforce this, +either the macro must be declared with @code{AC_DEFUN_ONCE} (although +this only helps in Autoconf 2.64 or newer), or all uses of that macro should be through @code{AC_REQUIRE}; directly expanding the macro defeats the point of using @code{AC_REQUIRE} to eliminate redundant expansion. In the example, this rule of thumb was @@ -13271,16 +13274,21 @@ One-Shot Macros @defmac AC_DEFUN_ONCE (@var{macro-name}, @var{macro-body}) @acindex{DEFUN_ONCE} - Declare macro @var{macro-name} like @code{AC_DEFUN} would (@pxref{Macro -Definitions}), and emit a warning any time the macro is called more than -once. +Definitions}), but add additional logic that guarantees that only the +first use of the macro (whether by direct expansion or +...@code{ac_require}) causes an expansion of @var{macro-body}; the +expansion will occur before the start of any enclosing macro defined by +...@code{ac_defun}. Subsequent expansions are silently ignored. +Generally, it does not make sense for @var{macro-body} to use parameters +such as @code{$1}. @end defmac -Obviously it is not sensible to evaluate a macro defined by -...@code{ac_defun_once} in a macro defined by @code{AC_DEFUN}. -Most of the time you want to use @code{AC_REQUIRE} (@pxref{Prerequisite -Macros}). +Prior to Autoconf 2.64, a macro defined by @code{AC_DEFUN_ONCE} would +emit a warning if it was directly expanded a second time, so for +portability, it is better to use @code{AC_REQUIRE} than direct +invocation of @var{macro-name} inside a macro defined by @code{AC_DEFUN} +(@pxref{Prerequisite Macros}). @node Obsoleting Macros @section Obsoleting Macros @@ -22867,7 +22875,10 @@ Expanded Before Required @noindent To avoid this warning, decide what purpose the macro in question serves. If it only needs to be expanded once (for example, if it provides -initialization text used by later macros), then the fix is to change all +initialization text used by later macros), then the simplest fix is to +change the macro to be declared with @code{AC_DEFUN_ONCE} +(@pxref{One-Shot Macros}), although this only works in Autoconf 2.64 and +newer. A more portable fix is to change all instances of direct calls to instead go through @code{AC_REQUIRE} (@pxref{Prerequisite Macros}). If, instead, the macro is parameterized by arguments or by the current definition of other macros in the m4 diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4 index df051a6..2ea42d8 100644 --- a/lib/m4sugar/m4sugar.m4 +++ b/lib/m4sugar/m4sugar.m4 @@ -1914,14 +1914,17 @@ m4_define([m4_defun_init], # m4_defun_once(NAME, EXPANSION) # ------------------------------ -# Like m4_defun, but issues the EXPANSION only once, and warns if used -# several times. +# Like m4_defun, but guarantee that EXPANSION only happens once +# (thereafter, using NAME is a no-op). +# +# If _m4_divert_dump is empty, we are called at the top level; +# otherwise, we must ensure that we are required in front of the +# current defun'd macro. m4_define([m4_defun_once], -[m4_define([m4_location($1)], m4_location)dnl -m4_define([$1], - [m4_provide_if([$1], - [m4_warn([syntax], [$1 invoked multiple times])], - [_m4_defun_pro([$1])$2[]_m4_defun_epi([$1])])])]) +[m4_define([m4_location($1)], m4_location)]dnl +[m4_define([$1], [m4_pushdef([$1])m4_if(_m4_divert_dump, [], + [_m4_defun_pro([$1])$2[]_m4_defun_epi([$1])], + [_m4_require_call([$1], [$2[]m4_provide([$1])], _m4_divert_dump)])])]) # m4_pattern_forbid(ERE, [WHY]) diff --git a/tests/base.at b/tests/base.at index 1ed8e1c..b5ee30f 100644 --- a/tests/base.at +++ b/tests/base.at @@ -2,8 +2,8 @@ AT_BANNER([Autoconf base layer.]) -# Copyright (C) 2000, 2001, 2003, 2005, 2006, 2007, 2008 Free Software -# Foundation, Inc. +# Copyright (C) 2000, 2001, 2003, 2005, 2006, 2007, 2008, 2009 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 @@ -28,6 +28,7 @@ AT_BANNER([Autoconf base layer.]) # Check that dependencies are always properly honored. AT_SETUP([AC_REQUIRE: topological sort]) +AT_KEYWORDS([m4@&t...@_require]) AT_DATA([configure.ac], [[define([REQUIRE_AND_CHECK], @@ -71,6 +72,7 @@ AT_CLEANUP # Check that the message mentions AC_DEFUN, not m4_defun. AT_SETUP([AC_REQUIRE: error message]) +AT_KEYWORDS([m4@&t...@_require]) AT_DATA([configure.ac], [[AC_REQUIRE([AC_PROG_CC]) ]]) @@ -88,6 +90,7 @@ AT_CLEANUP ## ----------------------------------------------- ## AT_SETUP([AC_REQUIRE & AC_DEFUN_ONCE: [Require, expand]]) +AT_KEYWORDS([m4@&t...@_require m4@&t...@_require_once]) AT_DATA([configure.ac], [[AC_DEFUN([TEST], @@ -116,10 +119,7 @@ case $multi_test:$single_test in esac ]]) -AT_CHECK_AUTOCONF([], 0, [], -[configure.ac:17: warning: SINGLE_TEST invoked multiple times -configure.ac:18: warning: SINGLE_TEST invoked multiple times -]) +AT_CHECK_AUTOCONF([], 0, []) AT_CHECK_CONFIGURE @@ -132,6 +132,7 @@ AT_CLEANUP ## ----------------------------------------------- ## AT_SETUP([AC_REQUIRE & AC_DEFUN_ONCE: [Expand, require]]) +AT_KEYWORDS([m4@&t...@_require m4@&t...@_require_once]) AT_DATA([configure.ac], [[AC_DEFUN([TEST], @@ -161,9 +162,7 @@ case $multi_test:$single_test in esac ]]) -AT_CHECK_AUTOCONF([], 0, [], -[configure.ac:16: warning: SINGLE_TEST invoked multiple times -]) +AT_CHECK_AUTOCONF([], 0, []) AT_CHECK_CONFIGURE AT_CLEANUP @@ -175,6 +174,7 @@ AT_CLEANUP ## ------------------------- ## AT_SETUP([AC_REQUIRE & AC_PROVIDE]) +AT_KEYWORDS([m4@&t...@_require]) AT_DATA([configure.ac], [[AC_DEFUN([TEST], diff --git a/tests/m4sugar.at b/tests/m4sugar.at index 22e7a07..99d8eab 100644 --- a/tests/m4sugar.at +++ b/tests/m4sugar.at @@ -430,8 +430,9 @@ AT_CLEANUP AT_SETUP([m4@&t...@_require: one-shot initialization]) AT_KEYWORDS([m4@&t...@_require]) -AT_KEYWORDS([m4@&t...@_defun_init m4@&t...@_copy]) +AT_KEYWORDS([m4@&t...@_defun_init m4@&t...@_copy m4@&t...@_defun_once]) +dnl check out m4_defun_init, m4_copy, and odd macro names AT_CHECK_M4SUGAR_TEXT([[ m4_defun_init([a], [[init a ]], [[common a]])dnl @@ -455,6 +456,33 @@ hello, world goodbye hello, again ]]) + +dnl Check m4_defun_once behavior +AT_CHECK_M4SUGAR_TEXT([[ +m4_defun_once([a], [[a]])dnl +m4_defun([b], [[b]m4_require([a])])dnl +m4_defun([c], [[c] +a[]m4_require([b])])dnl +c +a +m4_defun_once([d], [[d]m4_require([a])])dnl +d +m4_defun_once([e], [[e]])dnl +m4_defun([f], [[f]m4_require([e])e])dnl +f +]], [[ +a +b +c + + +d +e + +f +]]) + + AT_CLEANUP -- 1.6.0.4
