diff options
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | bin/autoconf.as | 12 | ||||
-rw-r--r-- | bin/autoheader.in | 2 | ||||
-rw-r--r-- | bin/autom4te.in | 5 | ||||
-rw-r--r-- | bin/autoscan.in | 5 | ||||
-rw-r--r-- | build-aux/help-extract.pl | 275 | ||||
-rwxr-xr-x | man/autoconf.w | 4 | ||||
-rwxr-xr-x | man/autoheader.w | 4 | ||||
-rwxr-xr-x | man/autom4te.w | 4 | ||||
-rwxr-xr-x | man/autoreconf.w | 4 | ||||
-rwxr-xr-x | man/autoscan.w | 4 | ||||
-rwxr-xr-x | man/autoupdate.w | 4 | ||||
-rwxr-xr-x | man/common.w | 4 | ||||
-rwxr-xr-x | man/ifnames.w | 4 | ||||
-rw-r--r-- | man/local.mk | 107 |
15 files changed, 386 insertions, 53 deletions
diff --git a/Makefile.am b/Makefile.am index 8c7ce7b6..ecc9cb35 100644 --- a/Makefile.am +++ b/Makefile.am @@ -34,6 +34,7 @@ EXTRA_DIST = ChangeLog.0 ChangeLog.1 ChangeLog.2 ChangeLog.3 \ 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 \ diff --git a/bin/autoconf.as b/bin/autoconf.as index ab6e3c52..ddf8cced 100644 --- a/bin/autoconf.as +++ b/bin/autoconf.as @@ -22,7 +22,7 @@ m4_divert_push([HEADER-COPYRIGHT])dnl 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 @@ -75,11 +75,11 @@ There is NO WARRANTY, to the extent permitted by law. 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: ' @@ -100,7 +100,7 @@ while test $# -gt 0 ; do --version | -V ) echo "$version" ; exit ;; --help | -h ) - AS_ECHO(["$usage"]); exit ;; + AS_ECHO(["$usage_err"]); exit ;; --verbose | -v ) verbose=: @@ -154,7 +154,7 @@ while test $# -gt 0 ; do break ;; -* ) exec >&2 - AS_ERROR([invalid option '$[1]'$as_nl$help]) ;; + AS_ERROR([invalid option '$[1]'$as_nl$usage_err]) ;; * ) break ;; esac @@ -178,7 +178,7 @@ case $# in 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. diff --git a/bin/autoheader.in b/bin/autoheader.in index b8ed9d3f..2a9a2e07 100644 --- a/bin/autoheader.in +++ b/bin/autoheader.in @@ -72,7 +72,7 @@ or else 'configure.in'. -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 diff --git a/bin/autom4te.in b/bin/autom4te.in index 3a80d4b7..d98998de 100644 --- a/bin/autom4te.in +++ b/bin/autom4te.in @@ -204,8 +204,7 @@ General help using GNU software: <https://www.gnu.org/gethelp/>. # $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> @@ -213,7 +212,7 @@ This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Written by Akim Demaille. -EOF +"; ## ---------- ## diff --git a/bin/autoscan.in b/bin/autoscan.in index 0eb35040..9780f7db 100644 --- a/bin/autoscan.in +++ b/bin/autoscan.in @@ -87,7 +87,6 @@ my %needed_macros = 'AC_PREREQ' => [$me], ); -my $configure_scan = 'configure.scan'; my $log; # Autoconf and lib files. @@ -103,7 +102,7 @@ $help = "Usage: $0 [OPTION]... [SRCDIR] 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 @@ -462,7 +461,7 @@ sub output_kind ($$) 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}}) { diff --git a/build-aux/help-extract.pl b/build-aux/help-extract.pl new file mode 100644 index 00000000..0edc793a --- /dev/null +++ b/build-aux/help-extract.pl @@ -0,0 +1,275 @@ +# 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: diff --git a/man/autoconf.w b/man/autoconf.w new file mode 100755 index 00000000..4bb2eb63 --- /dev/null +++ b/man/autoconf.w @@ -0,0 +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 "$@" diff --git a/man/autoheader.w b/man/autoheader.w new file mode 100755 index 00000000..821d30ba --- /dev/null +++ b/man/autoheader.w @@ -0,0 +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/autoheader.in "$@" diff --git a/man/autom4te.w b/man/autom4te.w new file mode 100755 index 00000000..03375042 --- /dev/null +++ b/man/autom4te.w @@ -0,0 +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/autom4te.in "$@" diff --git a/man/autoreconf.w b/man/autoreconf.w new file mode 100755 index 00000000..ab98a4f2 --- /dev/null +++ b/man/autoreconf.w @@ -0,0 +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/autoreconf.in "$@" diff --git a/man/autoscan.w b/man/autoscan.w new file mode 100755 index 00000000..214fc794 --- /dev/null +++ b/man/autoscan.w @@ -0,0 +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/autoscan.in "$@" diff --git a/man/autoupdate.w b/man/autoupdate.w new file mode 100755 index 00000000..0b75208d --- /dev/null +++ b/man/autoupdate.w @@ -0,0 +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/autoupdate.in "$@" diff --git a/man/common.w b/man/common.w new file mode 100755 index 00000000..ed981db4 --- /dev/null +++ b/man/common.w @@ -0,0 +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/common.in "$@" diff --git a/man/ifnames.w b/man/ifnames.w new file mode 100755 index 00000000..7812018a --- /dev/null +++ b/man/ifnames.w @@ -0,0 +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/ifnames.in "$@" diff --git a/man/local.mk b/man/local.mk index 68cf5a8d..d594448d 100644 --- a/man/local.mk +++ b/man/local.mk @@ -15,8 +15,6 @@ # 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 \ @@ -26,57 +24,86 @@ dist_man_MANS = \ 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) |