Rewrite bin/autoconf in Perl.

Of all the installed programs (autoconf, autoheader, autom4te,
autoreconf, autoscan, autoupdate, ifnames) autoconf is the only one
that is a shell script instead of a Perl script.  This means it has to
do a lot of fiddly quoting and requoting to assemble an autom4te
command line, it doesn’t get to use the shared option handling code in
Autom4te/{General,Getopt}.pm, and it has to duplicate usage text that
properly should only be in Autom4te/ChannelDefs.pm.  It also means
there’s extra code in build-aux/help-extract.pl just for it, and a
special two-phase generation process in bin/local.mk.

This also paves the way for the bootstrap script mentioned in the
previous commit; it will only have to know how to deal with
substitution variables, not generation of m4sh scripts.

The new script winds up being slightly longer on disk but that’s
because our boilerplate for Perl scripts is quite long.  The code is
visibly simpler.

* bin/autoconf.as: Rename to bin/autoconf.in and rewrite in Perl.
* bin/local.mk (EXTRA_DIST): Change autoconf.as to autoconf.in.
  (MOSTLYCLEANFILES): Don’t delete autoconf.in.
  (bin/autoconf.in): Delete rule.
  (ETAGS_PERL): Add autoconf.in and sort list.
  (ETAGS_SH): Delete, no longer needed.
* tests/tools.at: Syntax-check autoconf as a Perl script.

* build-aux/help-extract.pl: Remove all code for extracting usage text
  from shell scripts.
* man/autoconf.w, man/local.mk: Refer to autoconf.in, not autoconf.as.
This commit is contained in:
Zack Weinberg 2021-02-04 11:14:37 -05:00
parent af5c11ed61
commit f060c7e5fd
No known key found for this signature in database
GPG Key ID: 384F8E68AC65B0D5
8 changed files with 225 additions and 275 deletions

1
.gitignore vendored
View File

@ -43,7 +43,6 @@ Makefile.in
/autom4te.cache
/bin/autoheader
/bin/autoconf
/bin/autoconf.in
/bin/autom4te
/bin/autoreconf
/bin/autoscan

View File

