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 <[email protected]> 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@&t...@_require: nested): New test. * NEWS: Document this. Reported by Bruno Haible. Signed-off-by: Eric Blake <[email protected]> --- 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 <[email protected]> + 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@&t...@_require: 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@&t...@_require: error message]) +AT_KEYWORDS([m4@&t...@_require]) AT_DATA_M4SUGAR([script.4s], [[m4_defun([foo], [FOO]) @@ -398,6 +399,7 @@ AT_CLEANUP ## ----------------------------------- ## AT_SETUP([m4@&t...@_require: circular dependencies]) +AT_KEYWORDS([m4@&t...@_require]) AT_DATA_M4SUGAR([script.4s], [[m4_defun([foo], [m4_require([bar])]) @@ -427,6 +429,7 @@ 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_CHECK_M4SUGAR_TEXT([[ @@ -455,6 +458,102 @@ hello, again AT_CLEANUP +## -------------------- ## +## m4_require: nested. ## +## -------------------- ## + +AT_SETUP([m4@&t...@_require: nested]) +AT_KEYWORDS([m4@&t...@_require m4@&t...@_defun]) + +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
