* lib/m4sugar/m4sugar.m4 (m4_ignore, m4_unquote): New macros.
(m4_version_prereq): Inline constant expansions.
(m4_list_cmp): Reduce number of expansions, by avoiding m4_case.
Rewrite in terms of [] list, not () list.
(_m4_list_cmp, _m4_version_unletter): New helper macros.
(m4_version_unletter): Write wrapper around new implementation to
preserve old semantics.
(m4_version_compare): Pass correct type of list, and avoid
overhead of flattening expressions too early.
(m4_do): Move to be near other quoting macros.
(m4_max, m4_min): Always result in decimal output.
* doc/autoconf.texi (Looping constructs): Add m4_car, m4_cdr.
Move m4_do...
(Evaluation Macros): ...here. Add m4_ignore, m4_unquote.
(Text processing Macros): Move m4_version_compare...
(Number processing Macros): ...to this new node; document m4_cmp,
m4_list_cmp, m4_sign, m4_max, m4_min.
* tests/m4sugar.at (m4@&t@_version_compare): Enhance test, to pick
up on bugs fixed by this patch.
* NEWS: Document new macros.
Signed-off-by: Eric Blake <ebb9@byu.net>
+2007-10-13 Eric Blake <ebb9@byu.net>
+
+ Make AC_PREREQ faster and more robust.
+ * lib/m4sugar/m4sugar.m4 (m4_ignore, m4_unquote): New macros.
+ (m4_version_prereq): Inline constant expansions.
+ (m4_list_cmp): Reduce number of expansions, by avoiding m4_case.
+ Rewrite in terms of [] list, not () list.
+ (_m4_list_cmp, _m4_version_unletter): New helper macros.
+ (m4_version_unletter): Write wrapper around new implementation to
+ preserve old semantics.
+ (m4_version_compare): Pass correct type of list, and avoid
+ overhead of flattening expressions too early.
+ (m4_do): Move to be near other quoting macros.
+ (m4_max, m4_min): Always result in decimal output.
+ * doc/autoconf.texi (Looping constructs): Add m4_car, m4_cdr.
+ Move m4_do...
+ (Evaluation Macros): ...here. Add m4_ignore, m4_unquote.
+ (Text processing Macros): Move m4_version_compare...
+ (Number processing Macros): ...to this new node; document m4_cmp,
+ m4_list_cmp, m4_sign, m4_max, m4_min.
+ * tests/m4sugar.at (m4@&t@_version_compare): Enhance test, to pick
+ up on bugs fixed by this patch.
+ * NEWS: Document new macros.
+
2007-10-12 Eric Blake <ebb9@byu.net>
* doc/autoconf.texi (Text processing Macros): Fix bad merge.
- The following macros were previously available as undocumented
interfaces; the macros are now documented as stable interfaces.
- __oline__ m4_assert m4_bmatch m4_bpatsubsts m4_case
- m4_default m4_divert_once m4_divert_pop m4_divert_push
+ __oline__ m4_assert m4_bmatch m4_bpatsubsts m4_car m4_case
+ m4_cdr m4_default m4_divert_once m4_divert_pop m4_divert_push
m4_divert_text m4_do m4_errprintn m4_fatal m4_flatten
m4_ifndef m4_ifset m4_ifval m4_ifvaln m4_location
- m4_n m4_shiftn m4_strip m4_version_compare m4_warn
+ m4_n m4_shiftn m4_strip m4_warn
- The following macros were previously available as undocumented
- interfaces, but had bugs. Packages that relied on the
- undocumented and buggy behavior should analyze their code to make
- sure it still works with the new documented behavior.
+ interfaces, but had bug fixes or semantic changes as part of this
+ release. Packages that relied on the undocumented behavior
+ should be analyzed to make sure they will still work with the
+ new documented behavior.
- m4_join m4_text_box m4_text_wrap
+ m4_cmp m4_list_cmp m4_join m4_sign m4_text_box m4_text_wrap
+ m4_version_compare
- Packages using the undocumented m4sugar macro m4_PACKAGE_VERSION
should consider using the new AC_AUTOCONF_VERSION instead.
be used to take action depending on whether anything was appended.
** The following m4sugar macros are new:
- m4_cond m4_newline m4_shift2 m4_shift3
+ m4_cond m4_ignore m4_max m4_min m4_newline m4_shift2
+ m4_shift3 m4_unquote
** Warnings are now generated by default when an installer invokes
'configure' with an unknown --enable-* or --with-* option.
* Looping constructs:: Iteration in M4
* Evaluation Macros:: More quotation and evaluation control
* Text processing Macros:: String manipulation in M4
+* Number processing Macros:: Arithmetic computation in M4
* Forbidden Patterns:: Catching unexpanded macros
Writing Autoconf Macros
* Looping constructs:: Iteration in M4
* Evaluation Macros:: More quotation and evaluation control
* Text processing Macros:: String manipulation in M4
+* Number processing Macros:: Arithmetic computation in M4
* Forbidden Patterns:: Catching unexpanded macros
@end menu
@node Looping constructs
@subsection Looping constructs
-The following macros implement loops in M4.
+The following macros are useful in implementing recursive algorithms in
+M4, including loop operations. An M4 list is formed by quoting a list
+of quoted elements; generally the lists are comma-separated, although
+@code{m4_foreach_w} is whitespace-separated. For example, the list
+@samp{[[a], [b,c]]} contains two elements: @samp{[a]} and @samp{[b,c]}.
+It is common to see lists with unquoted elements when those elements are
+not likely to be macro names, as in @samp{[fputc_unlocked,
+fgetc_unlocked]}.
+
+@defmac m4_car (@var{list})
+@msindex{car}
+Expands to the quoted first element of the comma-separated quoted
+@var{list}. Often used with @code{m4_cdr} to recursively iterate
+through a list. Generally, when using quoted lists of quoted elements,
+@code{m4_car} should be called without any extra quotes.
+@end defmac
+
+@defmac m4_cdr (@var{list})
+@msindex{cdr}
+Expands to a quoted list of all but the first element of the
+comma-separated quoted @var{list}, or the empty string if @var{list} had
+only one element. Generally, when using quoted lists of quoted
+elements, @code{m4_cdr} should be called without any extra quotes.
+
+For example, this is an implementation of @code{m4_map}; note how each
+iteration of the helper macro @code{_m4_map} checks for the end of
+recursion, then merely applies the first argument to the first element
+of the list, then recurses with the rest of the list.
+@example
+m4_define([m4_map], [m4_if([$2], [[]], [], [_$0($@@)])])dnl
+m4_define([_m4_map], [m4_ifval([$2],
+ [$1(m4_unquote(m4_car($2)))[]$0([$1], m4_cdr($2))])])dnl
+m4_map([ m4_eval], [[1],[1+1]])
+@result{} 1 2
+@end example
+@end defmac
@defmac m4_for (@var{var}, @var{first}, @var{last}, @ovar{step}, @
@var{expression})
@example
m4_foreach([myvar], [[foo], [bar, baz]],
[echo myvar
-])
-
+])dnl
+@result{}echo foo
+@result{}echo bar, baz
@end example
@end defmac
@code{m4_foreach_w}.
@end defmac
-The following macros are useful in implementing recursive algorithms.
-
-@defmac m4_do (@dots{})
-@msindex{do}
-This macro loops over its arguments and expands each one in sequence.
-Its main use is for readability; it allows the use of indentation and
-fewer @code{dnl} to result in the same expansion.
-@end defmac
+@c TODO document m4_map, m4_map_sep
@defmac m4_shiftn (@var{count}, @dots{})
@defmacx m4_shift2 (@dots{})
@subsection Evaluation Macros
The following macros give some control over the order of the evaluation
-by adding or removing levels of quotes. They are meant for hard-core M4
-programmers.
+by adding or removing levels of quotes.
+
+@defmac m4_do (@var{arg1}, @dots{})
+@msindex{do}
+This macro loops over its arguments and expands each @var{arg} in
+sequence. Its main use is for readability; it allows the use of
+indentation and fewer @code{dnl} to result in the same expansion.
+@end defmac
@defmac m4_dquote (@var{arg1}, @dots{})
@msindex{dquote}
Return the arguments as a quoted list of quoted arguments.
+Conveniently, if there is just one @var{arg}, this effectively adds a
+level of quoting.
@end defmac
+@defmac m4_ignore (@dots{})
+@msindex{ignore}
+This macro was introduced in Autoconf 2.62. Expands to nothing,
+ignoring all of its arguments. By itself, this isn't very useful.
+However, it can be used to conditionally ignore an arbitrary number of
+arguments, by making a decision about which macro name to apply to a
+list of arguments.
+@example
+dnl foo outputs a message only if [debug] is defined.
+m4_define([foo],
+[m4_ifdef([debug],[AC_MSG_NOTICE],[m4_ignore])([debug message])])
+@end example
+
+Note that for earlier versions of Autoconf, the macro @code{__gnu__} can
+serve the same purpose, although it is less readable.
+@end defmac
+
+@c m4_noquote is too dangerous to document - it invokes macros that
+@c probably rely on @samp{[]} nested quoting for proper operation. The
+@c user should generally prefer m4_unquote instead.
+
@defmac m4_quote (@var{arg1}, @dots{})
@msindex{quote}
Return the arguments as a single entity, i.e., wrap them into a pair of
-quotes.
+quotes. This effectively collapses multiple arguments into one,
+although it loses whitespace after unquoted commas in the process.
+@end defmac
+
+@defmac m4_unquote (@var{arg1}, @dots{})
+@msindex{unquote}
+This macro was introduced in Autoconf 2.62. Expand each argument,
+separated by commas. For a single @var{arg}, this effectively removes a
+layer of quoting, and @code{m4_unquote([@var{arg}])} is more efficient
+than the equivalent @code{m4_do([@var{arg}])}. For multiple arguments,
+this results in an unquoted list of expansions. This is commonly used
+with @code{m4_split}, in order to convert a single quoted list into a
+series of quoted elements.
@end defmac
The following example aims at emphasizing the difference between (i), not
@node Text processing Macros
-@subsection Text processing Macros
+@subsection String manipulation in M4
The following macros may be used to manipulate strings in M4.
They are not intended for casual use.
respectively.
@end defmac
+@node Number processing Macros
+@subsection Arithmetic computation in M4
+
+The following macros make it easier to deal with arithmetic operations.
+Where a parameter is documented as taking an arithmetic expression, you
+can use anything that can be parsed by @code{m4_eval}.
+
+@defmac m4_cmp (@var{expr-1}, @var{expr-2})
+@msindex{cmp}
+Compare the arithmetic expressions @var{expr-1} and @var{expr-2}, and
+expand to @samp{-1} if @var{expr-1} is smaller, @samp{0} if they are
+equal, and @samp{1} if @var{expr-1} is larger.
+@end defmac
+
+@defmac m4_list_cmp (@var{list-1}, @var{list-2})
+@msindex{list_cmp}
+Compare the two M4 lists consisting of comma-separated arithmetic
+expressions, left to right. Expand to @samp{-1} for the first element
+pairing where the value from @var{list-1} is smaller, @samp{1} where the
+value from @var{list-2} is smaller, or @samp{0} if both lists have the
+same values. If one list is shorter than the other, the remaining
+elements of the longer list are compared against 0.
+@example
+m4_list_cmp([1, 0], [1])
+@result{}0
+m4_list_cmp([1, [1 * 0]], [1, 0])
+@result{}0
+m4_list_cmp([1, 2], [1, 0])
+@result{}1
+m4_list_cmp([1, [1+1], 3],[1, 2])
+@result{}1
+m4_list_cmp([1, 2, -3], [1, 2])
+@result{}-1
+m4_list_cmp([1, 0], [1, 2])
+@result{}-1
+m4_list_cmp([1], [1, 2])
+@result{}-1
+@end example
+@end defmac
+
+@defmac m4_max (@var{arg1}, @dots{})
+@msindex{max}
+This macro was introduced in Autoconf 2.62. Expand to the decimal value
+of the maximum arithmetic expression among all the arguments.
+@end defmac
+
+@defmac m4_min (@var{arg1}, @dots{})
+@msindex{min}
+This macro was introduced in Autoconf 2.62. Expand to the decimal value
+of the minimum arithmetic expression among all the arguments.
+@end defmac
+
+@defmac m4_sign (@var{expr})
+@msindex{sign}
+Expand to @samp{-1} if the arithmetic expression @var{expr} is negative,
+@samp{1} if it is positive, and @samp{0} if it is zero.
+@end defmac
+
@anchor{m4_version_compare}
@defmac m4_version_compare (@var{version-1}, @var{version-2})
@msindex{version_compare}
-This macro was introduced in Autoconf 2.53. Compare the version strings
-@var{version-1} and @var{version-2}, and expand to @samp{-1} if
-@var{version-1} is smaller, @samp{0} if they are the same, or @samp{1}
-@var{version-2} is smaller. Version strings must be a list of elements
-separated by @samp{.}, where each element is a number along with an
-optional lower case letter. The comparison stops at the leftmost
-element that contains a difference, although a 0 element compares equal
-to a missing element.
+This macro was introduced in Autoconf 2.53, but had a number of
+usability limitations that were not lifted until Autoconf 2.62. Compare
+the version strings @var{version-1} and @var{version-2}, and expand to
+@samp{-1} if @var{version-1} is smaller, @samp{0} if they are the same,
+or @samp{1} @var{version-2} is smaller. Version strings must be a list
+of elements separated by @samp{.}, where each element is a number along
+with optional case-insensitive letters designating beta releases. The
+comparison stops at the leftmost element that contains a difference,
+although a 0 element compares equal to a missing element.
@example
m4_version_compare([1.1], [2.0])
@result{}1
m4_version_compare([1.0], [1])
@result{}0
+m4_version_compare([1.1pre], [1.1PRE])
+@result{}0
m4_version_compare([1.1a], [1,10])
@result{}-1
@end example
m4_shift3($@))])])
-
-# m4_do(STRING, ...)
-# ------------------
-# This macro invokes all its arguments (in sequence, of course). It is
-# useful for making your macros more structured and readable by dropping
-# unnecessary dnl's and have the macros indented properly.
-m4_define([m4_do],
-[m4_if($#, 0, [],
- $#, 1, [$1],
- [$1[]m4_do(m4_shift($@))])])
-
-
# m4_define_default(MACRO, VALUE)
# -------------------------------
# If MACRO is undefined, set it to VALUE.
## 7. Quoting manipulation. ##
## ------------------------- ##
-# m4_quote(ARGS)
-# --------------
-# Return ARGS as a single argument. Any whitespace after unquoted commas
-# is stripped.
-#
-# It is important to realize the difference between `m4_quote(exp)' and
-# `[exp]': in the first case you obtain the quoted *result* of the
-# expansion of EXP, while in the latter you just obtain the string
-# `exp'.
-m4_define([m4_quote], [[$*]])
+# m4_do(STRING, ...)
+# ------------------
+# This macro invokes all its arguments (in sequence, of course). It is
+# useful for making your macros more structured and readable by dropping
+# unnecessary dnl's and have the macros indented properly.
+m4_define([m4_do],
+[m4_if([$#], 0, [],
+ [$#], 1, [$1],
+ [$1[]m4_do(m4_shift($@))])])
# m4_dquote(ARGS)
m4_define([m4_dquote], [[$@]])
+# m4_ignore(ARGS)
+# ---------------
+# Expands to nothing. Useful for conditionally ignoring an arbitrary
+# number of arguments (see _m4_list_cmp for an example).
+m4_define([m4_ignore])
+
+
# m4_noquote(STRING)
# ------------------
# Return the result of ignoring all quotes in STRING and invoking the
# macros it contains. Amongst other things, this is useful for enabling
# macro invocations inside strings with [] blocks (for instance regexps
-# and help-strings).
+# and help-strings). On the other hand, since all quotes are disabled,
+# any macro expanded during this time that relies on nested [] quoting
+# will likely crash and burn. This macro is seldom useful; consider
+# m4_unquote instead.
m4_define([m4_noquote],
[m4_changequote(-=<{,}>=-)$1-=<{}>=-m4_changequote([,])])
+# m4_quote(ARGS)
+# --------------
+# Return ARGS as a single argument. Any whitespace after unquoted commas
+# is stripped.
+#
+# It is important to realize the difference between `m4_quote(exp)' and
+# `[exp]': in the first case you obtain the quoted *result* of the
+# expansion of EXP, while in the latter you just obtain the string
+# `exp'.
+m4_define([m4_quote], [[$*]])
+
+
+# m4_unquote(ARGS)
+# ----------------
+# Remove one layer of quotes from each ARG, performing one level of
+# expansion. For one argument, m4_unquote([arg]) is more efficient than
+# m4_do([arg]), but for multiple arguments, the difference is that
+# m4_unquote separates arguments with commas while m4_do concatenates.
+m4_define([m4_unquote], [$*])
+
+
## -------------------------- ##
## 8. Implementing m4 loops. ##
## -------------------------- ##
# m4_list_cmp(A, B)
# -----------------
#
-# Compare the two lists of integers A and B. For instance:
-# m4_list_cmp((1, 0), (1)) -> 0
-# m4_list_cmp((1, 0), (1, 0)) -> 0
-# m4_list_cmp((1, 2), (1, 0)) -> 1
-# m4_list_cmp((1, 2, 3), (1, 2)) -> 1
-# m4_list_cmp((1, 2, -3), (1, 2)) -> -1
-# m4_list_cmp((1, 0), (1, 2)) -> -1
-# m4_list_cmp((1), (1, 2)) -> -1
+# Compare the two lists of integer expressions A and B. For instance:
+# m4_list_cmp([1, 0], [1]) -> 0
+# m4_list_cmp([1, 0], [1, 0]) -> 0
+# m4_list_cmp([1, 2], [1, 0]) -> 1
+# m4_list_cmp([1, 2, 3], [1, 2]) -> 1
+# m4_list_cmp([1, 2, -3], [1, 2]) -> -1
+# m4_list_cmp([1, 0], [1, 2]) -> -1
+# m4_list_cmp([1], [1, 2]) -> -1
+# m4_define([xa], [oops])dnl
+# m4_list_cmp([[0xa]], [5+5]) -> 0
+#
+# Rather than face the overhead of m4_case, we use a helper function whose
+# expansion includes the name of the macro to invoke on the tail, either
+# m4_ignore or m4_unquote. This is particularly useful when comparing
+# long lists, since less text is being expanded to determine when to recurse.
m4_define([m4_list_cmp],
-[m4_if([$1$2], [()()], 0,
- [$1], [()], [$0((0), [$2])],
- [$2], [()], [$0([$1], (0))],
- [m4_case(m4_cmp(m4_car$1, m4_car$2),
- -1, -1,
- 1, 1,
- 0, [$0((m4_shift$1), (m4_shift$2))])])])
-
-# m4_max(A, B, ...)
-# m4_min(A, B, ...)
+[m4_if([$1$2], [], 0,
+ [$1], [], [$0(0, [$2])],
+ [$2], [], [$0([$1], 0)],
+ [$1], [$2], 0,
+ [_$0(m4_cmp(m4_car($1), m4_car($2)))([$0(m4_cdr($1), m4_cdr($2))])])])
+m4_define([_m4_list_cmp],
+[m4_if([$1], 0, [m4_unquote], [$1m4_ignore])])
+
+# m4_max(EXPR, ...)
+# m4_min(EXPR, ...)
# -----------------
-# Return the maximum (or minimum) of a series of integer expressions.
+# Return the decimal value of the maximum (or minimum) in a series of
+# integer expressions.
#
# M4 1.4.x doesn't provide ?:. Hence this huge m4_eval. Avoid m4_eval
# if both arguments are identical, but be aware of m4_max(0xa, 10) (hence
# the use of <=, not just <, in the second multiply).
m4_define([m4_max],
[m4_if([$#], [0], [m4_fatal([too few arguments to $0])],
- [$#], [1], [$1],
- [$#$1], [2$2], [$1],
+ [$#], [1], [m4_eval([$1])],
+ [$#$1], [2$2], [m4_eval([$1])],
[$#], [2],
[m4_eval((([$1]) > ([$2])) * ([$1]) + (([$1]) <= ([$2])) * ([$2]))],
[$0($0([$1], [$2]), m4_shift2($@))])])
m4_define([m4_min],
[m4_if([$#], [0], [m4_fatal([too few arguments to $0])],
- [$#], [1], [$1],
- [$#$1], [2$2], [$1],
+ [$#], [1], [m4_eval([$1])],
+ [$#$1], [2$2], [m4_eval([$1])],
[$#], [2],
[m4_eval((([$1]) < ([$2])) * ([$1]) + (([$1]) >= ([$2])) * ([$2]))],
[$0($0([$1], [$2]), m4_shift2($@))])])
# m4_version_unletter(VERSION)
# ----------------------------
-# Normalize beta version numbers with letters to numbers only for comparison.
+# Normalize beta version numbers with letters to numeric expressions, which
+# can then be handed to m4_eval for the purpose of comparison.
#
# Nl -> (N+1).-1.(l#)
#
-#i.e., 2.14a -> 2.15.-1.1, 2.14b -> 2.15.-1.2, etc.
-# This macro is absolutely not robust to active macro, it expects
-# reasonable version numbers and is valid up to `z', no double letters.
+# for example:
+# [2.14a] -> [2.14+1.-1.[0r36:a]] -> 2.15.-1.10
+# [2.14b] -> [2.15+1.-1.[0r36:b]] -> 2.15.-1.11
+# [2.61aa.b] -> [2.61+1.-1.[0r36:aa],+1.-1.[0r36:b]] -> 2.62.-1.370.1.-1.11
+#
+# This macro expects reasonable version numbers, but can handle double
+# letters and does not expand one-letter macros. Inline constant expansions,
+# to avoid m4_defn overhead. _m4_version_unletter is the real workhorse
+# used by m4_version_compare, but since [0r36:a] is less readable than 10,
+# we provide a wrapper for human use.
m4_define([m4_version_unletter],
-[m4_translit(m4_bpatsubsts(m4_tolower([[$1]]),
- [\([0-9]+\)\([abcdefghi]\)],
- [m4_eval(\1 + 1).-1.\2],
- [\([0-9]+\)\([jklmnopqrs]\)],
- [m4_eval(\1 + 1).-1.1\2],
- [\([0-9]+\)\([tuvwxyz]\)],
- [m4_eval(\1 + 1).-1.2\2]),
- [abcdefghijklmnopqrstuvwxyz],
- [12345678901234567890123456])])
+[m4_map_sep([m4_eval], [.], _$0([$1]))])
+m4_define([_m4_version_unletter],
+[m4_translit(m4_bpatsubst([[[$1]]], ]dnl
+m4_dquote(m4_dquote(m4_defn([m4_cr_Letters])))[[+],
+ [+1.-1.[0r36:\&]]),
+ [.], [,])])
# m4_version_compare(VERSION-1, VERSION-2)
# 0 if =
# 1 if >
m4_define([m4_version_compare],
-[m4_list_cmp((m4_translit(m4_version_unletter([$1]), [.], [,])),
- (m4_translit(m4_version_unletter([$2]), [.], [,])))])
+[m4_list_cmp(_m4_version_unletter([$1]), _m4_version_unletter([$2]))])
# m4_PACKAGE_NAME
# ----------------------------------------------------
# Check this Autoconf version against VERSION.
m4_define([m4_version_prereq],
-[m4_if(m4_version_compare(m4_defn([m4_PACKAGE_VERSION]), [$1]), -1,
+[m4_if(m4_version_compare(]m4_dquote(m4_defn([m4_PACKAGE_VERSION]))[, [$1]),
+ [-1],
[m4_default([$3],
[m4_fatal([Autoconf version $1 or higher is required],
- 63)])],
- [$2])[]dnl
-])
+ [63])])],
+ [$2])])
m4_version_compare([1.0], [1])
m4_version_compare([1.0a], [1.0a])
m4_version_compare([1.1a], [1.1a.1])
+m4_version_compare([1.10], [1.1a])
+m4_version_compare([1.1a], [1.1A])
+m4_define([a], [oops])dnl
+m4_version_compare([1.1a], [1.1A])
+m4_version_compare([1z], [1aa])
]],
[[-1
1
0
0
-1
+1
+0
+0
+-1
]])
AT_CLEANUP