autoconf-patches
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Fix an 8-year-old AC_REQUIRE bug


From: Eric Blake
Subject: Re: Fix an 8-year-old AC_REQUIRE bug
Date: Wed, 31 Dec 2008 00:25:16 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Eric Blake <ebb9 <at> byu.net> writes:

> Here's my first draft of a patch.  Unfortunately, there is probably existing 
> code that was expecting the old buggy behavior.

And here's my proposed patch that fixes those, by introducing a new macro that 
states when we want the old behavior (AS_IF, AS_CACHE_VAL, and maybe a small 
handful of other places) instead of the new.  As a bonus, wrapping text in 
AC_REQUIRE_HOIST issues a warning on code that was previously triggering the 
buggy AC_REQUIRE behaivior.


From: Eric Blake <address@hidden>
Date: Tue, 30 Dec 2008 17:18:42 -0700
Subject: [PATCH] Introduce m4_require_hoist/AC_REQUIRE_HOIST.

* lib/m4sugar/m4sugar.m4 (m4_require_hoist): New macro.
(m4_require, m4_provide): Use it to restore some measure of
backwards compatibility, when needed, but with safety checking.
* lib/autoconf/general.m4 (AC_REQUIRE_HOIST): New macro.
(AC_CACHE_VAL, AC_CACHE_CHECK): Perform prequisite hoisting.
* lib/m4sugar/m4sh.m4 (AS_CASE, AS_FOR, AS_IF): Likewise.
* doc/autoconf.texi (Prerequisite Macros) <AC_REQUIRE_HOIST>:
Document new macro.
* doc/autoconf.texi (Suggested Ordering) <AC_PROVIDE>: Document
existing macro.
* NEWS: Document this.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog               |   13 +++++++++
 NEWS                    |   11 +++++++-
 doc/autoconf.texi       |   70 +++++++++++++++++++++++++++++++++++++++++++++++
 lib/autoconf/general.m4 |   31 +++++++++++++-------
 lib/m4sugar/m4sh.m4     |   23 +++++++++++----
 lib/m4sugar/m4sugar.m4  |   25 +++++++++++++++--
 6 files changed, 152 insertions(+), 21 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index e5f3872..e114407 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2008-12-30  Eric Blake  <address@hidden>
 
+       Introduce m4_require_hoist/AC_REQUIRE_HOIST.
+       * lib/m4sugar/m4sugar.m4 (m4_require_hoist): New macro.
+       (m4_require, m4_provide): Use it to restore some measure of
+       backwards compatibility, when needed, but with safety checking.
+       * lib/autoconf/general.m4 (AC_REQUIRE_HOIST): New macro.
+       (AC_CACHE_VAL, AC_CACHE_CHECK): Perform prequisite hoisting.
+       * lib/m4sugar/m4sh.m4 (AS_CASE, AS_FOR, AS_IF): Likewise.
+       * doc/autoconf.texi (Prerequisite Macros) <AC_REQUIRE_HOIST>:
+       Document new macro.
+       * doc/autoconf.texi (Suggested Ordering) <AC_PROVIDE>: Document
+       existing macro.
+       * NEWS: Document this.
+
        Fix output location of m4_defun->m4_defun->m4_require.
        This patch fails the testsuite, due to changed hoisting behavior.
        * lib/m4sugar/m4sugar.m4 (Defining macros): Update comments to
diff --git a/NEWS b/NEWS
index 99da48e..50cbd67 100644
--- a/NEWS
+++ b/NEWS
@@ -36,7 +36,10 @@ GNU Autoconf NEWS - User visible changes.
    This gives `A B PRE C POST' in older versions, and `PRE A B C POST'
    in this version; but since A and PRE don't have any dependency
    relation, both outputs should be equally correct (if they do have a
-   dependency relation, then you need an additional AC_REQUIRE).
+   dependency relation, then you should add AC_REQUIRE([a]) as part of
+   PRE, or wrap the body of outer with the new AC_REQUIRE_HOIST to get
+   the old behavior without the risk of depedencies issued out of
+   order).
 
 ** Autotest testsuites accept an option --jobs[=N] for parallel testing.
 
