build-aux/git-version-gen \
build-aux/announce-gen build-aux/gnupload \
build-aux/gitlog-to-changelog \
+ build-aux/help-extract.pl \
.prev-version .version
edit = sed \
m4_divert_pop([HEADER-COPYRIGHT])dnl back to BODY
AS_ME_PREPARE[]dnl
-usage=["\
+help=["\
Usage: $0 [OPTION]... [TEMPLATE-FILE]
Generate a configuration script from a TEMPLATE-FILE if given, or
Written by David J. MacKenzie and Akim Demaille."]
-help="\
+usage_err="\
Try '$as_me --help' for more information."
exit_missing_arg='
- m4_bpatsubst([AS_ERROR([option '$[1]' requires an argument$as_nl$help])],
+ m4_bpatsubst([AS_ERROR([option '$[1]' requires an argument$as_nl$usage_err])],
['], ['\\''])'
# restore font-lock: '
--version | -V )
echo "$version" ; exit ;;
--help | -h )
- AS_ECHO(["$usage"]); exit ;;
+ AS_ECHO(["$usage_err"]); exit ;;
--verbose | -v )
verbose=:
break ;;
-* )
exec >&2
- AS_ERROR([invalid option '$[1]'$as_nl$help]) ;;
+ AS_ERROR([invalid option '$[1]'$as_nl$usage_err]) ;;
* )
break ;;
esac
1)
infile=$1 ;;
*) exec >&2
- AS_ERROR([invalid number of arguments$as_nl$help]) ;;
+ AS_ERROR([invalid number of arguments$as_nl$usage_err]) ;;
esac
# Unless specified, the output is stdout.
-f, --force consider all files obsolete
-W, --warnings=CATEGORY report the warnings falling in CATEGORY
-" . Autom4te::ChannelDefs::usage () . "
+" . Autom4te::ChannelDefs::usage . "
Library directories:
-B, --prepend-include=DIR prepend directory DIR to search path
# $VERSION
# --------
-$version = <<"EOF";
-autom4te (@PACKAGE_NAME@) @VERSION@
+$version = "autom4te (@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>
There is NO WARRANTY, to the extent permitted by law.
Written by Akim Demaille.
-EOF
+";
## ---------- ##
'AC_PREREQ' => [$me],
);
-my $configure_scan = 'configure.scan';
my $log;
# Autoconf and lib files.
Examine source files in the directory tree rooted at SRCDIR, or the
current directory if none is given. Search the source files for
common portability problems, check for incompleteness of
-'configure.ac', and create a file '$configure_scan' which is a
+'configure.ac', and create a file 'configure.scan' which is a
preliminary 'configure.ac' for that package.
-h, --help print this help, then exit
if exists $kind_comment{$kind};
foreach my $word (sort keys %{$used{$kind}})
{
- # Output the needed macro invocations in $configure_scan if not
+ # Output the needed macro invocations in configure.scan if not
# already printed, and remember these macros are needed.
foreach my $macro (@{$macro{$kind}{$word}})
{
--- /dev/null
+# help-extract -- extract --help and --version output from a script.
+# Copyright (C) 2020 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/>.
+
+# Written by Zack Weinberg.
+
+use strict;
+use warnings;
+
+# File::Spec itself was added in 5.005.
+# File::Spec::Functions was added in 5.6.1 which is just barely too new.
+use File::Spec;
+
+# This script is not intended to be used directly. It's run by
+# help2man via wrappers in man/, e.g. man/autoconf.w, as if it were
+# one of autoconf's executable scripts. It extracts the --help and
+# --version output of that script from its source form, without
+# actually running it. The script to work from is set by the wrapper,
+# and several other parameters are passed down from the Makefile as
+# environment variables; see parse_args below.
+
+# The point of this script is, the preprocessed forms of the
+# executable scripts, and their wrappers for uninstalled use
+# (e.g. <build-dir>/{bin,tests}/autoconf) do not need to exist to
+# generate the corresponding manpages. This is desirable because we
+# can't put those dependencies in the makefiles without breaking
+# people's ability to build autoconf from a release tarball without
+# help2man installed. It also ensures that we will generate manpages
+# from the current source code and not from an older version of the
+# script that has already been installed.
+
+## ----------------------------- ##
+## Extraction from Perl scripts. ##
+## ----------------------------- ##
+
+sub eval_qq_no_interpolation ($)
+{
+ # The argument is expected to be a "double quoted string" including the
+ # leading and trailing delimiters. Returns the text of this string after
+ # processing backslash escapes but NOT interpolation.
+ # / (?<!\\) (?>\\\\)* blah /x means match blah preceded by an
+ # *even* number of backslashes. It would be nice if we could use \K
+ # to exclude the backslashes from the matched text, but that was only
+ # added in Perl 5.10 and we still support back to 5.006.
+ return eval $_[0] =~ s/ (?<!\\) (?>\\\\)* [\$\@] /\\$&/xrg;
+}
+
+sub extract_channeldefs_usage ($)
+{
+ my ($channeldefs_pm) = @_;
+ my $usage = "";
+ my $parse_state = 0;
+ local $_;
+
+ open (my $fh, "<", $channeldefs_pm) or die "$channeldefs_pm: $!\n";
+ while (<$fh>)
+ {
+ if ($parse_state == 0)
+ {
+ $parse_state = 1 if /^sub usage\b/;
+ }
+ elsif ($parse_state == 1)
+ {
+ if (s/^ return "//)
+ {
+ $parse_state = 2;
+ $usage .= $_;
+ }
+ }
+ elsif ($parse_state == 2)
+ {
+ if (s/(?<!\\) ((?>\\\\)*) "; $/$1/x)
+ {
+ $usage .= $_;
+ return $usage;
+ }
+ else
+ {
+ $usage .= $_;
+ }
+ }
+ }
+
+ die "$channeldefs_pm: unexpected EOF in state $parse_state\n";
+}
+
+sub extract_perl_assignment (*$$$)
+{
+ my ($fh, $source, $channeldefs_pm, $what) = @_;
+ my $value = "";
+ my $parse_state = 0;
+ local $_;
+
+ while (<$fh>)
+ {
+ if ($parse_state == 0)
+ {
+ if (s/^\$\Q${what}\E = (?=")//o)
+ {
+ $value .= $_;
+ $parse_state = 1;
+ }
+ }
+ elsif ($parse_state == 1)
+ {
+ if (/^"\s*\.\s*Autom4te::ChannelDefs::usage\s*(?:\(\))?\s*\.\s*"$/)
+ {
+ $value .= extract_channeldefs_usage ($channeldefs_pm);
+ }
+ elsif (/^";$/)
+ {
+ $value .= '"';
+ return eval_qq_no_interpolation ($value);
+ }
+ else
+ {
+ $value .= $_;
+ }
+ }
+ }
+
+ die "$source: unexpected EOF in state $parse_state\n";
+}
+
+
+## ------------------------------ ##
+## 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) = @_;
+
+ open (my $fh, "<", $source) or die "$source: $!\n";
+
+ my $firstline = <$fh>;
+ if ($firstline =~ /\@PERL\@/ || $firstline =~ /-\*-\s*perl\s*-\*-/i)
+ {
+ 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";
+ }
+}
+
+sub main ()
+{
+ # Most of our arguments come from environment variables, because
+ # help2man doesn't allow for passing additional command line
+ # arguments to the wrappers, and it's easier to write the wrappers
+ # 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.
+Required environment variables:
+
+ top_srcdir relative path from cwd to the top of the source tree
+ channeldefs_pm relative path from top_srcdir to ChannelDefs.pm
+ PACKAGE_NAME the autoconf PACKAGE_NAME substitution variable
+ VERSION the autoconf VERSION substitution variable
+ RELEASE_YEAR the autoconf RELEASE_YEAR substitution variable
+
+The script-source argument should also be relative to top_srcdir.
+";
+
+ my $source = shift(@ARGV) || die $usage;
+ my $what = shift(@ARGV) || die $usage;
+ my $top_srcdir = $ENV{top_srcdir} || die $usage;
+ my $channeldefs_pm = $ENV{channeldefs_pm} || die $usage;
+ my $package_name = $ENV{PACKAGE_NAME} || die $usage;
+ my $version = $ENV{VERSION} || die $usage;
+ my $release_year = $ENV{RELEASE_YEAR} || die $usage;
+
+ if ($what eq "-h" || $what eq "--help")
+ {
+ $what = "help";
+ }
+ elsif ($what eq "-V" || $what eq "--version")
+ {
+ $what = "version";
+ }
+ else
+ {
+ die $usage;
+ }
+
+ my $cmd_name = $source =~ s{^.*/([^./]+)\.(?:as|in)$}{$1}r;
+ $source = File::Spec->catfile($top_srcdir, $source);
+ $channeldefs_pm = File::Spec->catfile($top_srcdir, $channeldefs_pm);
+
+ my $text = extract_assignment ($source, $channeldefs_pm, $what);
+ $text =~ s/\$0\b/$cmd_name/g;
+ $text =~ s/[@]PACKAGE_NAME@/$package_name/g;
+ $text =~ s/[@]VERSION@/$version/g;
+ $text =~ s/[@]RELEASE_YEAR@/$release_year/g;
+ print $text;
+}
+
+main;
+
+
+### 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:
--- /dev/null
+#! /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 "$@"
--- /dev/null
+#! /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/autoheader.in "$@"
--- /dev/null
+#! /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/autom4te.in "$@"
--- /dev/null
+#! /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/autoreconf.in "$@"
--- /dev/null
+#! /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/autoscan.in "$@"
--- /dev/null
+#! /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/autoupdate.in "$@"
--- /dev/null
+#! /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/common.in "$@"
--- /dev/null
+#! /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/ifnames.in "$@"
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
-binsrcdir = $(srcdir)/bin
-
dist_man_MANS = \
man/autoconf.1 \
man/autoheader.1 \
man/autoupdate.1 \
man/ifnames.1
-EXTRA_DIST += $(dist_man_MANS:.1=.x) man/common.x
+EXTRA_DIST += $(dist_man_MANS:.1=.w) $(dist_man_MANS:.1=.x) man/common.x
-# Depend on .version to get version number changes.
-# Don't depend on the generated scripts, because then we would try to
-# regenerate the manpages after building the scripts, which would
-# defeat the purpose of shipping the manpages in the tarball.
-# (Instead we have recursive makes in the .x.1 rule below, which is
-# not ideal, but at least it prevents us from generating a manpage
-# from the *installed* utility.)
-common_dep = $(srcdir)/.version $(srcdir)/man/common.x
-man/autoconf.1: $(common_dep) $(binsrcdir)/autoconf.as
-man/autoheader.1: $(common_dep) $(binsrcdir)/autoheader.in
-man/autom4te.1: $(common_dep) $(binsrcdir)/autom4te.in
-man/autoreconf.1: $(common_dep) $(binsrcdir)/autoreconf.in
-man/autoscan.1: $(common_dep) $(binsrcdir)/autoscan.in
-man/autoupdate.1: $(common_dep) $(binsrcdir)/autoupdate.in
-man/ifnames.1: $(common_dep) $(binsrcdir)/ifnames.in
+# Each manpage depends on:
+# - its .w and .x files and its source script in bin/
+# - common.x for the SEE ALSO list
+# - lib/Autom4te/ChannelDefs.pm which contains additional --help text
+# (not included in _all_ the manpages, but it's easier to have them
+# all depend on it)
+# - .version and configure.ac for version information
+#
+# We ship the manpages in tarball releases so people can build from
+# them without having help2man installed. For this to work correctly,
+# the manpages cannot have declared dependencies on any file that is
+# not also shipped in the tarball. To avoid concurrency bugs, those
+# files plus the Makefile must in fact be sufficient to generate the
+# manpages. See the automake manual, section 'Errors with distclean',
+# for further discussion.
-remove_time_stamp = 's/^\(\.TH[^"]*"[^"]*"[^"]*\)"[^"]*"/\1/'
+binsrcdir = $(top_srcdir)/bin
+channeldefs_pm = lib/Autom4te/ChannelDefs.pm
+man_common_dep = $(top_srcdir)/man/common.x \
+ $(top_srcdir)/$(channeldefs_pm) \
+ $(top_srcdir)/.version \
+ $(top_srcdir)/configure.ac
-MOSTLYCLEANFILES += $(dist_man_MANS:=.t) $(dist_man_MANS:=a.t) \
- $(dist_man_MANS:=.tmp)
-MAINTAINERCLEANFILES += $(dist_man_MANS)
+man/autoconf.1: $(common_dep) man/autoconf.w man/autoconf.x $(binsrcdir)/autoconf.as
+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
+man/autoscan.1: $(common_dep) man/autoscan.w man/autoscan.x $(binsrcdir)/autoscan.in
+man/autoupdate.1: $(common_dep) man/autoupdate.w man/autoupdate.x $(binsrcdir)/autoupdate.in
+man/ifnames.1: $(common_dep) man/ifnames.w man/ifnames.x $(binsrcdir)/ifnames.in
-# To satisfy 'distcleancheck', we need to delete built manpages in
-# 'distclean' when the build and source directories are not the same.
-# We know we are in this case when 'man/common.x' doesn't exist.
-distclean-local: distclean-local-man
-distclean-local-man:
- test -f man/common.x || rm -f $(dist_man_MANS)
+# To generate the manpages, we use help2man, but we don't have it run
+# the built script that corresponds to the manpage. Instead it runs
+# the .w file listed above, which is a wrapper around
+# build-aux/help-extract.pl, which parses the *source* of the script
+# and extracts the help and version text. This means that the built
+# script doesn't need to exist to create the manpage. If it did,
+# we would have a concurrency bug, since we can't declare a dependency
+# on the built script, as discussed above.
+# We use a suffix rule describing the manpage as built from its .w file
+# so that we can use $(<F) to name the executable to be run, which avoids
+# an extra subshell and sed invocation.
+remove_time_stamp = 's/^\(\.TH[^"]*"[^"]*"[^"]*\)"[^"]*"/\1/'
+SUFFIXES += .w .1
-SUFFIXES += .x .1
-
-.x.1:
- @set -e; cmd=`basename $*`; \
- test -x bin/$$cmd || $(MAKE) bin/$$cmd; \
- test -x tests/$$cmd || $(MAKE) tests/$$cmd;
+.w.1:
@echo "Updating man page $@"
- @test -d $(@D) || mkdir $(@D)
- PATH="./tests$(PATH_SEPARATOR)$(top_srcdir)/build-aux$(PATH_SEPARATOR)$$PATH"; \
- export PATH; \
+ $(MKDIR_P) $(@D)
+ PATH="$(top_srcdir)/man$(PATH_SEPARATOR)$$PATH"; \
+ PERL="$(PERL)"; \
+ PACKAGE_NAME="$(PACKAGE_NAME)"; \
+ VERSION="$(VERSION)"; \
+ RELEASE_YEAR="$(RELEASE_YEAR)"; \
+ top_srcdir="$(top_srcdir)"; \
+ channeldefs_pm="$(channeldefs_pm)"; \
+ export PATH PERL PACKAGE_NAME VERSION RELEASE_YEAR; \
+ export top_srcdir channeldefs_pm; \
$(HELP2MAN) \
--include=$(srcdir)/$*.x \
--include=$(srcdir)/man/common.x \
--source='$(PACKAGE_STRING)' \
- --output=$@.t `echo '$*' | sed 's,.*/,,'`
- if sed $(remove_time_stamp) $@ >$@a.t 2>/dev/null && \
- sed $(remove_time_stamp) $@.t | cmp $@a.t - >/dev/null 2>&1; then \
+ --output=$@.t $(<F)
+ if $(SED) $(remove_time_stamp) $@ >$@a.t 2>/dev/null && \
+ $(SED) $(remove_time_stamp) $@.t | cmp $@a.t - >/dev/null 2>&1; then \
touch $@; \
else \
mv $@.t $@; \
fi
rm -f $@.t $@a.t
+
+
+MOSTLYCLEANFILES += $(dist_man_MANS:=.t) $(dist_man_MANS:=a.t)
+MAINTAINERCLEANFILES += $(dist_man_MANS)
+
+# To satisfy 'distcleancheck', we need to delete built manpages in
+# 'distclean' when the build and source directories are not the same.
+# We know we are in this case when 'man/common.x' doesn't exist.
+distclean-local: distclean-local-man
+distclean-local-man:
+ test -f man/common.x || rm -f $(dist_man_MANS)