diff --git a/doc/autoconf.texi b/doc/autoconf.texi index f78e5fc53..5e72add93 100644 --- a/doc/autoconf.texi +++ b/doc/autoconf.texi @@ -909,6 +909,9 @@ This module contains a source file for the replacement header, along with an Autoconf macro that arranges to use the replacement header on old-fashioned systems. +For more information, consult the Gnulib website, +@uref{https://@/www.gnu.org/@/software/@/gnulib/}. + @node Libtool @section Libtool @@ -1877,21 +1880,88 @@ distribution tar ball names (e.g., @samp{autoconf}). It defaults to other than alphanumerics and underscores are changed to @samp{-}. If provided, @var{url} should be the home page for the package. -All the arguments of @code{AC_INIT} must be static, i.e., there should not -be any shell computation, quotes, or newlines, but they can be computed -by M4. This is because the package information strings are expanded at -M4 time into several contexts, and must give the same text at shell time -whether used in single-quoted strings, double-quoted strings, quoted -here-documents, or unquoted here-documents. It is permissible to use -@code{m4_esyscmd} or @code{m4_esyscmd_s} for computing a version string -that changes with every commit to a version control system (in fact, -Autoconf does just that, for all builds of the development tree made -between releases). - -The @var{tarname} argument is used to construct filenames. -In addition to being static, it should not contain wildcard -characters, white space, or anything else that could be troublesome -as part of a file or directory name. +Leading and trailing whitespace is stripped from all the arguments to +@code{AC_INIT}, and interior whitespace is collapsed to a single space. +This means that, for instance, if you want to put several email +addresses in @var{bug-report}, you can put each one on its own line: + +@smallexample +@group +# We keep having problems with the mail hosting for +# gnomovision.example, so give people an alternative. +AC_INIT([Gnomovision], [17.0.1], [ + bugs@@gnomovision.example + or gnomo-bugs@@reliable-email.example +]) +@end group +@end smallexample + +The arguments to @code{AC_INIT} may be computed by M4, when +@command{autoconf} is run. For instance, if you want to include the +package's version number in the @var{tarname}, but you don't want to +repeat it, you can use a helper macro: + +@smallexample +@group +m4_define([gnomo_VERSION], [17.0.1]) +AC_INIT([Gnomovision], + m4_defn([gnomo_VERSION]), + [bugs@@gnomovision.example], + [gnomo-]m4_defn([gnomo_VERSION])) +@end group +@end smallexample + +This uses @code{m4_defn} to produce the expansion of +@code{gnomo_VERSION} @emph{as a quoted string}, so that if there happen +to be any more M4 macro names in @code{gnomo_VERSION}, they will not be +expanded. @xref{Defn,,Renaming Macros,m4,GNU m4 macro processor}. + +Continuing this example, if you don't want to embed the version number +in @file{configure.ac} at all, you can use @code{m4_esyscmd} to look it +up somewhere else when @command{autoconf} is run: + +@smallexample +@group +m4_define([gnomo_VERSION], + m4_esyscmd([build-aux/git-version-gen .tarball-version])) +AC_INIT([Gnomovision], + m4_defn([gnomo_VERSION]), + [bugs@@gnomovision.example], + [gnomo-]m4_defn([gnomo_VERSION])) +@end group +@end smallexample + +This uses the utility script @command{git-version-gen} to look up +the package's version in its version control metadata. This script +is part of Gnulib (@pxref{Gnulib}). + +The arguments to @code{AC_INIT} are written into @file{configure} in +several different places. Therefore, we strongly recommend that you +write any M4 logic in @code{AC_INIT} arguments to be evaluated +@emph{before} @code{AC_INIT} itself is evaluated. For instance, in the +above example, the second argument to @code{m4_define} is @emph{not} +quoted, so the @code{m4_esyscmd} is evaluated only once, and +@code{gnomo_VERSION} is defined to the output of the command. If the +second argument to @code{m4_define} were quoted, @code{m4_esyscmd} would +be evaluated each time the @var{version} or @var{tarname} arguments were +written to @file{configure}, and the command would be run repeatedly. + +In some of the places where the arguments to @code{AC_INIT} are used, +within @file{configure}, shell evaluation cannot happen. Therefore, the +arguments to @code{AC_INIT} may @emph{not} be computed when +@command{configure} is run. If they contain any construct that isn't +always treated as literal by the shell (e.g.@: variable expansions), +@command{autoconf} will issue an error. + +The @var{tarname} argument is used to construct filenames. It should +not contain wildcard characters, white space, or anything else that +could be troublesome as part of a file or directory name. + +Some of M4's active characters (notably parentheses, square brackets, +@samp{,} and @samp{#}) commonly appear in URLs and lists of email +addresses. If any of these characters appear in an argument to AC_INIT, +that argument will probably need to be double-quoted to avoid errors +and mistranscriptions. @xref{M4 Quotation}. The following M4 macros (e.g., @code{AC_PACKAGE_NAME}), output variables (e.g., @code{PACKAGE_NAME}), and preprocessor symbols (e.g., diff --git a/lib/autoconf/general.m4 b/lib/autoconf/general.m4 index 90cb5a5af..f75b2dbb2 100644 --- a/lib/autoconf/general.m4 +++ b/lib/autoconf/general.m4 @@ -230,56 +230,49 @@ m4_define([_AC_INIT_LITERAL], # _AC_INIT_PACKAGE(PACKAGE-NAME, VERSION, BUG-REPORT, [TARNAME], [URL]) # --------------------------------------------------------------------- +# Set the values of AC_PACKAGE_{NAME,VERSION,STRING,BUGREPORT,TARNAME,URL} +# from the arguments. m4_define([_AC_INIT_PACKAGE], -[m4_pushdef([_ac_init_NAME], m4_normalize([$1])) -m4_pushdef([_ac_init_VERSION], m4_normalize([$2])) -m4_pushdef([_ac_init_BUGREPORT], m4_normalize([$3])) -m4_pushdef([_ac_init_TARNAME], m4_normalize([$4])) -m4_pushdef([_ac_init_URL], m4_normalize([$5])) -# NAME, VERSION, BUGREPORT, and URL should all be safe for use in shell -# strings of all kinds. -_AC_INIT_LITERAL(m4_defn([_ac_init_NAME])) -_AC_INIT_LITERAL(m4_defn([_ac_init_VERSION])) -_AC_INIT_LITERAL(m4_defn([_ac_init_BUGREPORT])) -_AC_INIT_LITERAL(m4_defn([_ac_init_URL])) +[_AC_INIT_PACKAGE_N(m4_normalize([$1]), m4_normalize([$2]), m4_normalize([$3]), + m4_normalize([$4]), m4_normalize([$5]))]) + +# _AC_INIT_PACKAGE_N(PACKAGE-NAME, VERSION, BUG-REPORT, [TARNAME], [URL]) +# ----------------------------------------------------------------------- +# Subroutine of _AC_INIT_PACKAGE. +m4_define([_AC_INIT_PACKAGE_N], +[# PACKAGE-NAME, VERSION, BUGREPORT, and URL should all be safe for use +# in shell strings of all kinds. +_AC_INIT_LITERAL([$1]) +_AC_INIT_LITERAL([$2]) +_AC_INIT_LITERAL([$3]) +_AC_INIT_LITERAL([$5]) + # TARNAME is even more constrained: it should not contain any shell # metacharacters or whitespace, because it is used to construct # filenames. -AS_LITERAL_WORD_IF(m4_defn([_ac_init_TARNAME]), [], +AS_LITERAL_WORD_IF([$4], [], [m4_warn([syntax], - [AC_INIT: unsafe as a filename: "]m4_defn([_ac_init_TARNAME])["])]) -# -# These do not use m4_copy because we don't want to copy the pushdef stack. -m4_ifndef([AC_PACKAGE_NAME], - [m4_define([AC_PACKAGE_NAME], - m4_defn([_ac_init_NAME]))]) -m4_ifndef([AC_PACKAGE_VERSION], - [m4_define([AC_PACKAGE_VERSION], - m4_defn([_ac_init_VERSION]))]) -m4_ifndef([AC_PACKAGE_STRING], - [m4_define([AC_PACKAGE_STRING], - m4_defn([_ac_init_NAME])[ ]m4_defn([_ac_init_VERSION]))]) -m4_ifndef([AC_PACKAGE_BUGREPORT], - [m4_define([AC_PACKAGE_BUGREPORT], _ac_init_BUGREPORT)]) -m4_ifndef([AC_PACKAGE_TARNAME], - [m4_define([AC_PACKAGE_TARNAME], - m4_default(m4_defn([_ac_init_TARNAME]), - [m4_bpatsubst(m4_tolower( - m4_bpatsubst(m4_defn([_ac_init_NAME]), - [GNU ])), - [[^_abcdefghijklmnopqrstuvwxyz0123456789]], - [-])]))]) -m4_ifndef([AC_PACKAGE_URL], - [m4_define([AC_PACKAGE_URL], - m4_default(m4_defn([_ac_init_URL]), - [m4_if(m4_index(m4_defn([_ac_init_NAME]), - [GNU ]), [0], - [[https://www.gnu.org/software/]m4_defn([AC_PACKAGE_TARNAME])[/]])]))]) -m4_popdef([_ac_init_NAME]) -m4_popdef([_ac_init_VERSION]) -m4_popdef([_ac_init_BUGREPORT]) -m4_popdef([_ac_init_TARNAME]) -m4_popdef([_ac_init_URL]) + [AC_INIT: unsafe as a filename: "$4"])]) + +m4_define_default([AC_PACKAGE_NAME], [$1]) +m4_define_default([AC_PACKAGE_VERSION], [$2]) + +# The m4_strip makes AC_PACKAGE_STRING be [], not [ ], when +# both $1 and $2 are empty. +m4_define_default([AC_PACKAGE_STRING], m4_strip([$1 $2])) +m4_define_default([AC_PACKAGE_BUGREPORT], [$3]) + +# N.B. m4_ifnblank strips one layer of quotation from whichever of its +# second and third argument it evaluates to. +m4_define_default([AC_PACKAGE_TARNAME], + m4_ifnblank([$4], [[$4]], + [m4_quote(m4_bpatsubst(m4_tolower(m4_bpatsubst([$1], [^GNU ], [])), + [[^_abcdefghijklmnopqrstuvwxyz0123456789]], [-]))])) +m4_define_default([AC_PACKAGE_URL], + m4_ifnblank([$5], [[$5]], + [m4_if(m4_index([$1], [GNU ]), [0], + [[https://www.gnu.org/software/]m4_defn([AC_PACKAGE_TARNAME])[/]], + [])])) ]) @@ -1445,7 +1438,7 @@ m4_define([_AS_FORCE_REEXEC_WITH_CONFIG_SHELL], [yes]) AS_INIT[]dnl AS_PREPARE[]dnl m4_divert_push([KILL]) -m4_ifval([$2], [_AC_INIT_PACKAGE($@)]) +m4_ifval([$2], [_AC_INIT_PACKAGE($@)], [_AC_INIT_PACKAGE()]) _AC_INIT_DEFAULTS _AC_INIT_PARSE_ARGS _AC_INIT_DIRCHECK diff --git a/tests/base.at b/tests/base.at index 2c5c4bdf4..45b5142e5 100644 --- a/tests/base.at +++ b/tests/base.at @@ -231,6 +231,38 @@ AT_CHECK([grep "^PACKAGE_TARNAME='fu'\$" configure], [], [ignore]) AT_CLEANUP +## ------------------------------- ## +## AC_INIT (obsolete invocation). ## +## ------------------------------- ## + +# When AC_INIT is called with only one argument, it is equivalent to +# passing that argument to AC_CONFIG_SRCDIR and then calling AC_INIT +# with _no_ arguments. In particular, PACKAGE should _not_ be set +# to whatever the first argument is. + +AT_SETUP([AC_INIT (obsolete invocation)]) + +AT_DATA([configure.ac], +[[AC_INIT([program.c]) +AC_OUTPUT +]]) + +AT_CHECK_AUTOCONF + +# Ensure we get the expected definition: +AT_CHECK([grep "^PACKAGE_NAME=''\$" configure], [], [ignore]) + +# Running autoconf right now should fail because program.c doesn't exist. +AT_CHECK_CONFIGURE([], [1], [], +[[configure: error: cannot find sources (program.c) in . or .. +]]) + +# Create program.c and then it should be fine. +AT_DATA([program.c]) +AT_CHECK_CONFIGURE + +AT_CLEANUP + ## ------------------------------------- ## ## AC_INIT with unusual version strings. ## @@ -238,36 +270,136 @@ AT_CLEANUP AT_SETUP([AC_INIT with unusual version strings]) +if echo 'ab*c' | grep -F 'ab*c' >/dev/null 2>&1; then + FGREP="grep -F" +else + FGREP=fgrep +fi + +# In both of the arguments that might contain URLs, make sure we test +# all of the unusual ASCII characters that commonly appear in URLs. +# +# The RFC 3986 "unreserved" characters are ASCII letters and digits, plus +# - . _ ~ +# The RFC 3986 "gen-delims" and "sub-delims" are +# / : ? # [ ] @ ! $ & ' ( ) * + , ; = +# The URL escape character is +# % +# +# Any argument that contains characters significant to M4, +# [ ] ( ) , +# may need to be double-quoted depending on exactly how they are used +# (as would an argument that contains a macro name, but we don't test +# that here). +# +# Characters that are still significant for Bourne shell within a +# single-quoted string, double-quoted string, quoted here-doc, or +# unquoted here-doc are explicitly not allowed: +# ' " ` \ $ +# +# We don't test unbalanced parentheses or brackets here. + AT_DATA([configure.ac], -[[AC_INIT([GNU String++ with spaces (foo)], +[[AC_INIT([GNU String++ with spaces + (foo)], [2.48++ (2010-07-03)], - [[https://example.com/?a=b&c=d#e]], + [[https://example/~bug/cdfijknoqrvw/-._:@!()[]*+,;/?y=z#1234567890]], [string++], - [[https://example.com/?f=g&h=i%2fj#42]]) + [[HTTPS://EXAMPLE/~PKG/BCDFIJNOQRUVW/-._:@!()[]*+,;/?y=z#1234567890]]) AC_OUTPUT ]]) -if echo 'ab*c' | grep -F 'ab*c' >/dev/null 2>&1; then - FGREP="grep -F" -else - FGREP=fgrep -fi +AT_CHECK_AUTOCONF([-Werror]) +AT_CHECK_CONFIGURE([-q]) + +AT_CHECK_CONFIGURE([--help], [], [stdout]) +AT_CHECK([[$FGREP \ + 'https://example/~bug/cdfijknoqrvw/-._:@!()[]*+,;/?y=z#1234567890' \ + stdout]], [], [ignore]) +AT_CHECK([[$FGREP \ + 'HTTPS://EXAMPLE/~PKG/BCDFIJNOQRUVW/-._:@!()[]*+,;/?y=z#1234567890' \ + stdout]], [], [ignore]) + +AT_CHECK_CONFIGURE([--version], [], [stdout]) +AT_CHECK([$FGREP 'GNU String++ with spaces (foo)' stdout], [], [ignore]) +AT_CHECK([$FGREP '2.48++ (2010-07-03)' stdout], [], [ignore]) + +AT_CHECK([./config.status --help], [], [stdout]) +AT_CHECK([[$FGREP \ + 'https://example/~bug/cdfijknoqrvw/-._:@!()[]*+,;/?y=z#1234567890' \ + stdout]], [], [ignore]) +AT_CHECK([[$FGREP \ + 'HTTPS://EXAMPLE/~PKG/BCDFIJNOQRUVW/-._:@!()[]*+,;/?y=z#1234567890' \ + stdout]], [], [ignore]) + +AT_CHECK([./config.status --version], [], [stdout]) +AT_CHECK([$FGREP 'GNU String++ with spaces (foo)' stdout], [], [ignore]) +AT_CHECK([$FGREP '2.48++ (2010-07-03)' stdout], [], [ignore]) + +# Repeat the above test using all the unusual characters that might appear +# in a list of email addresses in both BUG-REPORT and URL. (URL isn't +# supposed to contain email addresses, but there's no good reason to +# restrict its syntax.) +# The RFC 5822 "atext" characters are ASCII letters and digits, plus +# ! # $ % & ' * + - / = ? ^ _ ` { | } ~ +# The RFC 5822 "special" characters, all of which might appear somewhere +# in an address list, are +# ( ) < > [ ] : ; @ \ , . " +# As above, characters that are significant for Bourne shell within +# strings and heredocs are explicitly not allowed: +# ' " ` \ $ +# As above, any argument that contains characters significant to M4, +# [ ] ( ) , +# may need to be double-quoted depending on exactly how they are used +# (as would an argument that contains a macro name, but we don't test +# that here). + +AT_DATA([configure.ac], +[[AC_INIT([GNU String++ with spaces + (foo)], + [2.48++ (2010-07-03)], + [[bugs: fred , + G!H!I#J@K.L (wilma), + M%N&O@[156.247.38.49], + P*Q+R-S/T@U, + {jon|chyp~}@kqtvxz.VWXYZ;]], + [string++], + [[contact: jem , + F!G!H#I@fv.J (lugh), + K%L&M@[156.247.38.49], + N*O+P-Q/R@STU, + {qik|~prys}@wxz.VWXYZ;]]) +AC_OUTPUT +]]) AT_CHECK_AUTOCONF([-Werror]) AT_CHECK_CONFIGURE([-q]) + AT_CHECK_CONFIGURE([--help], [], [stdout]) -AT_CHECK([[$FGREP 'com/?a=b&c=d#e' stdout]], [], [ignore]) -AT_CHECK([[$FGREP 'com/?f=g&h=i%2fj#42' stdout]], [], [ignore]) +AT_CHECK([[$FGREP \ + 'bugs: fred , G!H!I#J@K.L (wilma), M%N&O@[156.247.38.49], P*Q+R-S/T@U, {jon|chyp~}@kqtvxz.VWXYZ;' \ + stdout]], [], [ignore]) +AT_CHECK([[$FGREP \ + 'contact: jem , F!G!H#I@fv.J (lugh), K%L&M@[156.247.38.49], N*O+P-Q/R@STU, {qik|~prys}@wxz.VWXYZ;' \ + stdout]], [], [ignore]) + AT_CHECK_CONFIGURE([--version], [], [stdout]) AT_CHECK([$FGREP 'GNU String++ with spaces (foo)' stdout], [], [ignore]) AT_CHECK([$FGREP '2.48++ (2010-07-03)' stdout], [], [ignore]) AT_CHECK([./config.status --help], [], [stdout]) -AT_CHECK([[$FGREP 'com/?a=b&c=d#e' stdout]], [], [ignore]) +AT_CHECK([[$FGREP \ + 'bugs: fred , G!H!I#J@K.L (wilma), M%N&O@[156.247.38.49], P*Q+R-S/T@U, {jon|chyp~}@kqtvxz.VWXYZ;' \ + stdout]], [], [ignore]) +AT_CHECK([[$FGREP \ + 'contact: jem , F!G!H#I@fv.J (lugh), K%L&M@[156.247.38.49], N*O+P-Q/R@STU, {qik|~prys}@wxz.VWXYZ;' \ + stdout]], [], [ignore]) + AT_CHECK([./config.status --version], [], [stdout]) AT_CHECK([$FGREP 'GNU String++ with spaces (foo)' stdout], [], [ignore]) AT_CHECK([$FGREP '2.48++ (2010-07-03)' stdout], [], [ignore]) +# Check for invalid characters in each argument. AT_DATA([configure.ac], [[AC_INIT([GNU "String++"], [2.48], [https://example.com/], [string++]) @@ -347,6 +479,81 @@ AC_OUTPUT AT_CHECK_AUTOCONF([-Werror], [1], [ignore], [stderr]) AT_CHECK([grep 'AC_INIT: unsafe as a filename: ' stderr], [], [ignore]) +# Commas and unbalanced close parentheses are especially troublesome for M4. +# They are only expected to work when double-quoted, but even then they can +# cause malfunctions if not handled carefully within Autoconf. + +AT_DATA([configure.ac], +[[AC_INIT([[GNU, String]], [2.48], [bugs@gstring.example], + [string], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([[GNU String)]], [2.48], [bugs@gstring.example], + [string], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [[2,48]], [bugs@gstring.example], + [string], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [[2.48)]], [bugs@gstring.example], + [string], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [2.48], [[bugs@gstring.example, + gstring-bugs@example.com]], + [string], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [2.48], [[bugs)@gstring.example]], + [string], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [2.48], [bugs@gstring.example], + [[string,]], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [2.48], [bugs@gstring.example], + [[string)]], [https://gstring.example/]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF([], [0], [ignore], [stderr]) +AT_CHECK([grep 'AC_INIT: unsafe as a filename: ' stderr], [], [ignore]) + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [2.48], [bugs@gstring.example], + [string], [[https://gstring.example/docs,html]]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF + +AT_DATA([configure.ac], +[[AC_INIT([GNU String], [2.48], [bugs@gstring.example], + [string], [[https://gstring.example/weird)/path]]) +AC_OUTPUT +]]) +AT_CHECK_AUTOCONF AT_CLEANUP