autoconf-patches
[Top][All Lists]
Advanced

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

m4_defun_init


From: Eric Blake
Subject: m4_defun_init
Date: Fri, 24 Oct 2008 07:30:23 -0600
User-agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.17) Gecko/20080914 Thunderbird/2.0.0.17 Mnenhy/0.7.5.666

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I'm shocked at how much difference this made, touching just m4sh!
Pre-patch, running autoconf on coreutils took 22.270s, after it takes
18.127s; an 18% speedup, and with no change to configure contents.

The concept of this patch is that one-shot initialization routines, like
AS_REQUIRE, are expensive.  Rather than calling the initialization every
single time, as a no-op that just finds out that things were already
initialized, it is nicer to define two versions of the same macro - one
that does the initialization and popdef's itself, and another that does
the common work.  In fact, the common version can often be m4_define'd
rather than m4_defun'd: the main reason for m4_defun is for self-providing
and for saving location details to print a nicer error stack about any
detected dependency errors; but once the one-shot initializer has
completed successfully, we know there are no dependency problems and the
macro is provided, so we should no longer care about using the m4_defun
prolog to track location, and m4_require notices that the macro has
already been provided.  (However, I'm not completely sure that always
using m4_define for the common definition is is always correct, though; so
we may still need to modify m4_defun_init to allow m4_defun'ing the common
definition, and I am leaving m4_define_init undocumented for now).

In order to implement the concept, I first had to fix m4_copy to manage
entire pushdef stacks (well, actually, I fixed that second, after noticing
the breakage of AC_MSG_WARN not working when it cloned only half of the
AS_WARN pushdef stack; thank goodness for git rebase -i).  Note that the
new m4_copy definition doesn't work for m4_ifdef, m4_pushdef, _m4_defn,
_m4_popdef, or _m4_copy.  I could work around that by introducing an extra
layer:

m4_define([m4_copy(m4_ifdef)])
m4_define([m4_copy(m4_pushdef)])...
m4_define([m4_copy],
[m4_ifdef([m4_copy($1)], [m4_define([$2], _m4_defn([$1]))],
          [current implementation])])

However, as I was able to work around the limitation in the few places
that actually need to rename builtins, I didn't think it necessary to slow
down m4_copy any further just for these corner cases.

Oh, and since this patch introduces the first instance where the frozen
file saves pushdef'd values, autoconf no longer works with the beta
m4-1.4o (in fact, any version of the m4.git master branch prior to May of
this year, where a 7-year-old frozen file regression local to that branch
was finally fixed).

I will probably do another followup for autoconf macros that do one-shot
initialization (think _AC_*_IFELSE, for starters), for even more speedup.

- --
Don't work too hard, make some time for fun as well!

Eric Blake             address@hidden
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Public key at home.comcast.net/~ericblake/eblake.gpg
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkkBze8ACgkQ84KuGfSFAYBsWwCfZ4QkThcmhRa4+INa2aboMWda
5K4AnilFbMnXypVukiJ5Re/9+CzAsJRu
=EeRR
-----END PGP SIGNATURE-----
>From 2922868b72bb0847d5df79dfc95fd93a2049cf42 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 23 Oct 2008 21:17:25 -0600
Subject: [PATCH] Improve m4_copy.

* lib/m4sugar/m4sugar.m4 (m4_copy): Add second implementation for
public use.
(_m4_copy): New macro, which preserves pushdef stacks.
(_m4_defun_pro_outer): Bypass it, for speed.
(m4_init): Bypass new implementation, since it breaks on m4_defn.
* bin/autoupdate.in (handle_autoconf_macros): Likewise.
* lib/autoconf/general.m4 (AC_PREREQ): Undefine before redefining,
now that m4_copy checks this.
* doc/autoconf.texi (Redefined M4 Macros) <m4_copy>: Document
this, as well as m4_rename.
* lib/autoconf/autoconf.m4 (m4_copy): Temporarily redefine when
renaming builtins.
* NEWS: Likewise.
* tests/m4sugar.at (m4@&address@hidden): Enhance test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog                |   18 +++++++++++
 NEWS                     |    5 ++-
 bin/autoupdate.in        |    3 +-
 doc/autoconf.texi        |   19 +++++++++++
 lib/autoconf/autoconf.m4 |   12 +++++++-
 lib/autoconf/general.m4  |    1 +
 lib/m4sugar/m4sugar.m4   |   76 +++++++++++++++++++++++++++++++++-------------
 tests/m4sugar.at         |   21 ++++++++++++-
 8 files changed, 130 insertions(+), 25 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 0a83589..f8361b7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,23 @@
 2008-10-24  Eric Blake  <address@hidden>
 
