autoconf-patches
[Top][All Lists]
Advanced

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

Fix an 8-year-old AC_REQUIRE bug


From: Eric Blake
Subject: Fix an 8-year-old AC_REQUIRE bug
Date: Tue, 30 Dec 2008 18:59:11 +0000 (UTC)
User-agent: Loom/3.14 (http://gmane.org/)

Ever since Axel Thimm's idea for fixing nested AC_REQUIRE was introduced on 
2000-06-26 (unfortunately, it predates the mailing list archives), it has had a 
subtle bug in relation to directly-invoked DEFUN'd macros, analyzed here:
http://lists.gnu.org/archive/html/bug-autoconf/2008-12/msg00039.html

Here's my first draft of a patch.  Unfortunately, there is probably existing 
code that was expecting the old buggy behavior.  For example, the testsuite 
checks something like this:

AC_DEFUN([FOO], [foo=1])
AC_DEFUN([BAR], [AC_REQUIRE([FOO])bar=$foo])
AS_IF([:], [], [BAR])
echo "foo=$foo bar=$bar"

and expects the output:
foo=1 bar=

in other words, it is _expecting_ the AC_REQUIRE to be hoisted in front of the 
AS_IF, rather than in front of BAR.  So it seems like we need some way that 
certain macros, like AS_IF, can inform the m4_require engine that any 
m4_require encountered during nested macros should be hoisted in front of AS_IF 
instead of their normal position in front of the nested macro.

Until I can patch enough things to at least get a clean testsuite run, I can't 
apply this to mainline yet.  But if anyone wants to review my algorithm 
documentation, please feel free.


From: Eric Blake <address@hidden>
Date: Tue, 30 Dec 2008 11:10:54 -0700
Subject: [PATCH] Fix output location of m4_defun->m4_defun->m4_require.

* lib/m4sugar/m4sugar.m4 (Defining macros): Update comments to
describe why old algorithm was broken, and why new one works.
(_m4_divert_grow, m4_require): Move _m4_divert_grow management
into defun calls.
(_m4_defun_pro_outer, _m4_defun_epi_outer): Inline...
(_m4_defun_pro, _m4_defun_epi): ...into these, and directly manage
_m4_divert_grow, even with direct invocation of nested defun'd
macros.
(m4_divert_require): Guarantee that the entire prerequisite chain
is expanded into the requested diversion.
* lib/m4sugar/m4sh.m4 (AS_REQUIRE): Be careful of KILL.
* tests/m4sugar.at (m4@&address@hidden: nested): New test.
* NEWS: Document this.
Reported by Bruno Haible.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |   16 +++
 NEWS                   |   28 +++++
 lib/m4sugar/m4sh.m4    |   11 ++-
 lib/m4sugar/m4sugar.m4 |  256 +++++++++++++++++++++++++++++++++++++++--------
 tests/m4sugar.at       |   99 +++++++++++++++++++
 5 files changed, 362 insertions(+), 48 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index d1a5382..b278818 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,21 @@
 2008-12-30  Eric Blake  <address@hidden>
 
+       Fix output location of m4_defun->m4_defun->m4_require.
+       * lib/m4sugar/m4sugar.m4 (Defining macros): Update comments to
+       describe why old algorithm was broken, and why new one works.
+       (_m4_divert_grow, m4_require): Move _m4_divert_grow management
+       into defun calls.
+       (_m4_defun_pro_outer, _m4_defun_epi_outer): Inline...
+       (_m4_defun_pro, _m4_defun_epi): ...into these, and directly manage
+       _m4_divert_grow, even with direct invocation of nested defun'd
+       macros.
+       (m4_divert_require): Guarantee that the entire prerequisite chain
+       is expanded into the requested diversion.
+       * lib/m4sugar/m4sh.m4 (AS_REQUIRE): Be careful of KILL.
+       * tests/m4sugar.at (m4@&address@hidden: nested): New test.
+       * NEWS: Document this.
+       Reported by Bruno Haible.
+
        Make it easier to track diversion bugs.
        * lib/m4sugar/m4sugar.m4 (_m4_divert_raw, _m4_undivert): New
        internal macros, which are easier to trace than m4_builtin.
diff --git a/NEWS b/NEWS
index 7eff140..99da48e 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,34 @@ GNU Autoconf NEWS - User visible changes.
 ** AC_HEADER_ASSERT is fixed so that './configure --enable-assert' no
    longer mistakenly disables assertions.
 
+** Directly invoking a macro created with AC_DEFUN (or m4_defun) inside
+   the body of another AC_DEFUN macro no longer hoists AC_REQUIRE of
+   the nested macro prior to the outer macro; this in turn fixes some
+   subtle bugs where AC_REQUIRE could be output in the wrong order
+   (bug present since current m4_defun algorithm was written in 2.50).
+
+   For an example:
+     AC_DEFUN([a], [A])
+     AC_DEFUN([b], [B AC_REQUIRE([a])])
+     AC_DEFUN([c], [C AC_REQUIRE([b])])
+     AC_DEFUN([outer], [PRE a c POST])
+     outer
+   now outputs `PRE A B C POST' instead of `B PRE A C POST'.
+
+   If you have code that triggers the bug, but need to work with older
+   autoconf, you can use the workaround of splitting the macro that
+   was emitted too late to instead AC_REQUIRE its body:
+     AC_DEFUN([a], [AC_REQUIRE([A_BODY])])
+     AC_DEFUN([A_BODY], [A])
+     AC_DEFUN([b], [B AC_REQUIRE([a])])
+     AC_DEFUN([c], [C AC_REQUIRE([b])])
+     AC_DEFUN([outer], [PRE a c POST])
+     outer
+   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).
+
 ** Autotest testsuites accept an option --jobs[=N] for parallel testing.
 
 ** Autotest testsuites do not attempt to write startup error messages
diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4
index be9ff3b..358aa81 100644
--- a/lib/m4sugar/m4sh.m4
+++ b/lib/m4sugar/m4sh.m4
@@ -356,9 +356,11 @@ m4_divert_pop[]])
 # AS_REQUIRE(NAME-TO-CHECK, [BODY-TO-EXPAND = NAME-TO-CHECK],
 #            [DIVERSION = M4SH-INIT])
 # -----------------------------------------------------------
-# BODY-TO-EXPAND is some initialization which must be expanded in the
-# given diversion when expanded (required or not).  The expansion
-# goes in the named diversion or an earlier one.
+# BODY-TO-EXPAND is some initialization which must be expanded no
+# later than DIVERSION, if NAME-TO-CHECK is not already provided, and
+# which provides NAME-TO-CHECK.  If the current diversion is not KILL
+# and comes earlier than DIVERSION, the expansion is placed in the
+# current diversion.
 #
 # Note: we expand _m4_divert_desired before passing it to m4_divert_require,
 # otherwise we would need to use m4_pushdef and m4_popdef instead of
@@ -368,7 +370,8 @@ m4_divert_pop[]])
 # either m4_require([$1], [$2]) or m4_divert_require(desired, [$1], [$2]).
 m4_defun([AS_REQUIRE],
 [m4_define([_m4_divert_desired], [m4_default_quoted([$3], [M4SH-INIT])])]dnl
-[m4_if(m4_eval(_m4_divert(_m4_divert_dump) <= _m4_divert(_m4_divert_desired)),
+[m4_if(m4_eval(_m4_divert(_m4_divert_dump) <= _m4_divert(_m4_divert_desired)
+              && m4_divnum >= 0),
        1, [m4_require(],
          [m4_divert_require(_m4_divert(_m4_divert_desired),]) [$1], [$2])])
 
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index 556969f..825001c 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -1460,10 +1460,11 @@ m4_define([m4_undivert],
 # difficult part is the proper expansion of macros when they are
 # m4_require'd.
 #
-# The implementation is based on two ideas, (i) using diversions to
+# The implementation is based on three ideas, (i) using diversions to
 # prepare the expansion of the macro and its dependencies (by Franc,ois
-# Pinard), and (ii) expand the most recently m4_require'd macros _after_
-# the previous macros (by Axel Thimm).
+# Pinard), (ii) expand the most recently m4_require'd macros _after_
+# the previous macros (by Axel Thimm), and (iii) collect the expansion
+# of a macro and its requirements as one unit (by Eric Blake).
 #
 #
 # The first idea: why use diversions?
@@ -1597,8 +1598,8 @@ m4_define([m4_undivert],
 #         GROW:
 #         BODY:     TEST2a; TEST3; TEST2b: TEST1
 #
-# The idea is simple, but the implementation is a bit evolved.  If you
-# are like me, you will want to see the actual functioning of this
+# The idea is simple, but the implementation is a bit involved.  If
+# you are like me, you will want to see the actual functioning of this
 # implementation to be convinced.  The next section gives the full
 # details.
 #
@@ -1649,7 +1650,7 @@ m4_define([m4_undivert],
 #   BODY:        empty
 #   GROW - 1:    TEST2a
 #   diversions:  GROW - 1, GROW, BODY |-
-# Than the content of the temporary diversion is moved to DUMP and the
+# Then the content of the temporary diversion is moved to DUMP and the
 # temporary diversion is popped.
 #   DUMP:        BODY
 #   BODY:        TEST2a
@@ -1669,7 +1670,7 @@ m4_define([m4_undivert],
 #   BODY:        TEST2a
 #   GROW - 2:    TEST3
 #   diversions:  GROW - 2, GROW - 1, GROW, BODY |-
-# Than the diversion is appended to DUMP, and popped.
+# Then the diversion is appended to DUMP, and popped.
 #   DUMP:        BODY
 #   BODY:        TEST2a; TEST3
 #   diversions:  GROW - 1, GROW, BODY |-
@@ -1697,6 +1698,171 @@ m4_define([m4_undivert],
 #   diversions: BODY |-
 #
 #
+# The third idea: handle a macro and its requirements together
+# ------------------------------------------------------------
+#
+# Using just the first two ideas, Autoconf 2.50 through 2.63 still had
+# a subtle bug for more than seven years.  Let's consider the
+# following example to explain the bug:
+#
+# | m4_defun([TEST1], [1])
+# | m4_defun([TEST2], [2[]REQUIRE([TEST1])])
+# | m4_defun([TEST3], [3[]REQUIRE([TEST2])])
+# | m4_defun([WRAP],  [PRE TEST1 TEST3 POST])
+# |
+# | AC_INIT
+# | WRAP
+#
+# After the prologue of WRAP, we are collecting text in GROW with the
+# intent of dumping it in BODY during the epilogue.  Next, we
+# encounter the direct invocation of TEST1, rather than a
+# REQUIRE([TEST1]).  Under the Axel Thimm algorithm, the second
+# prologue realizes that it is nested, so the body is still collected
+# into GROW, with no change in diversion, at which point TEST1 is now
+# provided.
+#
+# From there, we encounter the direct invocation of TEST3, also
+# collected directly into GROW.  The REQUIRE([TEST2]) opens up GROW -
+# 1 to collect the expansion of TEST2.  The REQUIRE([TEST1]) is a
+# no-op, since TEST1 is already provided.  Thus, when all is said and
+# done, the contents of GROW - 1 (2) are transferred to BODY first,
+# followed by the contents of GROW (PRE, 1, 3, POST).  Once again, we
+# have a case where the dependency was emitted too soon.
+#
+# The solution is to make every DEFUN'd macro track two growth
+# diversions, rather than one.  Each prologue reserves one diversion
+# for itself, and another diversion for all of its prerequisites.
+# Then, when a nested DEFUN'd macro is encountered via direct
+# expansion, it uses the outer macro's primary diversion as its dump
+# location, and when a nested DEFUN'd macro is encountered via a
+# REQUIRE, it uses the outer macro's prerequisite diversion.  The
+# prologue then collects both prerequisites and the main expansion, as
+# a single unit, into the target DUMP location.
+#
+# To manage dual diversions, we use _m4_divert_grow for the diversion
+# that REQUIRE will dump to, and _m4_divert_grow + 1 as the current
+# diversion collecting direct expansion.  Using this fixed
+# implementation, and the earlier definitions, we now have the
+# following walk-through example:
+#
+#          AC_INIT
+#          WRAP
+#
+# After AC_INIT was run, the current diversion is BODY.
+# * AC_INIT was run
+#   DUMP:                undefined
+#   diversion stack:     BODY, KILL |-
+#   divert_grow:         |-
+#   BODY:                empty
+#
+# * WRAP is expanded
+# The prologue of WRAP sets _m4_divert_dump, which is the diversion
+# where the current elaboration will be dumped, to the current
+# diversion.  It also does m4_divert_push to GROW, for the body of
+# WRAP, and GROW - 1 for the prerequisites of WRAP.  The text PRE goes
+# into GROW.
+#   DUMP:        BODY
+#   diversions:  GROW, BODY, KILL |-
+#   divert_grow: GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE
+#   GROW - 1:    empty
+#
+# * WRAP invokes TEST1
+# The prologue of TEST1 tracks GROW as the diversion to dump into,
+# then reserves GROW - 2 and GROW - 3.  The text 1 goes into GROW - 2.
+#   DUMP:        BODY
+#   diversions:  GROW - 2, GROW, BODY, KILL |-
+#   divert_grow: GROW - 3, GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE
+#   GROW - 1:    empty
+#   GROW - 2:    1
+#   GROW - 3:    empty
+#
+# Then the epilogue of TEST1 moves both GROW - 3 and GROW - 2 into the
+# current DUMP location, GROW.
+#   DUMP:        BODY
+#   diversions:  GROW, BODY, KILL |-
+#   divert_grow: GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1
+#   GROW - 1:    empty
+#
+# * WRAP invokes TEST3
+# Again, the prologue tracks a new diversion pair.
+#   DUMP:        BODY
+#   diversions:  GROW - 2, GROW, BODY, KILL |-
+#   divert_grow: GROW - 3, GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1
+#   GROW - 1:    empty
+#   GROW - 2:    3
+#   GROW - 3:    empty
+#
+# * TEST3 requires TEST2
+# _m4_require_call sets GROW - 3 as the current diversion.
+#   DUMP:        BODY
+#   diversions:  GROW - 3, GROW - 2, GROW, BODY, KILL |-
+#   divert_grow: GROW - 3, GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1
+#   GROW - 1:    empty
+#   GROW - 2:    3
+#   GROW - 3:    empty
+# Then, seeing that TEST2 has not been provided, invokes TEST2.  The
+# prologue of TEST2 reserves two diversions, GROW - 4 and GROW - 5.
+#   DUMP:        BODY
+#   diversions:  GROW - 4, GROW - 3, GROW - 2, GROW, BODY, KILL |-
+#   divert_grow: GROW - 5, GROW - 3, GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1
+#   GROW - 1:    empty
+#   GROW - 2:    3
+#   GROW - 3:    empty
+#   GROW - 4:    2
+#   GROW - 5:    empty
+#
+# * TEST2 requires TEST1
+# But this is a no-op, since TEST1 is already provided.  Now TEST2 is
+# complete, and the epilogue moves both GROW - 5 and GROW - 4 into the
+# next divert_grow location.  The conclusion of REQUIRE then restores
+# GROW - 2 as the current diversion.
+#   DUMP:        BODY
+#   diversions:  GROW - 2, GROW, BODY, KILL |-
+#   divert_grow: GROW - 3, GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1
+#   GROW - 1:    empty
+#   GROW - 2:    3
+#   GROW - 3:    2
+#
+# * TEST3 completes
+# The epilogue moves two diversions into the current DUMP location,
+# with the prerequisites in GROW - 3 copied prior to the expansion in
+# GROW - 2.
+#   DUMP:        BODY
+#   diversions:  GROW, BODY, KILL |-
+#   divert_grow: GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1 2 3
+#   GROW - 1:    empty
+#
+# * WRAP completes
+# POST is expanded in the current diversion.
+#   DUMP:        BODY
+#   diversions:  GROW, BODY, KILL |-
+#   divert_grow: GROW - 1 |-
+#   BODY:        empty
+#   GROW:        PRE 1 2 3 POST
+#   GROW - 1:    empty
+# Then the epilogue collects GROW - 1 and GROW into BODY.
+#   DUMP:        undefined
+#   diversions:  BODY, KILL |-
+#   divert_grow: |-
+#   BODY:        PRE 1 2 3 POST
+#
+#
 # 2. Keeping track of the expansion stack
 # =======================================
 #
@@ -1763,46 +1929,56 @@ m4_define([_m4_divert(GROW)],       10000)
 
 # _m4_defun_pro(MACRO-NAME)
 # -------------------------
-# The prologue for Autoconf macros.
+# The prologue for the defun'd macro MACRO-NAME.
 #
 # This is called frequently, so minimize the number of macro invocations
 # by avoiding dnl and m4_defn overhead.
 m4_define([_m4_defun_pro],
-[m4_ifdef([_m4_expansion_stack], [], [_m4_defun_pro_outer[]])]dnl
+[m4_divert_push(m4_ifdef([_m4_expansion_stack],
+  [m4_decr(_m4_divert_grow)], [m4_pushdef([_m4_divert_dump],
+    _m4_defn([_m4_divert_diversion]))[GROW]]))]dnl
+[m4_pushdef([_m4_divert_grow], m4_decr(m4_divnum))]dnl
 [m4_expansion_stack_push([$1])m4_pushdef([_m4_expanding($1)])])
 
-m4_define([_m4_defun_pro_outer],
-[m4_define([_m4_divert_dump],
-  m4_defn([_m4_divert_diversion]))m4_divert_push([GROW])])
-
 # _m4_defun_epi(MACRO-NAME)
 # -------------------------
-# The Epilogue for Autoconf macros.  MACRO-NAME only helps tracing
-# the PRO/EPI pairs.
+# The epilogue for the defun'd macro MACRO-NAME.
 #
 # This is called frequently, so minimize the number of macro invocations
 # by avoiding dnl and m4_popdef overhead.
 m4_define([_m4_defun_epi],
 [_m4_popdef([_m4_expanding($1)], [_m4_expansion_stack])]dnl
-[m4_ifdef([_m4_expansion_stack], [], [_m4_defun_epi_outer[]])]dnl
-[m4_provide([$1])])
+[m4_divert_pop(m4_ifdef([_m4_expansion_stack],
+  [m4_incr(_m4_divert_grow)], [_m4_popdef([_m4_divert_dump])[GROW]]))]dnl
+[_m4_undivert(_m4_divert_grow, m4_incr(_m4_divert_grow))]dnl
+[_m4_popdef([_m4_divert_grow])m4_provide([$1])])
+
+
+# _m4_divert_dump
+# ---------------
+# Undefined when no defun'd macros are in flight, otherwise the
+# diversion name or number where the overall expansion will be dumped.
+# Useful to decide when to call m4_divert_require.
 
-m4_define([_m4_defun_epi_outer],
-[_m4_undefine([_m4_divert_dump])m4_divert_pop([GROW])m4_undivert([GROW])])
+# _m4_divert_grow
+# ---------------
+# Undefined when no defun'd macros are in flight, otherwise the
+# diversion number where require'd macros should be expanded.
 
 
 # m4_divert_require(DIVERSION, NAME-TO-CHECK, [BODY-TO-EXPAND])
 # --------------------------------------------------------------
-# Same as m4_require, but BODY-TO-EXPAND goes into the named diversion;
-# requirements still go in the current diversion though.
+# Same as m4_require, but BODY-TO-EXPAND and all its prerequisites
+# that had not yet been provided go into DIVERSION.
 #
 m4_define([m4_divert_require],
 [m4_ifdef([_m4_expanding($2)],
   [m4_fatal([$0: circular dependency of $2])])]dnl
-[m4_ifdef([_m4_divert_dump], [],
-  [m4_fatal([$0($2): cannot be used outside of an m4_defun'd macro])])]dnl
 [m4_provide_if([$2], [],
-  [_m4_require_call([$2], [$3], [$1])])])
+  [m4_pushdef([_m4_divert_grow], m4_ifdef([_m4_divert_grow],
+     [m4_decr(_m4_divert_grow)], [_m4_divert([GROW])]))_m4_require_call([$2],
+     [$3], _m4_divert_grow)m4_divert_text([$1],
+     [_m4_undivert(_m4_divert_grow)])_m4_popdef([_m4_divert_grow])])])
 
 
 # m4_defun(NAME, EXPANSION, [MACRO = m4_define])
@@ -1923,38 +2099,29 @@ m4_define([m4_before],
 # This is called frequently, so minimize the number of macro invocations
 # by avoiding dnl and other overhead on the common path.
 m4_define([m4_require],
-m4_do([[m4_ifdef([_m4_expanding($1)],
-                [m4_fatal([$0: circular dependency of $1])])]],
-      [[m4_ifdef([_m4_divert_dump], [],
-                [m4_fatal([$0($1): cannot be used outside of an ]dnl
-m4_bmatch([$0], [^AC_], [[AC_DEFUN]], [[m4_defun]])['d macro])])]],
-      [[m4_provide_if([$1],
-                     [],
-                     [_m4_require_call([$1], [$2], [_m4_defn
([_m4_divert_dump])])])]]))
+[m4_ifdef([_m4_expanding($1)],
+         [m4_fatal([$0: circular dependency of $1])])]dnl
+[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_require_call(NAME-TO-CHECK, [BODY-TO-EXPAND = NAME-TO-CHECK], DIVERSION)
 # ----------------------------------------------------------------------------
 # If m4_require decides to expand the body, it calls this macro.  The
-# expansion is placed in DIVERSION.
+# expansion of BODY-TO-EXPAND, plus a newline, is placed in DIVERSION,
+# at which point NAME-TO-CHECK must be provided.
 #
 # This is called frequently, so minimize the number of macro invocations
 # by avoiding dnl and other overhead on the common path.
 m4_define([_m4_require_call],
-[m4_pushdef([_m4_divert_grow], m4_decr(_m4_divert_grow))]dnl
-[m4_divert_push(_m4_divert_grow)]dnl
+[m4_divert_push($3)]dnl
 [m4_if([$2], [], [$1], [$2])
 m4_provide_if([$1], [], [m4_warn([syntax],
   [$1 is m4_require'd but not m4_defun'd])])]dnl
-[_m4_divert_raw(_m4_divert($3))]dnl
-[_m4_undivert(_m4_divert_grow)]dnl
-[m4_divert_pop(_m4_divert_grow)_m4_popdef([_m4_divert_grow])])
-
-
-# _m4_divert_grow
-# ---------------
-# The counter for _m4_require_call.
-m4_define([_m4_divert_grow], _m4_divert([GROW]))
+[m4_divert_pop($3)])
 
 
 # m4_expand_once(TEXT, [WITNESS = TEXT])
@@ -1969,6 +2136,7 @@ m4_define([m4_expand_once],
 
 # m4_provide(MACRO-NAME)
 # ----------------------
+# Declare that MACRO-NAME has been provided.
 m4_define([m4_provide],
 [m4_define([m4_provide($1)])])
 
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index 0d90ca2..36bf03d 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -379,6 +379,7 @@ AT_CLEANUP
 ## --------------------------- ##
 
 AT_SETUP([m4@&address@hidden: error message])
+AT_KEYWORDS([m4@&address@hidden)
 
 AT_DATA_M4SUGAR([script.4s],
 [[m4_defun([foo], [FOO])
@@ -398,6 +399,7 @@ AT_CLEANUP
 ## ----------------------------------- ##
 
 AT_SETUP([m4@&address@hidden: circular dependencies])
+AT_KEYWORDS([m4@&address@hidden)
 
 AT_DATA_M4SUGAR([script.4s],
 [[m4_defun([foo], [m4_require([bar])])
@@ -427,6 +429,7 @@ AT_CLEANUP
 ## ---------------------- ##
 
 AT_SETUP([m4@&address@hidden: one-shot initialization])
+AT_KEYWORDS([m4@&address@hidden)
 AT_KEYWORDS([m4@&address@hidden m4@&address@hidden)
 
 AT_CHECK_M4SUGAR_TEXT([[
@@ -455,6 +458,102 @@ hello, again
 AT_CLEANUP
 
 
+## -------------------- ##
+## m4_require: nested.  ##
+## -------------------- ##
+
+AT_SETUP([m4@&address@hidden: nested])
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden)
+
+dnl From the m4sugar.m4 discourse: Require chains, top level
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_defun([a], [[a]])dnl aka TEST2a
+m4_defun([b], [[b]m4_require([a])])dnl aka TEST3
+m4_defun([c], [[c]m4_require([b])])dnl aka TEST2b
+m4_defun([d], [[d]m4_require([a])m4_require([c])])dnl aka TEST1
+pre
+d
+d
+post
+]],
+[[pre
+a
+b
+c
+d
+d
+post
+]])
+
+dnl From the m4sugar.m4 discourse: Require chains, nested
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_defun([a], [[a]])dnl aka TEST2a
+m4_defun([b], [[b]m4_require([a])])dnl aka TEST3
+m4_defun([c], [[c]m4_require([b])])dnl aka TEST2b
+m4_defun([d], [[d]m4_require([a])m4_require([c])])dnl aka TEST1
+m4_defun([wrap],
+[pre
+d
+d
+post])dnl
+wrap
+]],
+[[pre
+a
+b
+c
+d
+d
+post
+]])
+
+dnl From the m4sugar.m4 discourse: Direct invocation, top level
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_defun([a], [[a]])dnl
+m4_defun([b], [[b]m4_require([a])])dnl
+m4_defun([c], [[c]m4_require([b])])dnl
+pre
+a
+c
+a
+c
+post
+]],
+[[pre
+a
+b
+c
+a
+c
+post
+]])
+
+dnl From the m4sugar.m4 discourse: Direct invocation, nested
+AT_CHECK_M4SUGAR_TEXT([[dnl
+m4_defun([a], [[a]])dnl
+m4_defun([b], [[b]m4_require([a])])dnl
+m4_defun([c], [[c]m4_require([b])])dnl
+m4_defun([outer],
+[pre
+a
+c
+a
+c
+post])dnl
+outer
+]],
+[[pre
+a
+b
+c
+a
+c
+post
+]])
+
+AT_CLEANUP
+
+
 ## --------- ##
 ## m4_cond.  ##
 ## --------- ##
-- 
1.6.0.4







reply via email to

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