[Top][All Lists]
[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
- Fix an 8-year-old AC_REQUIRE bug,
Eric Blake <=