@@ -53,6 +56,12 @@ GNU Autoconf NEWS - User visible changes.
 ** Autoreconf added aclocal to the set of programs affected by the
    `autoreconf -I dir' option.
 
+** The following autoconf macros are documented now:
+   AC_PROVIDE
+
+** The following autoconf macros are new:
+   AC_REQUIRE_HOIST
+
 ** The following documented m4sugar macros are new:
    m4_chomp  m4_curry  m4_default_quoted  m4_esyscmd_s  m4_map_args
    m4_map_args_pair  m4_map_args_sep  m4_map_args_w  m4_set_map
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index d619578..dfc7242 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -13109,6 +13109,63 @@ Prerequisite Macros
 at the beginning of a macro.  You can use @code{dnl} to avoid the empty
 lines they leave.
 
address@hidden AC_REQUIRE_HOIST (@var{text})
address@hidden
+Expands @var{text} as normal, except that the current macro behaves as
+if it called @code{AC_REQUIRE} on the same set of macros required by any
+macros defined by @code{AC_DEFUN} and directly invoked (rather than
+required) during the expansion of @var{text}.  Issue a warning if,
+during the course of @var{text}, a macro is first expanded and later
+required, as that is a likely indicator that dependencies could not be
+honored and the output is out of order.
+
+This macro was implemented in Autoconf 2.64, as part of fixing a bug in
address@hidden  All prior versions behave as if this macro were
+always in effect, except that there is no warning when the
address@hidden bug outputs required macros in the wrong order.  To
+remove the warning, either avoid hoisting required macros, or add
address@hidden(address@hidden)} at a point earlier in @var{text} than
+where @var{macro} is directly expanded.
+
+There are very few places where this macro is actually needed.  One use
+is in the implementation of @code{AS_IF}, where it is desirable to hoist
+the prerequisite macro expansions to occur before the overall if
+statement, rather than hidden inside the branch of the conditional that
+contains the macro call that needed the prerequisite.
address@hidden defmac
+
+Here is an example of how @code{AC_REQUIRE_HOIST} affects output.  With
+Autoconf 2.64 and newer, @code{MACRO1} and @code{MACRO2} have different
+output order; with older Autoconf, the use of a preparation sequence
+allows both invocations to match @code{MACRO2}.
+
address@hidden
+dnl Use this sequence to be portable to earlier autoconf versions.
+m4_ifndef([AC_REQUIRE_HOIST],
+ [m4_define([AC_REQUIRE_HOIST], [$1])])dnl
+AC_DEFUN([A1], [[in A1]])dnl
+AC_DEFUN([B1], [[in B1]AC_REQUIRE([A1])])dnl
+AC_DEFUN([MACRO1], [PRE
+B1
+POST])dnl
+MACRO1
address@hidden
address@hidden
address@hidden
address@hidden
+
+AC_DEFUN([A2], [[in A2]])dnl
+AC_DEFUN([B2], [[in B2]AC_REQUIRE([A2])])dnl
+AC_DEFUN([MACRO2], [AC_REQUIRE_HOIST([PRE
+B2
+POST])])dnl
+MACRO2
address@hidden
address@hidden
address@hidden
address@hidden
address@hidden example
+
 @node Suggested Ordering
 @subsection Suggested Ordering
 @cindex Macros, ordering
@@ -13149,6 +13206,19 @@ Suggested Ordering
 that it has been called.
 @end defmac
 
address@hidden AC_PROVIDE (@var{this-macro-name})
address@hidden
+Declares that @var{this-macro-name} has been provided, which affects the
+behavior of @code{AC_REQUIRE} and @code{AC_BEFORE} when checking for
address@hidden  Normally, this is not needed, since
address@hidden automatically provides the current macro.  But sometimes
+it is desirable to have at most one out of a set of multiple
+initialization options executed; by giving the set a unique name, and
+making sure each initialization option provides that name, then it is
+possible to check that the single name has been provided rather than
+iterating through all the options.
address@hidden defmac
+
 @node One-Shot Macros
 @subsection One-Shot Macros
 @cindex One-shot macros
diff --git a/lib/autoconf/general.m4 b/lib/autoconf/general.m4
index bcf8720..65db81b 100644
--- a/lib/autoconf/general.m4
+++ b/lib/autoconf/general.m4
@@ -171,16 +171,18 @@ m4_copy([m4_divert_pop], [AC_DIVERT_POP])
 # AC_DEFUN(NAME, EXPANSION)
 # AC_DEFUN_ONCE(NAME, EXPANSION)
 # AC_BEFORE(THIS-MACRO-NAME, CALLED-MACRO-NAME)
-# AC_REQUIRE(STRING)
+# AC_REQUIRE(MACRO-NAME)
+# AC_REQUIRE_HOIST(TEXT)
 # AC_PROVIDE(MACRO-NAME)
 # AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
 # -----------------------------------------------------------
-m4_copy([m4_defun],       [AC_DEFUN])
-m4_copy([m4_defun_once],  [AC_DEFUN_ONCE])
-m4_copy([m4_before],      [AC_BEFORE])
-m4_copy([m4_require],     [AC_REQUIRE])
-m4_copy([m4_provide],     [AC_PROVIDE])
-m4_copy([m4_provide_if],  [AC_PROVIDE_IFELSE])
+m4_copy([m4_defun],        [AC_DEFUN])
+m4_copy([m4_defun_once],   [AC_DEFUN_ONCE])
+m4_copy([m4_before],       [AC_BEFORE])
+m4_copy([m4_require],      [AC_REQUIRE])
+m4_copy([m4_require_hoist],[AC_REQUIRE_HOIST])
+m4_copy([m4_provide],      [AC_PROVIDE])
+m4_copy([m4_provide_if],   [AC_PROVIDE_IFELSE])
 
 
 # AC_OBSOLETE(THIS-MACRO-NAME, [SUGGESTION])
@@ -1991,8 +1993,11 @@ rm -f confcache[]dnl
 # AC_CACHE_VAL(CACHE-ID, COMMANDS-TO-SET-IT)
 # ------------------------------------------
 # The name of shell var CACHE-ID must contain `_cv_' in order to get saved.
