Skip to content

Commit

Permalink
AC_INIT: better handling of unusual arguments (#110349)
Browse files Browse the repository at this point in the history
Fix some subtle quotation bugs in _AC_INIT_PACKAGE that made it
impossible to put ‘,’ or an unbalanced close parenthesis in some of
the arguments to AC_INIT.  Document that arguments to AC_INIT
containing parentheses, square brackets, ‘,’ or ‘#’ may need to be
double-quoted.  Provide more detailed examples and exposition re
computing the arguments to AC_INIT when autoconf is run (e.g. with
git-version-gen).  Add a whole bunch more tests for unusual arguments
to AC_INIT, and a test that the backward-compatibility behavior of
AC_INIT with only one argument is still correct.

This may still break some of the existing configure scripts described
in the threads at
https://lists.gnu.org/r/autoconf/2020-10/msg00013.html and
https://lists.gnu.org/r/bug-autoconf/2020-10/msg00012.html
but, I hope, only in ways covered by the existing warning in NEWS
about pickier M4 quotation.

* lib/autoconf/general.m4 (_AC_INIT_PACKAGE): Redo argument
  normalization and default value selection in a simpler, less
  error-prone fashion.
  (_AC_INIT_PACKAGE_N): New helper subroutine.
  (AC_INIT): Always call _AC_INIT_PACKAGE, but supply no arguments if
  we were called with only one argument.

* tests/base.at (AC_INIT (obsolete invocation)): New test.
  (AC_INIT with unusual version strings): Expand test.

* doc/autoconf.texi (AC_INIT): Revise.
  • Loading branch information
zackw committed Nov 16, 2020
1 parent b7e32b4 commit 3b8f293
Show file tree
Hide file tree
Showing 3 changed files with 342 additions and 72 deletions.
100 changes: 85 additions & 15 deletions doc/autoconf.texi
Expand Up @@ -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

Expand Down Expand Up @@ -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.,
Expand Down
85 changes: 39 additions & 46 deletions lib/autoconf/general.m4
Expand Up @@ -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])[/]],
[])]))
])


Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 3b8f293

Please sign in to comment.