@ -1,208 +0,0 @@
AS_INIT[]dnl -*- shell-script -*-
m4_divert_push([HEADER-COPYRIGHT])dnl
# @configure_input@
# autoconf -- create 'configure' using m4 macros.
# Copyright (C) 1992-1994, 1996, 1999-2017, 2020-2021 Free Software
# Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
m4_divert_pop([HEADER-COPYRIGHT])dnl back to BODY
AS_ME_PREPARE[]dnl
help=["\
Usage: $0 [OPTION]... [TEMPLATE-FILE]
Generate a configuration script from a TEMPLATE-FILE if given, or
'configure.ac' if present, or else 'configure.in'. Output is sent
to the standard output if TEMPLATE-FILE is given, else into
'configure'.
Operation modes:
-h, --help print this help, then exit
-V, --version print version number, then exit
-v, --verbose verbosely report processing
-d, --debug don't remove temporary files
-f, --force consider all files obsolete
-o, --output=FILE save output in FILE (stdout is the default)
-W, --warnings=CATEGORY report the warnings falling in CATEGORY
Warning categories include:
cross cross compilation issues
gnu GNU coding standards (default in gnu and gnits modes)
obsolete obsolete features or constructions (default)
override user redefinitions of Automake rules or variables
portability portability issues (default in gnu and gnits modes)
portability-recursive nested Make variables (default with -Wportability)
extra-portability extra portability issues related to obscure tools
syntax dubious syntactic constructs (default)
unsupported unsupported or incomplete features (default)
all all the warnings
no-CATEGORY turn off warnings in CATEGORY
none turn off all the warnings
The environment variables 'M4' and 'WARNINGS' are honored.
Library directories:
-B, --prepend-include=DIR prepend directory DIR to search path
-I, --include=DIR append directory DIR to search path
Tracing:
-t, --trace=MACRO[:FORMAT] report the list of calls to MACRO
-i, --initialization also trace Autoconf's initialization process
In tracing mode, no configuration script is created. FORMAT defaults
to '\$f:\$l:\$n:\$%'; see 'autom4te --help' for information about FORMAT.
Report bugs to <bug-autoconf@gnu.org>.
GNU Autoconf home page: <https://www.gnu.org/software/autoconf/>.
General help using GNU software: <https://www.gnu.org/gethelp/>."]
version=["\
autoconf (@PACKAGE_NAME@) @VERSION@
Copyright (C) @RELEASE_YEAR@ Free Software Foundation, Inc.
License GPLv3+/Autoconf: GNU GPL version 3 or later
<https://gnu.org/licenses/gpl.html>, <https://gnu.org/licenses/exceptions.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by David J. MacKenzie and Akim Demaille."]
usage_err="\
Try '$as_me --help' for more information."
exit_missing_arg='
m4_bpatsubst([AS_ERROR([option '$[1]' requires an argument$as_nl$usage_err])],
['], ['\\''])'
# restore font-lock: '
# Variables.
: ${AUTOM4TE='@bindir@/@autom4te-name@'}
: ${trailer_m4='@pkgdatadir@/autoconf/trailer.m4'}
autom4te_options=
outfile=
verbose=false
# Parse command line.
while test $# -gt 0 ; do
option=[`expr "x$1" : 'x\(--[^=]*\)' \| \
"x$1" : 'x\(-.\)'`]
optarg=[`expr "x$1" : 'x--[^=]*=\(.*\)' \| \
"x$1" : 'x-.\(.*\)'`]
case $1 in
--version | -V )
AS_ECHO(["$version"]); exit ;;
--help | -h )
AS_ECHO(["$help"]); exit ;;
--verbose | -v )
verbose=:
autom4te_options="$autom4te_options $1"; shift ;;
# Arguments passed as is to autom4te.
--debug | -d | \
--force | -f | \
--include=* | -I?* | \
--prepend-include=* | -B?* | \
--warnings=* | -W?* )
case $1 in
*\'*) arg=`AS_ECHO(["$1"]) | sed "s/'/'\\\\\\\\''/g"` ;; #'
*) arg=$1 ;;
esac
autom4te_options="$autom4te_options '$arg'"; shift ;;
# Options with separated arg passed as is to autom4te.
--include | -I | \
--prepend-include | -B | \
--warnings | -W )
test $# = 1 && eval "$exit_missing_arg"
case $2 in
*\'*) arg=`AS_ECHO(["$2"]) | sed "s/'/'\\\\\\\\''/g"` ;; #'
*) arg=$2 ;;
esac
autom4te_options="$autom4te_options $option '$arg'"
shift; shift ;;
--trace=* | -t?* )
traces="$traces --trace='"`AS_ECHO(["$optarg"]) | sed "s/'/'\\\\\\\\''/g"`"'"
shift ;;
--trace | -t )
test $# = 1 && eval "$exit_missing_arg"
traces="$traces --trace='"`AS_ECHO(["$[2]"]) | sed "s/'/'\\\\\\\\''/g"`"'"
shift; shift ;;
--initialization | -i )
autom4te_options="$autom4te_options --melt"
shift;;
--output=* | -o?* )
outfile=$optarg
shift ;;
--output | -o )
test $# = 1 && eval "$exit_missing_arg"
outfile=$2
shift; shift ;;
-- ) # Stop option processing
shift; break ;;
- ) # Use stdin as input.
break ;;
-* )
exec >&2
AS_ERROR([invalid option '$[1]'$as_nl$usage_err]) ;;
* )
break ;;
esac
done
# Find the input file.
case $# in
0)
if test -f configure.ac; then
if test -f configure.in; then
AS_ECHO(["$as_me: warning: both 'configure.ac' and 'configure.in' are present."]) >&2
AS_ECHO(["$as_me: warning: proceeding with 'configure.ac'."]) >&2
fi
infile=configure.ac
elif test -f configure.in; then
infile=configure.in
else
AS_ERROR([no input file])
fi
test -z "$traces" && test -z "$outfile" && outfile=configure;;
1)
infile=$1 ;;
*) exec >&2
AS_ERROR([invalid number of arguments$as_nl$usage_err]) ;;
esac
# Unless specified, the output is stdout.
test -z "$outfile" && outfile=-
# Don't read trailer.m4 if we are tracing.
if test -n "$traces"; then
trailer_m4=""
else
# The extra quotes will be stripped by eval.
trailer_m4=\""$trailer_m4"\"
fi
# Run autom4te with expansion.
# trailer.m4 is read _before_ $infile, despite the name,
# because putting it afterward screws up autom4te's location tracing.
eval set x "$autom4te_options" \
--language=autoconf --output=\"\$outfile\" "$traces" \
$trailer_m4 \"\$infile\"
shift
$verbose && AS_ECHO(["$as_me: running $AUTOM4TE $*"]) >&2
exec "$AUTOM4TE" "$@"