+       Improve m4_copy.
+       * lib/m4sugar/m4sugar.m4 (m4_copy): Add second implementation for
+       public use.
+       (_m4_copy): New macro, which preserves pushdef stacks.
+       (_m4_defun_pro_outer): Bypass it, for speed.
+       (m4_init): Bypass new implementation, since it breaks on m4_defn.
+       * bin/autoupdate.in (handle_autoconf_macros): Likewise.
+       * lib/autoconf/general.m4 (AC_PREREQ): Undefine before redefining,
+       now that m4_copy checks this.
+       * doc/autoconf.texi (Redefined M4 Macros) <m4_copy>: Document
+       this, as well as m4_rename.
+       * lib/autoconf/autoconf.m4 (m4_copy): Temporarily redefine when
+       renaming builtins, since it breaks on m4_ifdef.
+       * NEWS: Likewise.
+       * tests/m4sugar.at (m4@&address@hidden): Enhance test.
+
+2008-10-24  Eric Blake  <address@hidden>
+
        AC_FUNC_GETGROUPS: Revert regression.
        * lib/autoconf/functions.m4 (AC_FUNC_GETGROUPS): Only set
        ac_cv_func_getgroups_works=no when it is not available.
diff --git a/NEWS b/NEWS
index 8b682b8..496fac7 100644
--- a/NEWS
+++ b/NEWS
@@ -12,10 +12,13 @@ GNU Autoconf NEWS - User visible changes.
 
 ** Configure scripts now use shell functions.
 
-** The following m4sugar macros are new:
+** The following documented m4sugar macros are new:
    m4_curry  m4_default_quoted  m4_map_args  m4_map_args_pair
    m4_set_map
 
+** The following m4sugar macros are documented now:
+   m4_copy  m4_rename
+
 ** The following documented m4sh macros are new:
    AS_LINENO_PREPARE  AS_ME_PREPARE  AS_VAR_APPEND  AS_VAR_COPY
 
diff --git a/bin/autoupdate.in b/bin/autoupdate.in
index d23048a..c923559 100644
--- a/bin/autoupdate.in
+++ b/bin/autoupdate.in
@@ -271,7 +271,8 @@ foreach my $file (@ARGV)
       define([_au__include], defn([include]))
       define([_au___undefine], defn([undefine]))
       define([_au__undefine], [_au__ifdef([$1], [_au___undefine([$1])])])
-      define([_au__save], [m4_ifdef([$1], [m4_copy([$1], [_au_$1])])])
+      define([_au__save], [m4_ifdef([$1],
+       [m4_define([_au_$1], _m4_defn([$1]))])])
       define([_au__restore],
        [_au_m4_ifdef([_au_$1],
          [_au_m4_define([$1], _au__defn([_au_$1]))])])
diff --git a/doc/autoconf.texi b/doc/autoconf.texi
index 71f507f..8509322 100644
--- a/doc/autoconf.texi
+++ b/doc/autoconf.texi
@@ -10363,6 +10363,25 @@ Redefined M4 Macros
 released and supports extended regular expression syntax.
 @end defmac
 
address@hidden m4_copy (@var{source}, @var{dest})
address@hidden m4_rename (@var{source}, @var{dest})
address@hidden
address@hidden
+These macros aren't directly builtins, but are closely related to
address@hidden and @code{m4_defn}.  They both ensures that @var{dest}
+is undefined, then proceed to copy the entire pushdef stack of
+definitions of @var{source}.  @code{m4_copy} preserves the source, while
address@hidden undefines the original macro name.
+
+Note that attempting to invoke a renamed macro might not work, since the
+macro may have a dependence on helper macros accessed via composition of
address@hidden but that were not also renamed; likewise, other macros may
+have a hard-coded dependence on @var{source} and could break if
address@hidden has been deleted.  On the other hand, it is always safe to
+rename a macro to temporarily move it out of the way, then rename it
+back later to restore original semantics.
address@hidden defmac
+
 @defmac m4_defn (@address@hidden)
 @msindex{defn}
 This macro fails if @var{macro} is not defined, even when using older
diff --git a/lib/autoconf/autoconf.m4 b/lib/autoconf/autoconf.m4
index c802959..0a838a6 100644
--- a/lib/autoconf/autoconf.m4
+++ b/lib/autoconf/autoconf.m4
@@ -1,7 +1,7 @@
 # This file is part of Autoconf.                -*- Autoconf -*-
 # Driver that loads the Autoconf macro files.
 #