-# Should be dnl'ed.  Try to catch common mistakes.
+# Should be dnl'ed.  Try to catch common mistakes.  Additionally, hoist
+# any AC_REQUIRE to occur before the cache check, and issue an error if
+# this is not possible due to a missing AC_REQUIRE.
 m4_defun([AC_CACHE_VAL],
+[m4_require_hoist(
 [AS_LITERAL_IF([$1], [m4_if(m4_index(m4_quote($1), [_cv_]), [-1],
                            [AC_DIAGNOSE([syntax],
 [$0($1, ...): suspicious cache-id, must contain _cv_ to be cached])])])dnl
@@ -2007,20 +2012,24 @@ m4_if(m4_index([$2], [AC_SUBST]), [-1], [],
 AS_VAR_SET_IF([$1],
              [_AS_ECHO_N([(cached) ])],
              [$2])
-])
+])])
 
 
 # AC_CACHE_CHECK(MESSAGE, CACHE-ID, COMMANDS)
 # -------------------------------------------
+# Wrap a cache check with MESSAGE and the result of the check.
+# Additionally, hoist any AC_REQUIRE to occur before this macro, or issue
+# an error if it is not possible.
+#
 # Do not call this macro with a dnl right behind.
 m4_defun([AC_CACHE_CHECK],
-[AC_MSG_CHECKING([$1])
+[m4_require_hoist([AC_MSG_CHECKING([$1])
 AC_CACHE_VAL([$2], [$3])dnl
 AS_LITERAL_IF([$2],
              [AC_MSG_RESULT([$$2])],
              [AS_VAR_COPY([ac_res], [$2])
               AC_MSG_RESULT([$ac_res])])dnl
-])
+])])
 
 # _AC_CACHE_CHECK_INT(MESSAGE, CACHE-ID, EXPRESSION,
 #                     [PROLOGUE = DEFAULT-INCLUDES], [IF-FAILS])
diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4
index 358aa81..b042fb3 100644
--- a/lib/m4sugar/m4sh.m4
+++ b/lib/m4sugar/m4sh.m4
@@ -523,6 +523,10 @@ _AS_UNSET_PREPARE
 # realize the impacts of using insufficient m4 quoting.  This macro
 # always provides a default case, to work around a Solaris /bin/sh
 # bug regarding the exit status when no case matches.
+#
+# Additionally, hoist any required macros occuring in the expansion of
+# the arguments to occur prior to the AS_IF expansion, or issue an
+# error alerting the user of a missing AC_REQUIRE.
 m4_define([_AS_CASE],
 [ address@hidden:@(]
   $1[)] m4_default([$2], [:]) ;;])