213
bin/autoconf.in Normal file
View File

@ -0,0 +1,213 @@
#! @PERL@
# -*- Perl -*-
# @configure_input@
eval 'case $# in 0) exec @PERL@ -S "$0";; *) exec @PERL@ -S "$0" "$@";; esac'
if 0;
# autoconf -- create 'configure' using m4 macros.
# Copyright (C) 1992-1994, 1996, 1999-2017, 2020-2021 Free Software
# Foundation, Inc.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
use 5.006;
use strict;
use warnings FATAL => 'all';
BEGIN
{
my $pkgdatadir = $ENV{'autom4te_perllibdir'} || '@pkgdatadir@';
unshift @INC, $pkgdatadir;
# Override SHELL. On DJGPP SHELL may not be set to a shell
# that can handle redirection and quote arguments correctly,
# e.g.: COMMAND.COM. For DJGPP always use the shell that configure
# has detected.
$ENV{'SHELL'} = '@SHELL@' if ($^O eq 'dos');
}
use Autom4te::ChannelDefs;
use Autom4te::Channels qw(msg);
use Autom4te::General;
# Lib files.
my $autom4te = $ENV{'AUTOM4TE'} || '@bindir@/@autom4te-name@';
my $trailer_m4 = $ENV{'trailer_m4'} || '@pkgdatadir@/autoconf/trailer.m4';
# $HELP
# -----
$help = "Usage: $0 [OPTION]... [TEMPLATE-FILE]
Generate a configuration script from a TEMPLATE-FILE if given, or
'configure.ac' if present, or else 'configure.in'. Output is sent
to the standard output if TEMPLATE-FILE is given, else into
'configure'.
Operation modes:
-h, --help print this help, then exit
-V, --version print version number, then exit
-v, --verbose verbosely report processing
-d, --debug don't remove temporary files
-f, --force consider all files obsolete
-o, --output=FILE save output in FILE (stdout is the default)
-W, --warnings=CATEGORY report the warnings falling in CATEGORY
" . Autom4te::ChannelDefs::usage . "
Library directories:
-B, --prepend-include=DIR prepend directory DIR to search path
-I, --include=DIR append directory DIR to search path
Tracing:
-t, --trace=MACRO[:FORMAT] report the list of calls to MACRO
-i, --initialization also trace Autoconf's initialization process
In tracing mode, no configuration script is created. FORMAT defaults
to '\$f:\$l:\$n:\$%'; see 'autom4te --help' for information about FORMAT.
Report bugs to <bug-autoconf\@gnu.org>.
GNU Autoconf home page: <https://www.gnu.org/software/autoconf/>.
General help using GNU software: <https://www.gnu.org/gethelp/>.
";
# $VERSION
# --------
$version = "autoconf (@PACKAGE_NAME@) @VERSION@
Copyright (C) @RELEASE_YEAR@ Free Software Foundation, Inc.
License GPLv3+/Autoconf: GNU GPL version 3 or later
<https://gnu.org/licenses/gpl.html>, <https://gnu.org/licenses/exceptions.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by David J. MacKenzie and Akim Demaille.
";
## ---------- ##
## Routines. ##
## ---------- ##
my $infile;
my $outfile;
my @autom4te_options;
my @traces;
# parse_args ()
# -------------
# Process any command line arguments.
sub parse_args ()
{
getopt (
# Arguments passed as-is to autom4te.
'I|include=s' => sub { push @autom4te_options, '--include='.$_[1] },
'B|prepend-include=s' => sub { push @autom4te_options,
'--prepend-include='.$_[1] },
'W|warnings=s' => sub { push @autom4te_options,
'--warnings='.$_[1] },
# Arguments processed (somewhat) in this program.
'i|initialization' => sub { push @autom4te_options, '--melt' },
't|trace=s' => sub { push @traces, '--trace='.$_[1] },
'o|output=s' => \$outfile,
);
# Also pass down certain options that were processed into their own
# variables by Autom4te::General::getopt.
push @autom4te_options, '--debug' if $debug;
push @autom4te_options, '--force' if $force;
push @autom4te_options, '--verbose' if $verbose;
# Find the input file.
if (@ARGV == 0)
{
$outfile = 'configure' if !$outfile && !@traces;
if (-f 'configure.ac')
{
$infile = 'configure.ac';
if (-f 'configure.in')
{
msg 'obsolete',
q(both 'configure.ac' and 'configure.in' are present);
msg 'obsolete',
q(proceeding with 'configure.ac');
}
}
elsif (-f 'configure.in')
{
$infile = 'configure.in';
}
else
{
fatal 'no input file';
}
}
elsif (@ARGV == 1)
{
$infile = $ARGV[0];
}
else
{
fatal "invalid number of arguments.\n"
. "Try '$me --help' for more information.";
}
# Unless already set, the output is stdout.
$outfile = '-' if !$outfile;
}
## -------------- ##
## Main program. ##
## -------------- ##
parse_args;
# Construct the autom4te invocation.
# $autom4te might contain additional command line options; word-split it
# exactly as the shell would.
my @autom4te_command = split /[ \t\n]+/, $autom4te;
push @autom4te_command, @autom4te_options;
push @autom4te_command, '--language=autoconf', "--output=$outfile", @traces;
# Don't read trailer.m4 if we are tracing.
# trailer.m4 is read _before_ $infile, despite the name,
# because putting it afterward screws up autom4te's location tracing.
push @autom4te_command, $trailer_m4 if !@traces;
push @autom4te_command, $infile;
print {*STDERR} $me,
': running ',
join (" ", map { shell_quote($_) } @autom4te_command),
"\n"
if $verbose;
exec {$autom4te_command[0]} @autom4te_command;
### Setup "GNU" style for perl-mode and cperl-mode.
## Local Variables:
## perl-indent-level: 2
## perl-continued-statement-offset: 2
## perl-continued-brace-offset: 0
## perl-brace-offset: 0
## perl-brace-imaginary-offset: 0
## perl-label-offset: -2
## cperl-indent-level: 2
## cperl-brace-offset: 0
## cperl-continued-brace-offset: 0
## cperl-label-offset: -2
## cperl-extra-newline-before-brace: t
## cperl-merge-trailing-else: nil
## cperl-continued-statement-offset: 2
## End:

View File

@ -26,7 +26,7 @@ bin_SCRIPTS = \
bin/ifnames
EXTRA_DIST += \
bin/autoconf.as \
bin/autoconf.in \
bin/autoheader.in \
bin/autom4te.in \
bin/autoreconf.in \
@ -35,20 +35,12 @@ EXTRA_DIST += \
bin/ifnames.in
# Files that should be removed, but which Automake does not know.
MOSTLYCLEANFILES += $(bin_SCRIPTS) bin/autoconf.in bin/*.tmp
MOSTLYCLEANFILES += $(bin_SCRIPTS) bin/*.tmp
## ------------- ##
## The scripts. ##
## ------------- ##
# autoconf is written in M4sh.
# FIXME: this target should depend on the frozen files below lib/m4sugar,
# otherwise autom4te may pick up a frozen m4sh.m4f from an earlier
# installation below the same $(prefix); work around this with --melt.
bin/autoconf.in: $(srcdir)/bin/autoconf.as $(m4sh_m4f_dependencies)
$(MY_AUTOM4TE) --language M4sh --cache '' \
--melt $(srcdir)/bin/autoconf.as -o $@
## All the scripts depend on Makefile so that they are rebuilt when the
## prefix etc. changes. It took quite a while to have the rule correct,
## don't break it!
@ -83,13 +75,12 @@ LETTERS = ABCDEFGHIJKLMNOPQRSTUVWXYZ
DIGITS = 0123456789
WORD_REGEXP = [$(LETTERS)$(letters)_][$(LETTERS)$(letters)$(DIGITS)_]*
ETAGS_PERL = --lang=perl \
bin/autoconf.in \
bin/autoheader.in \
bin/autoreconf.in \
bin/autoupdate.in \
bin/autoscan.in \
bin/autom4te.in \
bin/autoreconf.in \
bin/autoscan.in \
bin/autoupdate.in \
bin/ifnames.in
ETAGS_SH = --lang=none --regex='/\($(WORD_REGEXP)\)=/\1/' \
bin/autoconf.in
ETAGS_ARGS += $(ETAGS_PERL) $(ETAGS_SH)
ETAGS_ARGS += $(ETAGS_PERL)

View File

@ -135,49 +135,10 @@ sub extract_perl_assignment (*$$$)
}
## ------------------------------ ##
## Extraction from shell scripts. ##
## ------------------------------ ##
sub extract_shell_assignment (*$$)
{
my ($fh, $source, $what) = @_;
my $value = "";
my $parse_state = 0;
local $_;
while (<$fh>)
{
if ($parse_state == 0)
{
if (/^\Q${what}\E=\[\"\\$/)
{
$parse_state = 1;
}
}
elsif ($parse_state == 1)
{
my $done = s/"\]$//;
$value .= $_;
if ($done)
{
# This is not strictly correct but it works acceptably
# for the escapes that actually occur in the strings
# we're extracting.
return eval_qq_no_interpolation ('"'.$value.'"');
}
}
}
die "$source: unexpected EOF in state $parse_state\n";
}
## -------------- ##
## Main program. ##
## -------------- ##
sub extract_assignment ($$$)
{
my ($source, $channeldefs_pm, $what) = @_;
@ -189,12 +150,6 @@ sub extract_assignment ($$$)
{
return extract_perl_assignment ($fh, $source, $channeldefs_pm, $what);
}
elsif ($firstline =~ /\bAS_INIT\b/
|| $firstline =~ /bin\/[a-z0-9]*sh\b/
|| $firstline =~ /-\*-\s*shell-script\s*-\*-/i)
{
return extract_shell_assignment ($fh, $source, $what);
}
else
{
die "$source: language not recognized\n";
@ -209,7 +164,7 @@ sub main ()
# to not mess with the command line.
my $usage = "Usage: $0 script-source (--help | --version)
Extract help and version information from a perl or shell script.
Extract help and version information from a perl script.
Required environment variables:
top_srcdir relative path from cwd to the top of the source tree
@ -242,7 +197,7 @@ The script-source argument should also be relative to top_srcdir.
die $usage;
}
my $cmd_name = $source =~ s{^.*/([^./]+)\.(?:as|in)$}{$1}r;
my $cmd_name = $source =~ s{^.*/([^./]+)\.in$}{$1}r;
$source = File::Spec->catfile($top_srcdir, $source);
$channeldefs_pm = File::Spec->catfile($top_srcdir, $channeldefs_pm);