-# Copyright (C) 1994, 1999, 2000, 2001, 2002, 2006 Free Software
+# Copyright (C) 1994, 1999, 2000, 2001, 2002, 2006, 2008 Free Software
 # Foundation, Inc.
 #
 # This program is free software: you can redistribute it and/or modify
@@ -76,6 +76,14 @@ m4_include([autoconf/oldnames.m4])
 # names too.  But users may still depend upon these, so reestablish
 # them.
 
+# In order to copy pushdef stacks, m4_copy temporarily destroys the
+# current pushdef stack.  But these builtins are so primitive that:
+#   1. they should not have more than one pushdef definition
+#   2. undefining the pushdef stack to copy breaks m4_copy
+# Hence, we temporarily restore a simpler m4_copy.
+
+m4_pushdef([m4_copy], [m4_define([$2], m4_defn([$1]))])
+
 m4_copy_unm4([m4_builtin])
 m4_copy_unm4([m4_changequote])
 m4_copy_unm4([m4_decr])
@@ -104,6 +112,8 @@ m4_copy_unm4([m4_translit])
 m4_copy_unm4([m4_undefine])
 m4_copy_unm4([m4_undivert])
 
+m4_popdef([m4_copy])
+
 # Yet some people have started to use m4_patsubst and m4_regexp.
 m4_define([m4_patsubst],
 [m4_expand_once([m4_warn([syntax],
diff --git a/lib/autoconf/general.m4 b/lib/autoconf/general.m4
index 158391c..4f13b58 100644
--- a/lib/autoconf/general.m4
+++ b/lib/autoconf/general.m4
@@ -317,6 +317,7 @@ AU_DEFUN([AC_PREREQ],
 # AC_PREREQ(VERSION)
 # ------------------
 # Complain and exit if the Autoconf version is less than VERSION.
+m4_undefine([AC_PREREQ])
 m4_copy([m4_version_prereq], [AC_PREREQ])
 
 
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index ee8ee63..d0fe9ad 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -91,6 +91,10 @@ m4_undefine([undefine])
 # Nevertheless, one huge difference is the handling of `$0'.  If `from'
 # uses `$0', then with 1, `to''s `$0' is `to', while it is `from' in 2.
 # The user would certainly prefer to see `to'.
+#
+# This definition is in effect during m4sugar initialization, when
+# there are no pushdef stacks; later on, we redefine it to something
+# more powerful for all other clients to use.
 m4_define([m4_copy],
 [m4_define([$2], m4_defn([$1]))])
 
@@ -166,6 +170,28 @@ m4_rename_m4([traceon])
 m4_rename_m4([translit])
 m4_undefine([undivert])
 
+# _m4_defn(ARG)
+# ----------------
+# _m4_defn is for internal use only - it bypasses the wrapper, so it
+# must only be used on one argument at a time, and only on macros
+# known to be defined.  Make sure this still works if the user renames
+# m4_defn but not _m4_defn.
+m4_copy([m4_defn], [_m4_defn])
+
+# _m4_popdef(ARG...)
+# ------------------
+# _m4_popdef is for internal use only - it bypasses the wrapper, so it
+# must only be used on macros known to be defined.  Make sure this
+# still works if the user renames m4_popdef but not _m4_popdef.
+m4_copy([m4_popdef], [_m4_popdef])
+
+# _m4_undefine(ARG...)
+# --------------------
+# _m4_undefine is for internal use only - it bypasses the wrapper, so
+# it must only be used on macros known to be defined.  Make sure this
+# still works if the user renames m4_undefine but not _m4_undefine.
+m4_copy([m4_undefine], [_m4_undefine])
+
 
 ## ------------------- ##
 ## 2. Error messages.  ##
@@ -501,6 +527,28 @@ m4_define([_m4_bpatsubsts],
           m4_shift3($@))])])
 
 
+# m4_copy(SRC, DST)
+# -----------------
+# Define the pushdef stack DST as a copy of the pushdef stack SRC;
+# give an error if DST is already defined.  This is particularly nice
+# for copying self-modifying pushdef stacks, where the top definition
+# includes one-shot initialization that is later popped to the normal
+# definition.
+#
+# The recursive worker destructively swaps the order of a stack.  We
+# use a temporary stack, and swap directions twice, using the third
+# argument to restore the original stack.
+#
+# Some macros simply can't be renamed with this method: namely,
+# anything involved in the implementation of _m4_copy.
+m4_define([m4_copy],
+[m4_ifdef([$2], [m4_fatal([$0: won't overwrite defined macro: $2])],
+         [_$0([$1], [m4_tmp])_$0([m4_tmp], [$2],
+  [m4_pushdef([$1], _m4_defn([m4_tmp]))])])])
+m4_define([_m4_copy],
+[m4_ifdef([$1], [m4_pushdef([$2], _m4_defn([$1]))$3[]_m4_popdef([$1])$0($@)])])
+
+
 # m4_define_default(MACRO, VALUE)
 # -------------------------------
 # If MACRO is undefined, set it to VALUE.
@@ -547,12 +595,6 @@ m4_define([m4_default_quoted],
 # This macro is called frequently, so minimize the amount of additional
 # expansions by skipping m4_ifndef.  Better yet, if __m4_version__ exists,
 # (added in M4 1.6), then let m4 do the job for us (see m4_init).
-#
-# _m4_defn is for internal use only - it bypasses the wrapper, so it
-# must only be used on one argument at a time, and only on macros
-# known to be defined.  Make sure this still works if the user renames
-# m4_defn but not _m4_defn.
-m4_copy([m4_defn], [_m4_defn])
 m4_define([m4_defn],
 [m4_if([$#], [0], [[$0]],
        [$#], [1], [m4_ifdef([$1], [_m4_defn([$1])],
@@ -596,11 +638,6 @@ _m4_dumpdefs_down([$1])])
 # This macro is called frequently, so minimize the amount of additional
 # expansions by skipping m4_ifndef.  Better yet, if __m4_version__ exists,
 # (added in M4 1.6), then let m4 do the job for us (see m4_init).
-#
-# _m4_popdef is for internal use only - it bypasses the wrapper, so it
-# must only be used on macros known to be defined.  Make sure this
-# still works if the user renames m4_popdef but not _m4_popdef.
-m4_copy([m4_popdef], [_m4_popdef])
 m4_define([m4_popdef],
 [m4_if([$#], [0], [[$0]],
        [$#], [1], [m4_ifdef([$1], [_m4_popdef([$1])],
@@ -662,11 +699,6 @@ m4_define([_m4_shift3],
 # This macro is called frequently, so minimize the amount of additional
 # expansions by skipping m4_ifndef.  Better yet, if __m4_version__ exists,
 # (added in M4 1.6), then let m4 do the job for us (see m4_init).
-#
-# _m4_undefine is for internal use only - it bypasses the wrapper, so
-# it must only be used on macros known to be defined.  Make sure this
-# still works if the user renames m4_undefine but not _m4_undefine.
-m4_copy([m4_undefine], [_m4_undefine])
 m4_define([m4_undefine],
 [m4_if([$#], [0], [[$0]],
        [$#], [1], [m4_ifdef([$1], [_m4_undefine([$1])],
@@ -1578,7 +1610,8 @@ m4_do([[m4_ifdef([m4_expansion_stack], [], 
[_m4_defun_pro_outer[]])]],
       [[m4_pushdef([_m4_expanding($1)])]]))
 
 m4_define([_m4_defun_pro_outer],
-[m4_copy([_m4_divert_diversion], [_m4_divert_dump])m4_divert_push([GROW])])
+[m4_define([_m4_divert_dump],
+  m4_defn([_m4_divert_diversion]))m4_divert_push([GROW])])
 
 # _m4_defun_epi(MACRO-NAME)
 # -------------------------
@@ -2838,12 +2871,13 @@ m4_pattern_forbid([^dnl$])
 # But if it is missing, we assume we are being run by M4 1.4.x, that
 # $@ recursion is quadratic, and that we need foreach-based
 # replacement macros.  Use the raw builtin to avoid tripping up
-# include tracing.
+# include tracing.  Meanwhile, avoid m4_copy, since it temporarily
+# undefines m4_defn.
 m4_ifdef([__m4_version__],
 [m4_debugmode([+d])
-m4_copy([_m4_defn], [m4_defn])
-m4_copy([_m4_popdef], [m4_popdef])
-m4_copy([_m4_undefine], [m4_undefine])],
+m4_define([m4_defn], _m4_defn([m4_defn]))
+m4_define([m4_popdef], _m4_defn([m4_popdef]))
+m4_define([m4_undefine], _m4_defn([m4_undefine]))],
 [m4_builtin([include], [m4sugar/foreach.m4])])
 
 # _m4_divert_diversion should be defined:
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index f34bca3..deb6f8c 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -43,7 +43,7 @@ AT_CHECK_M4SUGAR([-o-],, [$2], [$3])
 
 AT_SETUP([m4@&address@hidden)
 
-AT_KEYWORDS([m4@&address@hidden m4@&address@hidden)
+AT_KEYWORDS([m4@&address@hidden m4@&address@hidden m4@&address@hidden 
m4@&address@hidden)
 
 # Ensure that m4sugar dies when dereferencing undefined macros, whether
 # this is provided by m4 natively or faked by wrappers in m4sugar.
@@ -75,6 +75,25 @@ AT_CHECK_M4SUGAR([-o-], 1, [], [stderr])
 AT_CHECK([grep good stderr], [1])
 AT_CHECK([grep 'm4@&address@hidden: undefined.*oops' stderr], [0], [ignore])
 
+# Check that pushdef stacks can be renamed.
+AT_CHECK_M4SUGAR_TEXT([[m4_pushdef([a], [1])dnl
+m4_pushdef([a], [2])dnl
+a b c
+m4_rename([a], [b])dnl
+a b c
+m4_copy([b], [c])dnl
+a b c
+m4_popdef([b], [c])dnl
+a b c
+m4_popdef([b], [c])dnl
+a b c
+]], [[2 b c
+a 2 c
+a 2 2
+a 1 1
+a b c
+]])
+
 AT_CLEANUP
 
 
-- 
1.6.0.2


>From d0c5f482d5aa2eb662758921e441d39dd0277531 Mon Sep 17 00:00:00 2001
From: Eric Blake <address@hidden>
Date: Thu, 23 Oct 2008 15:08:45 -0600
Subject: [PATCH] Optimize clients of AS_REQUIRE.

* lib/m4sugar/m4sugar.m4 (m4_defun): Add undocumented third
argument.
(m4_defun_init): New undocumented macro.
* lib/m4sugar/m4sh.m4 (_AS_ECHO_LOG, AS_MESSAGE, AS_BASENAME)
(_AS_DIRNAME_EXPR, AS_DIRNAME, AS_ECHO, AS_ECHO_N, AS_TEST_X)
(AS_LN_S, AS_MKDIR_P, _AS_PATH_WALK, AS_VERSION_COMPARE)
(AS_TR_SH, AS_TR_CPP, AS_VAR_APPEND, AS_VAR_PUSHDEF): Use it to
simplify these macros once the one-shot initialization is
complete.
* tests/m4sugar.at (m4@&address@hidden: one-shot initialization): New
test.

Signed-off-by: Eric Blake <address@hidden>
---
 ChangeLog              |   13 ++++++++
 lib/m4sugar/m4sh.m4    |   72 ++++++++++++++++++++++++------------------------
 lib/m4sugar/m4sugar.m4 |   50 +++++++++++++++++++++++++--------
 tests/m4sugar.at       |   25 ++++++++++++++++
 4 files changed, 112 insertions(+), 48 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index f8361b7..ac48cd9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,18 @@
 2008-10-24  Eric Blake  <address@hidden>
 
+       Optimize clients of AS_REQUIRE.
+       * lib/m4sugar/m4sugar.m4 (m4_defun): Add undocumented third
+       argument.
+       (m4_defun_init): New undocumented macro.
+       * lib/m4sugar/m4sh.m4 (_AS_ECHO_LOG, AS_MESSAGE, AS_BASENAME)
+       (_AS_DIRNAME_EXPR, AS_DIRNAME, AS_ECHO, AS_ECHO_N, AS_TEST_X)
+       (AS_LN_S, AS_MKDIR_P, _AS_PATH_WALK, AS_VERSION_COMPARE)
+       (AS_TR_SH, AS_TR_CPP, AS_VAR_APPEND, AS_VAR_PUSHDEF): Use it to
+       simplify these macros once the one-shot initialization is
+       complete.
+       * tests/m4sugar.at (m4@&address@hidden: one-shot initialization): New
+       test.
+
        Improve m4_copy.
        * lib/m4sugar/m4sugar.m4 (m4_copy): Add second implementation for
        public use.
diff --git a/lib/m4sugar/m4sh.m4 b/lib/m4sugar/m4sh.m4
index b96ec09..6620a56 100644
--- a/lib/m4sugar/m4sh.m4
+++ b/lib/m4sugar/m4sh.m4
@@ -648,8 +648,8 @@ m4_define([_AS_ECHO],
 # _AS_ECHO_LOG(STRING)
 # --------------------
 # Log the string to AS_MESSAGE_LOG_FD.
-m4_define([_AS_ECHO_LOG],
-[AS_REQUIRE([_AS_LINENO_PREPARE])]dnl
+m4_defun_init([_AS_ECHO_LOG],
+[AS_REQUIRE([_AS_LINENO_PREPARE])],
 [_AS_ECHO([$as_me:${as_lineno-$LINENO}: $1], [AS_MESSAGE_LOG_FD])])
 
 
@@ -685,8 +685,9 @@ m4_define([_AS_ECHO_N],
 
 # AS_MESSAGE(STRING, [FD = AS_MESSAGE_FD])
 # ----------------------------------------
-m4_define([AS_MESSAGE],
-[AS_REQUIRE([_AS_ME_PREPARE])]dnl
+# Output "`basename $0`: "STRING to the open file FD.
+m4_defun_init([AS_MESSAGE],
+[AS_REQUIRE([_AS_ME_PREPARE])],
 [m4_ifset([AS_MESSAGE_LOG_FD],
          [{ _AS_ECHO_LOG([$1])
 _AS_ECHO([$as_me: $1], [$2]);}],
@@ -757,8 +758,8 @@ m4_defun([_AS_BASENAME_SED],
          }
          s/.*/./; q']])
 
-m4_defun([AS_BASENAME],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_BASENAME],
+[AS_REQUIRE([_$0_PREPARE])],
 [$as_basename -- $1 ||
 _AS_BASENAME_EXPR([$1]) 2>/dev/null ||
 _AS_BASENAME_SED([$1])])
@@ -768,8 +769,7 @@ _AS_BASENAME_SED([$1])])
 # --------------------
 # Avoid Solaris 9 /usr/ucb/basename, as `basename /' outputs an empty line.
 # Also, traditional basename mishandles --.  Require here _AS_EXPR_PREPARE,
-# to avoid problems when _AS_BASENAME is called from the M4SH-INIT diversion
-# (AS_REQUIRE is nowhere near being as sophisticated as AC_REQUIRE).
+# to avoid problems when _AS_BASENAME is called from the M4SH-INIT diversion.
 m4_defun([_AS_BASENAME_PREPARE],
 [AS_REQUIRE([_AS_EXPR_PREPARE])]dnl
 [if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; 
then
@@ -791,8 +791,8 @@ fi
 # a silly length limit that causes expr to fail if the matched
 # substring is longer than 120 bytes.  So fall back on echo|sed if
 # expr fails.
-m4_defun([_AS_DIRNAME_EXPR],
-[AS_REQUIRE([_AS_EXPR_PREPARE])]dnl
+m4_defun_init([_AS_DIRNAME_EXPR],
+[AS_REQUIRE([_AS_EXPR_PREPARE])],
 [$as_expr X[]$1 : 'X\(.*[[^/]]\)//*[[^/][^/]]*/*$' \| \
         X[]$1 : 'X\(//\)[[^/]]' \| \
         X[]$1 : 'X\(//\)$' \| \
@@ -818,8 +818,8 @@ m4_defun([_AS_DIRNAME_SED],
          }
          s/.*/./; q']])
 
-m4_defun([AS_DIRNAME],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_DIRNAME],
+[AS_REQUIRE([_$0_PREPARE])],
 [$as_dirname -- $1 ||
 _AS_DIRNAME_EXPR([$1]) 2>/dev/null ||
 _AS_DIRNAME_SED([$1])])
@@ -842,16 +842,16 @@ fi
 # Output WORD followed by a newline.  WORD must be a single shell word
 # (typically a quoted string).  The bytes of WORD are output as-is, even
 # if it starts with "-" or contains "\".
-m4_defun([AS_ECHO],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_ECHO],
+[AS_REQUIRE([_$0_PREPARE])],
 [$as_echo $1])
 
 
 # AS_ECHO_N(WORD)
 # -------------
 # Like AS_ECHO(WORD), except do not output the trailing newline.
-m4_defun([AS_ECHO_N],
-[AS_REQUIRE([_AS_ECHO_PREPARE])]dnl
+m4_defun_init([AS_ECHO_N],
+[AS_REQUIRE([_AS_ECHO_PREPARE])],
 [$as_echo_n $1])
 
 
@@ -898,16 +898,16 @@ fi
 # AS_TEST_X
 # ---------
 # Check whether a file has executable or search permissions.
-m4_defun([AS_TEST_X],
-[AS_REQUIRE([_AS_TEST_PREPARE])]dnl
+m4_defun_init([AS_TEST_X],
+[AS_REQUIRE([_AS_TEST_PREPARE])],
 [$as_test_x $1[]])# AS_TEST_X
 
 
 # AS_EXECUTABLE_P
 # ---------------
 # Check whether a file is a regular file that has executable permissions.
-m4_defun([AS_EXECUTABLE_P],
-[AS_REQUIRE([_AS_TEST_PREPARE])]dnl
+m4_defun_init([AS_EXECUTABLE_P],
+[AS_REQUIRE([_AS_TEST_PREPARE])],
 [{ test -f $1 && AS_TEST_X([$1]); }])# AS_EXECUTABLE_P
 
 
@@ -1045,8 +1045,8 @@ rmdir conf$$.dir 2>/dev/null
 # -------------------
 # FIXME: Should we add the glue code to handle properly relative symlinks
 # simulated with `ln' or `cp'?
-m4_defun([AS_LN_S],
-[AS_REQUIRE([_AS_LN_S_PREPARE])]dnl
+m4_defun_init([AS_LN_S],
+[AS_REQUIRE([_AS_LN_S_PREPARE])],
 [$as_ln_s $1 $2])
 
 
@@ -1077,8 +1077,8 @@ m4_define([_AS_MKDIR_P],
 # AS_MKDIR_P(DIR)
 # ---------------
 # Emulate `mkdir -p' with plain `mkdir' if needed.
-m4_define([AS_MKDIR_P],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_MKDIR_P],
+[AS_REQUIRE([_$0_PREPARE])],
 [as_dir=$1; as_func_mkdir_p])# AS_MKDIR_P
 
 
@@ -1126,8 +1126,8 @@ fi
 # expansions, not on literal text.  This closes a longstanding sh security
 # hole.  Optimize it away when not needed, i.e., if there are no literal
 # path separators.
-m4_define([_AS_PATH_WALK],
-[AS_REQUIRE([_AS_PATH_SEPARATOR_PREPARE])]dnl
+m4_defun_init([_AS_PATH_WALK],
+[AS_REQUIRE([_AS_PATH_SEPARATOR_PREPARE])],
 [as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
 m4_bmatch([$1], [[:;]],
 [as_dummy="$1"
@@ -1498,8 +1498,8 @@ m4_defun([_AS_VERSION_COMPARE_PREPARE],
 #
 # This usage is portable even to ancient awk,
 # so don't worry about finding a "nice" awk version.
-m4_defun([AS_VERSION_COMPARE],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_VERSION_COMPARE],
+[AS_REQUIRE([_$0_PREPARE])],
 [as_arg_v1=$1
 as_arg_v2=$2
 awk "$as_awk_strverscmp" v1="$as_arg_v1" v2="$as_arg_v2" /dev/null
@@ -1557,8 +1557,8 @@ as_tr_sh="eval sed 'y%*+%pp%;s%[[^_$as_cr_alnum]]%_%g'"
 # fact to skip worrying about the length of m4_cr_not_symbols2.
 #
 # For speed, we inline the literal definitions that can be computed up front.
-m4_defun([AS_TR_SH],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_TR_SH],
+[AS_REQUIRE([_$0_PREPARE])],
 [AS_LITERAL_IF([$1],
              [m4_translit([$1], [*+[]]]]dnl
 m4_dquote(m4_dquote(m4_defn([m4_cr_not_symbols2])))[[,
@@ -1584,8 +1584,8 @@ as_tr_cpp="eval sed 
'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[[^_$as_cr_alnum]]%_%g
 # of `$as_tr_cpp' if you change this.
 #
 # See implementation comments in AS_TR_SH.
-m4_defun([AS_TR_CPP],
-[AS_REQUIRE([_$0_PREPARE])]dnl
+m4_defun_init([AS_TR_CPP],
+[AS_REQUIRE([_$0_PREPARE])],
 [AS_LITERAL_IF([$1],
              [m4_translit([$1], [*[]]]]dnl
 m4_dquote(m4_dquote(m4_defn([m4_cr_letters])m4_defn([m4_cr_not_symbols2])))[[,
@@ -1659,9 +1659,9 @@ m4_define([_AS_VAR_APPEND_WORKS],
 # contents of VAR outweigh the typical VALUE size of repeated appends.
 # Note that unlike AS_VAR_SET, VALUE must be properly quoted to avoid
 # field splitting and file name expansion.
-m4_define([AS_VAR_APPEND],
+m4_defun_init([AS_VAR_APPEND],
 [_AS_DETECT_SUGGESTED([_AS_VAR_APPEND_WORKS])]dnl
-[AS_REQUIRE([_AS_VAR_APPEND_PREPARE], [], [M4SH-INIT-FN])]dnl
+[AS_REQUIRE([_AS_VAR_APPEND_PREPARE], [], [M4SH-INIT-FN])],
 [as_func_append $1 $2])
 
 
@@ -1741,8 +1741,8 @@ m4_define([AS_VAR_POPDEF],
 # calls that trigger AS_LITERAL_IF([VARNAME]), and that macro performs
 # expansion inside an argument collection context, where diversions
 # don't work.  Therefore, we must require the preparation ourselves.
-m4_define([AS_VAR_PUSHDEF],
-[AS_REQUIRE([_AS_TR_SH_PREPARE])]dnl
+m4_defun_init([AS_VAR_PUSHDEF],
+[AS_REQUIRE([_AS_TR_SH_PREPARE])],
 [AS_LITERAL_IF([$2],
               [m4_pushdef([$1], [AS_TR_SH($2)])],
               [as_$1=AS_TR_SH($2)
diff --git a/lib/m4sugar/m4sugar.m4 b/lib/m4sugar/m4sugar.m4
index d0fe9ad..13a1ddb 100644
--- a/lib/m4sugar/m4sugar.m4
+++ b/lib/m4sugar/m4sugar.m4
@@ -1644,25 +1644,51 @@ m4_define([m4_divert_require],
   [_m4_require_call([$2], [$3], [$1])])])
 
 
-# m4_defun(NAME, EXPANSION)
-# -------------------------
-# Define a macro which automatically provides itself.  Add machinery
-# so the macro automatically switches expansion to the diversion
-# stack if it is not already using it.  In this case, once finished,
-# it will bring back all the code accumulated in the diversion stack.
-# This, combined with m4_require, achieves the topological ordering of
-# macros.  We don't use this macro to define some frequently called
-# macros that are not involved in ordering constraints, to save m4
-# processing.
+# m4_defun(NAME, EXPANSION, [MACRO = m4_define])
+# ----------------------------------------------
+# Define a macro NAME which automatically provides itself.  Add
+# machinery so the macro automatically switches expansion to the
+# diversion stack if it is not already using it, prior to EXPANSION.
+# In this case, once finished, it will bring back all the code
+# accumulated in the diversion stack.  This, combined with m4_require,
+# achieves the topological ordering of macros.  We don't use this
+# macro to define some frequently called macros that are not involved
+# in ordering constraints, to save m4 processing.
+#
+# MACRO is an undocumented argument; when set to m4_pushdef, and NAME
+# is already defined, the new definition is added to the pushdef
+# stack, rather than overwriting the current definition.  It can thus
+# be used to write self-modifying macros, which pop themselves to a
+# previously m4_define'd definition so that subsequent use of the
+# macro is faster.
 m4_define([m4_defun],
 [m4_define([m4_location($1)], m4_location)dnl
-m4_define([$1],
+m4_default([$3], [m4_define])([$1],
          [_m4_defun_pro([$1])$2[]_m4_defun_epi([$1])])])
 
 
+# m4_defun_init(NAME, INIT, COMMON)
+# ---------------------------------
+# Like m4_defun, but split EXPANSION into two portions: INIT which is
+# done only the first time NAME is invoked, and COMMON which is
+# expanded every time.
+#
+# For now, the COMMON definition is always m4_define'd, giving an even
+# lighter-weight definition.  m4_defun allows self-providing, but once
+# a macro is provided, m4_require no longer cares if it is m4_define'd
+# or m4_defun'd.  m4_defun also provides location tracking to identify
+# dependency bugs, but once the INIT has been expanded, we know there
+# are no dependency bugs.  However, if a future use needs COMMON to be
+# m4_defun'd, we can add a parameter, similar to the third parameter
+# to m4_defun.
+m4_define([m4_defun_init],
+[m4_define([$1], [$3])m4_defun([$1],
+   [$2[]_m4_popdef(]m4_dquote([$][0])[)$][0($][@)], [m4_pushdef])])
+
+
 # m4_defun_once(NAME, EXPANSION)
 # ------------------------------
-# As m4_defun, but issues the EXPANSION only once, and warns if used
+# Like m4_defun, but issues the EXPANSION only once, and warns if used
 # several times.
 m4_define([m4_defun_once],
 [m4_define([m4_location($1)], m4_location)dnl
diff --git a/tests/m4sugar.at b/tests/m4sugar.at
index deb6f8c..cfaed3b 100644
--- a/tests/m4sugar.at
+++ b/tests/m4sugar.at
@@ -195,6 +195,31 @@ autom4te: m4 failed with exit status: 1
 AT_CLEANUP
 
 
+## ---------------------- ##
+## m4_require: one-shot.  ##
+## ---------------------- ##
+
+AT_SETUP([m4@&address@hidden: one-shot initialization])
+AT_KEYWORDS([m4@&address@hidden)
+
+AT_CHECK_M4SUGAR_TEXT([[
+m4_defun_init([a], [[init a
+]], [[common a]])dnl
+m4_defun([b], [[b]m4_require([a])])dnl
+m4_defun([c], [[c]m4_require([a])])dnl
+b
+c
+a
+]], [[
+init a
+common a
+b
+c
+common a
+]])
+AT_CLEANUP
+
+
 ## --------- ##
 ## m4_cond.  ##
 ## --------- ##
-- 
1.6.0.2


reply via email to

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