@@ -531,9 +535,9 @@ m4_define([_AS_CASE_DEFAULT],
   *[)] m4_default([$1], [:]) ;;])
 
 m4_defun([AS_CASE],
-[case $1 in[]m4_map_args_pair([_$0], [_$0_DEFAULT],
+[m4_require_hoist([case $1 in[]m4_map_args_pair([_$0], [_$0_DEFAULT],
    m4_shift(address@hidden(m4_eval([$# & 1]), [1], [,])))
-esac])# AS_CASE
+esac])])# AS_CASE
 
 
 # _AS_EXIT_PREPARE
@@ -581,6 +585,10 @@ m4_defun([AS_EXIT],
 # that element, thus providing a direct value rather than a shell
 # variable indirection.
 #
+# Additionally, hoist any required macros occuring in the expansion of
+# the arguments to occur prior to the AS_IF expansion, or issue an
+# error alerting the user of a missing AC_REQUIRE.
+#
 # Only use the optimization if LIST can be used without additional
 # shell quoting in either a literal or double-quoted context (that is,
 # we give up on default IFS chars, parameter expansion, command
@@ -589,10 +597,10 @@ m4_defun([AS_EXIT],
 m4_defun([AS_FOR],
 [m4_pushdef([$1], m4_if([$3], [], [[$$2]], m4_translit([$3], ]dnl
 m4_dquote(_m4_defn([m4_cr_symbols2]))[[%+=:,./-]), [], [[$3]], [[$$2]]))]dnl
-[for $2[]m4_ifval([$3], [ in $3])
+[m4_require_hoist([for $2[]m4_ifval([$3], [ in $3])
 do
   m4_default([$4], [:])
-done[]_m4_popdef([$1])])
+done[]_m4_popdef([$1])])])
 
 
 # AS_IF(TEST1, [IF-TRUE1 = :]...[IF-FALSE = :])
@@ -608,6 +616,9 @@ done[]_m4_popdef([$1])])
 # | fi
 # with simplifications if IF-TRUE1 and/or IF-FALSE is empty.
 #
+# Additionally, hoist any required macros occuring in the expansion of
+# the arguments to occur prior to the AS_IF expansion, or issue an
+# error alerting the user of a missing AC_REQUIRE.
 m4_define([_AS_IF],
 [elif $1; then
   m4_default([$2], [:])
@@ -618,10 +629,10 @@ m4_define([_AS_IF_ELSE],
   $1])])
 
 m4_defun([AS_IF],
-[if $1; then
+[m4_require_hoist([if $1; then
   m4_default([$2], [:])
 m4_map_args_pair([_$0], [_$0_ELSE], m4_shift2($@))]dnl
-[fi[]])# AS_IF
+[fi[]])])# AS_IF
 
 
 # AS_SET_STATUS(STATUS)
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 825001c..d30c4ca 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -2104,8 +2104,10 @@ m4_define([m4_require],
 [m4_ifdef([_m4_divert_grow], [],
          [m4_fatal([$0($1): cannot be used outside of an ]dnl
 m4_bmatch([$0], [^AC_], [[AC_DEFUN]], [[m4_defun]])['d macro])])]dnl
-[m4_provide_if([$1], [],
-              [_m4_require_call([$1], [$2], _m4_divert_grow)])])
+[m4_provide_if([$1], [m4_set_contains([_m4_hoist_provide], [$1],
+    [m4_warn([m4_require_hoist: $1 was provided before it was required])])],
+  [_m4_require_call([$1], [$2], m4_ifdef([_m4_hoisting], [_m4_hoisting],
+    [_m4_divert_grow]))m4_set_remove([_m4_hoist_provide], [$1])])])
 
 
 # _m4_require_call(NAME-TO-CHECK, [BODY-TO-EXPAND = NAME-TO-CHECK], DIVERSION)
@@ -2124,6 +2126,22 @@ m4_provide_if([$1], [], [m4_warn([syntax],
 [m4_divert_pop($3)])
 
 
+# m4_require_hoist(TEXT)
+# ----------------------
+# Expand TEXT, except that any m4_require done during TEXT is hoisted
+# to also be a requirement of the current defun'd macro.  In order to
+# prevent subtle bugs, an error is issued if a previously unprovided
+# macro name is first provided and then required within TEXT.
+m4_define([m4_require_hoist],
+[m4_ifdef([_m4_divert_grow], [],
+         [m4_fatal([$0: cannot be used outside of an ]dnl
+m4_if([$0], [m4_require_hoist], [[m4_defun]], [[AC_DEFUN]])['d macro])])]dnl
+[m4_pushdef([_m4_hoisting], m4_ifdef([_m4_hoisting], [_m4_hoisting],
+  [_m4_divert_grow]))$1[]]dnl
+[_m4_popdef([_m4_hoisting])m4_ifdef([_m4_hoisting], [],
+  [m4_set_delete([_m4_hoist_provide])])])
+
+
 # m4_expand_once(TEXT, [WITNESS = TEXT])
 # --------------------------------------
 # If TEXT has never been expanded, expand it *here*.  Use WITNESS as
@@ -2138,7 +2156,8 @@ m4_define([m4_expand_once],
 # ----------------------
 # Declare that MACRO-NAME has been provided.
 m4_define([m4_provide],
-[m4_define([m4_provide($1)])])
+[m4_provide_if([$1], [], [m4_ifdef([_m4_hoisting],
+  [m4_set_add([_m4_hoist_provide], [$1])])m4_define([m4_provide($1)])])])
 
 
 # m4_provide_if(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED)
-- 
1.6.0.4








reply via email to

[Prev in Thread] Current Thread [Next in Thread]