[Top][All Lists]

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

Re: m4_map and AC_REQUIRE hoisting

From: Eric Blake
Subject: Re: m4_map and AC_REQUIRE hoisting
Date: Wed, 24 Jan 2018 09:49:29 -0600
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Thunderbird/52.5.2

On 01/24/2018 12:32 AM, Nick Bowler wrote:
> Hello Autoconfers,
> I hit a weird (to me) issue involving m4_map(all) and AC_REQUIRE, and I
> would appreciate some help understanding what's going on here!

Let's see if I can give an explanation.

> So normally when expanding a macro defined with AC_DEFUN and a nested
> AC_REQUIRE causes a macro to be expanded, that expansion gets "hoisted"
> outside of the topmost macro expansion.  Example:
>   AC_INIT([test], [0])
>   AC_DEFUN([DEF0], [echo def0])
>   AC_DEFUN([TEST0], [m4_newline([echo hello])[]m4_newline([REQ0])])
>   TEST0
> When I run this, I see:
>   def0
>   hello
> which is as expected.

Or, put in other words, expanding an AC_REQUIRE tells autoconf to "make
sure the required macro gets expanded prior to the start of the macro
I'm currently expanding".  Step-wise, you are expanding:



<start of TEST0>m4_newline([echo hello])[]m4_newline([REQ0])


<start of TEST0>
echo hello


<start of TEST0>
echo hello

where AC_REQUIRE figures out the current context (that of outputting
TEST0), and ensures that DEF0 is expanded prior to that point, thus
behaving like:

<start of TEST0>
echo hello

and turning into the desired:

echo def0
echo hello

> But when my macro is using m4_map or m4_mapall, this mechanism seems to
> break down.  Example:
>   AC_INIT([test], [0])
>   AC_DEFUN([DEF1], [echo def1])
>   AC_DEFUN([TEST1], [m4_mapall([m4_newline], [[echo hello], [REQ1]])])

Underquoted; the second argument to m4_mapall() is a "comma-separated
quoted list of argument descriptions" which are "in turn a
comma-separated quoted list of quoted elements".  Better would be:

m4_mapall([m4_newline], [[[echo hello]], [[REQ1]]])

(there are two argument descriptions, [[echo hello]] and [[REQ1]]; the
first argument description is the quoted list containing the single
element [echo hello] as the argument to supply).

>   TEST1
> When I run that, I get:
>   hello
>   def1
> with the order reversed from my expectation...

step-wise, this is going from



<start of TEST1>m4_mapall([m4_newline], [[echo hello], [REQ1]])

and conceptually into this (because of the underquoting):

<start of TEST1>m4_newline(echo hello)[]m4_newline(REQ1)[]

But note what happens here.  Since REQ1 is unquoted, it gets expanded
PRIOR to invoking m4_newline(), in order to determine what argument to
pass to m4_newline; and as currently implemented, the require mechanism
in autoconf can only hoist its argument to the front of the current
parsing level.  So you are getting:

<start of TEST1>
echo hello[]m4_newline(<start of m4_newline args>AC_REQUIRE([DEF1]))[]


<start of TEST1>
echo hello[]m4_newline(DEF1<start of m4_newline args>)[]

<start of TEST1>
echo hello[]m4_newline(echo def1)[]

where the required text occurs before anything else at the current
parsing level (but the current parsing level was during argument
collection), so the requirement text is passed as the argument to
m4_newline(), at which point you end with:

<start of TEST1>
echo hello
echo def1

and the order is different than you wanted, because you did not
encounter AC_REQUIRE during the context of the body of TEST1 for
hoisting anything prior to TEST1.

And indeed, when I retried your example with sufficient quoting, I got
the desired 'def1' before 'hello', because the expansion of REQ1 is then
deferred until after m4_newline() has done its work, at which point the
parse is now in the context of expanding TEST1 rather than collecting
arguments to m4_newline.

AC_DEFUN([TEST1], [m4_mapall([m4_newline], [[[echo hello]], [[REQ1]]])])

> But if I implement my own version of mapall in a straightforward way,
> it seems to work out just fine:
>   AC_INIT([test], [0])
>   AC_DEFUN([DEF2], [echo def2])
>   m4_define([my_mapall], [m4_ifval([$2],
>     [m4_indir([$1], m4_car($2))[]my_mapall([$1], m4_cdr($2))])])
>   AC_DEFUN([TEST2], [my_mapall([m4_newline], [[echo hello], [REQ2]])])

Your naive version is less susceptible to underquoting issues, but is
more expensive in computation power (it computes the expansion of $2
much more frequently, and therefore m4 has to expand more input - adding
a quadratic complexity for how many bytes are parsed as the list gets
larger) - I remember spending quite a bit of time optimizing m4_map and
friends to get to a minimal expansion that autoconf currently uses; for
small lists (like your example of two argument descriptions with one
argument each), the difference is in the noise, but for large lists
(such as hundreds of argument descriptions, where I seem to recall that
AC_CHECK_FUNCS_ONCE was a candidate for something that could map lots of
arguments in a that checks a lot of functions), the
difference between O(n^3) and O(n^2) algorithms was very noticeable in
time and memory consumed by 'autoconf'.

>   TEST2
> Running this gives:
>   def2
>   hello
> as expected.
> I really have no idea why TEST1 results in a different expansion from
> the other two examples.  What exactly is different about m4_mapall?

Hopefully my explanation into the deep magic guts helped!

Eric Blake, Principal Software Engineer
Red Hat, Inc.           +1-919-301-3266
Virtualization: |

Attachment: signature.asc
Description: OpenPGP digital signature

reply via email to

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