View File

@ -1,4 +1,4 @@
#! /bin/sh
# help-extract wrapper, see build-aux/help-extract.pl and man/local.mk
exec "${PERL-perl}" "${top_srcdir}/build-aux/help-extract.pl" \
bin/autoconf.as "$@"
bin/autoconf.in "$@"

View File

@ -50,7 +50,7 @@ man_common_dep = $(top_srcdir)/man/common.x \
$(top_srcdir)/.version \
$(top_srcdir)/configure.ac
man/autoconf.1: $(common_dep) man/autoconf.w man/autoconf.x $(binsrcdir)/autoconf.as
man/autoconf.1: $(common_dep) man/autoconf.w man/autoconf.x $(binsrcdir)/autoconf.in
man/autoheader.1: $(common_dep) man/autoheader.w man/autoheader.x $(binsrcdir)/autoheader.in
man/autom4te.1: $(common_dep) man/autom4te.w man/autom4te.x $(binsrcdir)/autom4te.in
man/autoreconf.1: $(common_dep) man/autoreconf.w man/autoreconf.x $(binsrcdir)/autoreconf.in

View File

@ -50,7 +50,6 @@ AT_CHECK([test "$SHELL_N" != none || exit 77])
# Specify the absolute name of the tool, as some shells don't honor PATH when
# running 'sh PROG'.
AT_CHECK_SHELL_SYNTAX(["$abs_top_builddir/bin/autoconf"])
AT_CHECK_SHELL_SYNTAX(["$abs_top_builddir/tests/autoconf"])
AT_CHECK_SHELL_SYNTAX(["$abs_top_builddir/tests/testsuite"])
@ -69,6 +68,7 @@ AT_CLEANUP
AT_SETUP([Syntax of the Perl scripts])
AT_CHECK_PERL_SYNTAX([autoconf])
AT_CHECK_PERL_SYNTAX([autoheader])
AT_CHECK_PERL_SYNTAX([autom4te])
AT_CHECK_PERL_SYNTAX([autoreconf])