]> git.ipfire.org Git - thirdparty/automake.git/commitdiff
Patch from https://bugs.gnu.org/81040. master
authorKamila Szewczyk <szewczyk@cs.uni-saarland.de>
Sat, 30 May 2026 16:18:13 +0000 (09:18 -0700)
committerKarl Berry <karl@freefriends.org>
Sat, 30 May 2026 16:18:13 +0000 (09:18 -0700)
* m4/optional.m4: New file.  AM_OPTIONAL_AUTOMAKE(OPTIONS) makes
`make dist' build the listed dist-XXX archives when their tool is
available and skip them otherwise, without failing.  Supports
all the available compression options:
dist-bzip2, dist-bzip3, dist-xz, dist-lzip, dist-zstd, dist-zip,
dist-shar, dist-tarZ.  dist-shar is handled separately: shar is
run to an intermediate file before gzip so a missing shar cannot
leave a bogus .shar.gz behind.
* m4/local.mk (dist_automake_ac_DATA): Add m4/optional.m4.
* doc/automake.texi (Public Macros): Document it.  Cross-reference
it from dist-bzip3, dist-lzip and dist-zstd.
* t/am-optional-automake.sh: New test, including a distcheck case.
* t/list-of-tests.mk: Add it.
* NEWS: Mention the new macro.

2025-05-19  Kamila Szewczyk  <szewczyk@cs.uni-saarland.de>

NEWS
doc/automake.texi
m4/local.mk
m4/optional.m4 [new file with mode: 0644]
t/am-optional-automake.sh [new file with mode: 0644]
t/list-of-tests.mk

diff --git a/NEWS b/NEWS
index 7862559321fa5e7c3521703779302af59cc4fa9b..535f65effbe2b83d2a97c05d99283abe973609f8 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,6 +1,18 @@
 For planned incompatibilities in a possible future Automake 2.0 release,
 please see NEWS-future and start following the advice there now.
 
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+New in 1.18.2 (????-??-??):
+
+* New features added
+
+  - New autoconf macro AM_OPTIONAL_AUTOMAKE.  It takes a list of
+    `dist-XXX' option names (dist-bzip2, dist-bzip3, dist-xz,
+    dist-lzip, dist-zstd, dist-zip, dist-shar, dist-tarZ);
+    `make dist' builds each archive when its tool is available and skips
+    it otherwise, without making `make dist' fail.  This is for
+    systems that intentionally lack some compression tools.
+
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 New in 1.18.1 (2025-06-25):
 
index eeacee762487293678ac02f2462c6a04d185d420..a098e7e0367fbbcafa8bf01581aeb39e4dd3e91b 100644 (file)
@@ -4137,6 +4137,51 @@ option (@pxref{List of Automake options}):
 AM_INIT_AUTOMAKE([no-define ...])
 @end example
 
+@item AM_OPTIONAL_AUTOMAKE([@var{options}])
+@acindex AM_OPTIONAL_AUTOMAKE
+@cindex Optional @code{dist-} archives
+@comment https://debbugs.gnu.org/cgi/bugreport.cgi?bug=81040
+@var{options} is a whitespace-separated list of @code{dist-} option
+names, using the same spellings @code{AM_INIT_AUTOMAKE} accepts:
+@option{dist-bzip2}, @option{dist-bzip3}, @option{dist-xz},
+@option{dist-lzip}, @option{dist-zstd}, @option{dist-zip},
+@option{dist-shar}, and @option{dist-tarZ}.  For each, @samp{make
+dist} builds the matching archive when its compressor or archiver is
+available and skips it otherwise, including when a tool present at
+@command{configure} time is gone by @samp{make dist} time; a missing
+listed tool does not make @samp{make dist} fail.  An unknown
+@var{option} is reported as a @code{syntax} warning and ignored.
+
+@cindex minimal system images
+@cindex CI images, minimal
+@cindex container images, minimal
+@cindex compression tools, making optional
+This is meant for systems that intentionally lack some compression
+tools, such as minimal container or CI images, where installing
+@command{bzip3}, @command{lzip}, @command{zstd}, etc.@: only so that
+@samp{make distcheck} succeeds is undesirable.  It avoids overriding
+@code{DIST_TARGETS} or @code{DIST_ARCHIVES} by hand.
+
+The macro does not pass the options to @command{automake}; it uses
+only the @code{distdir} target and @code{$(am__tar)}, present since
+Automake 1.10, so an older @command{automake} that does not recognize
+an option (such as @option{dist-bzip3}) is not a problem.
+
+@example
+AM_INIT_AUTOMAKE([-Wall foreign dist-bzip2 dist-xz dist-zip])
+AM_OPTIONAL_AUTOMAKE([dist-bzip3])
+@end example
+
+Here @file{.tar.gz}, @file{.tar.bz2}, @file{.tar.xz}, and @file{.zip}
+are always built (and @samp{make dist} fails if their tools are
+missing), while @file{.tar.bz3} is built only if @command{bzip3} is
+installed.
+
+A given @code{dist-} option should appear in @code{AM_INIT_AUTOMAKE}
+or @code{AM_OPTIONAL_AUTOMAKE}, not both; listing it in
+@code{AM_INIT_AUTOMAKE} already makes that format mandatory.  The
+macro may be called more than once; repeating an option is a no-op.
+
 @item AM_PATH_LISPDIR
 @acindex AM_PATH_LISPDIR
 @vindex EMACS
@@ -9468,7 +9513,8 @@ make it use a different one, set the @env{BZIP2} environment variable.
 @vindex BZIP3
 Generate a @samp{bzip3} tar archive of the distribution.  Unlike the
 other compression programs here, @command{bzip3} does not read any
-environment variables.
+environment variables.  To build this only when @command{bzip3} is
+present, see @code{AM_OPTIONAL_AUTOMAKE} (@pxref{Public Macros}).
 
 @item @code{dist-lzip}
 @trindex dist-lzip
@@ -9478,7 +9524,9 @@ Generate an @samp{lzip} tar archive of the distribution.
 @command{lzip} archives are usually smaller than
 @command{bzip2}-compressed archives.  By default, this rule makes
 @samp{lzip} use a compression option of @option{-9}.  To make it use a
-different one, set the @env{LZIP_OPT} environment variable.
+different one, set the @env{LZIP_OPT} environment variable.  To build
+this only when @command{lzip} is present, see
+@code{AM_OPTIONAL_AUTOMAKE} (@pxref{Public Macros}).
 
 @item @code{dist-xz}
 @trindex dist-xz
@@ -9510,7 +9558,8 @@ default compression ratio, but with a progress indicator: @samp{make
 dist-zstd ZSTD_OPT=-19v}.  However, note that for compatibility with
 @command{zstd} itself, you may instead set the @env{ZSTD_CLEVEL}
 environment variable, in which case, any @env{ZSTD_OPT} setting is
-ignored.
+ignored.  To build this only when @command{zstd} is present, see
+@code{AM_OPTIONAL_AUTOMAKE} (@pxref{Public Macros}).
 
 @item @code{dist-shar}
 @trindex dist-shar
index 5e9830ae6b24f79334f628a2065d7b390768fa4e..6b76fd2b9663aa7335f9fe83184fb5d8e262a702 100644 (file)
@@ -43,6 +43,7 @@ dist_automake_ac_DATA = \
   %D%/missing.m4 \
   %D%/mkdirp.m4 \
   %D%/obsolete.m4 \
+  %D%/optional.m4 \
   %D%/options.m4 \
   %D%/python.m4 \
   %D%/prog-cc-c-o.m4 \
diff --git a/m4/optional.m4 b/m4/optional.m4
new file mode 100644 (file)
index 0000000..9d0e96b
--- /dev/null
@@ -0,0 +1,135 @@
+##                                                       -*- Autoconf -*-
+## AM_OPTIONAL_AUTOMAKE -- skip `dist-XXX' formats whose tool is absent.
+
+# Copyright (C) 2026 Free Software Foundation, Inc.
+#
+# This file is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+
+# AM_OPTIONAL_AUTOMAKE(OPTIONS)
+# -----------------------------
+# OPTIONS: whitespace-separated `dist-XXX' names (AM_INIT_AUTOMAKE
+# spellings) for dist-bzip2/bzip3/xz/lzip/zstd/zip/shar/tarZ.  `make
+# dist' builds each archive when its tool is present and skips it
+# otherwise, without failing.  Unknown OPTION warns at autoreconf; a
+# tool absent at configure appends no rule; a tool lost afterwards
+# fails the pipeline, which the recipe catches.  See bug#81040.
+AC_DEFUN([AM_OPTIONAL_AUTOMAKE],
+[m4_foreach_w([_am_opt_o], [$1],
+   [_AM_OPTIONAL_AUTOMAKE_ONE(_m4_defn([_am_opt_o]))])dnl
+])
+
+
+# _AM_OPTIONAL_AUTOMAKE_ONE(OPT)
+# ------------------------------
+# Dispatch on OPT; unrecognised OPT warns and is ignored.
+AC_DEFUN([_AM_OPTIONAL_AUTOMAKE_ONE],
+[m4_case([$1],
+  [dist-bzip2], [_AM_OPTIONAL_TAR([bzip2], [bzip2], [bz2], [-c])],
+  [dist-bzip3], [_AM_OPTIONAL_TAR([bzip3], [bzip3], [bz3], [-c])],
+  [dist-xz],    [_AM_OPTIONAL_TAR([xz],    [xz],    [xz],  [-c])],
+  [dist-lzip],  [_AM_OPTIONAL_TAR([lzip],  [lzip],  [lz],  [-c])],
+  [dist-zstd],  [_AM_OPTIONAL_TAR([zstd],  [zstd],  [zst], [-c -q])],
+  [dist-tarZ],  [_AM_OPTIONAL_TAR([compress], [compress], [Z], [-c])],
+  [dist-zip],   [_AM_OPTIONAL_ZIP()],
+  [dist-shar],  [_AM_OPTIONAL_SHAR()],
+  [m4_warn([syntax],
+     [am-optional: unknown option `$1', ignoring it])])dnl
+])
+
+
+# _AM_OPTIONAL_RULE(TAG, VAR, RECIPE, ARTIFACTS)
+# ----------------------------------------------
+# AC_CHECK_PROG for TAG, then once append RECIPE to the top Makefile
+# via AC_CONFIG_COMMANDS (survives config.status reruns) iff configure
+# found the tool.  Hooked into dist/dist-all as an extra prerequisite.
+# ARTIFACTS stay out of $(DIST_ARCHIVES), so distcleancheck must remove
+# the copies that the in-tree `make dist' run by `make distcheck' leaves.
+AC_DEFUN([_AM_OPTIONAL_RULE],
+[m4_ifdef([_AM_OPTIONAL_DONE_$1], [],
+ [m4_define([_AM_OPTIONAL_DONE_$1])dnl
+AC_CHECK_PROG([am__optional_$2], [$1], [$1])
+AC_SUBST([am__optional_$2])dnl
+AC_CONFIG_COMMANDS([am--optional-rule-$1],
+[am__optional_mf=
+for am__optional_cand in Makefile GNUmakefile makefile; do
+  if test -f "$am__optional_cand"; then
+    am__optional_mf=$am__optional_cand
+    break
+  fi
+done
+if test -n "$am__optional_mf" && test -n "$am__optional_$2"; then
+  cat >>"$am__optional_mf" <<'_am_opt_rule_eof_'
+
+# Appended by am-optional for dist-$1.
+dist dist-all: am--optional-dist-$1
+distcleancheck: am--optional-rm-$1
+.PHONY: am--optional-dist-$1 am--optional-rm-$1
+$3
+am--optional-rm-$1:
+       -rm -f $4
+_am_opt_rule_eof_
+fi
+],
+[am__optional_$2="$am__optional_$2"
+])])dnl
+])
+
+
+# _AM_OPTIONAL_TAR(TOOL, VAR-TAG, EXT, FLAGS)
+# -------------------------------------------
+# Single-tool tar pipe; TOOL is last, so its failure is what's caught.
+AC_DEFUN([_AM_OPTIONAL_TAR],
+[_AM_OPTIONAL_RULE([$1], [$2],
+[am--optional-dist-$1: distdir
+       @if test -n "$(am__optional_$2)"; then \
+         { tardir=$(distdir) \
+           && $(am__tar) | "$(am__optional_$2)" $4 > $(distdir).tar.$3; } \
+         || { rm -f $(distdir).tar.$3; \
+              echo "am-optional: dist-$1 failed; archive not built" >&2; }; \
+       else \
+         echo "am-optional: $1 unavailable, skipping $(distdir).tar.$3" >&2; \
+       fi; \
+       :], [$(distdir).tar.$3])dnl
+])
+
+
+# _AM_OPTIONAL_ZIP
+# ----------------
+AC_DEFUN([_AM_OPTIONAL_ZIP],
+[_AM_OPTIONAL_RULE([zip], [zip],
+[am--optional-dist-zip: distdir
+       @if test -n "$(am__optional_zip)"; then \
+         rm -f $(distdir).zip; \
+         { "$(am__optional_zip)" -rq $(distdir).zip $(distdir); } \
+         || { rm -f $(distdir).zip; \
+              echo "am-optional: dist-zip failed; archive not built" >&2; }; \
+       else \
+         echo "am-optional: zip unavailable, skipping $(distdir).zip" >&2; \
+       fi; \
+       :], [$(distdir).zip])dnl
+])
+
+
+# _AM_OPTIONAL_SHAR
+# -----------------
+# Only `shar' is optional (gzip is a hard automake prereq).  shar
+# writes an intermediate file instead of piping into gzip: piped, gzip
+# would succeed on empty input and leave a bogus .shar.gz when shar
+# failed; the `&&' chain catches shar's exit instead.
+AC_DEFUN([_AM_OPTIONAL_SHAR],
+[_AM_OPTIONAL_RULE([shar], [shar],
+[am--optional-dist-shar: distdir
+       @if test -n "$(am__optional_shar)"; then \
+         rm -f $(distdir).shar $(distdir).shar.gz; \
+         { "$(am__optional_shar)" $(distdir) > $(distdir).shar \
+           && eval GZIP= gzip $(GZIP_ENV) -f $(distdir).shar; } \
+         || { rm -f $(distdir).shar $(distdir).shar.gz; \
+              echo "am-optional: dist-shar failed; archive not built" >&2; }; \
+       else \
+         echo "am-optional: shar unavailable, skipping $(distdir).shar.gz" >&2; \
+       fi; \
+       :], [$(distdir).shar $(distdir).shar.gz])dnl
+])
diff --git a/t/am-optional-automake.sh b/t/am-optional-automake.sh
new file mode 100644 (file)
index 0000000..e41dc53
--- /dev/null
@@ -0,0 +1,185 @@
+#! /bin/sh
+# Copyright (C) 2026 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 2, 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/>.
+
+# Check AM_OPTIONAL_AUTOMAKE.  Five scenarios:
+#  (1) The tool is on PATH at configure time -> dist-bzip3 produces
+#      $distdir.tar.bz3 alongside the always-built $distdir.tar.gz.
+#  (2) An unknown option produces an autoreconf-time `syntax' warning
+#      but does not break configure or make dist.
+#  (3) The tool is missing at configure time -> no extra rule is
+#      appended; `make dist' runs cleanly and does not produce
+#      $distdir.tar.bz3.
+#  (4) The tool is present at configure time but fails when invoked
+#      by `make dist' -> the recipe cleans up, prints a warning to
+#      stderr, `make dist' still succeeds, and no $distdir.tar.bz3
+#      appears.
+#  (5) dist-shar and dist-tarZ, the two non-tar-pipe formats.
+
+required='bzip3'
+. test-init.sh
+
+: "Test 1: bzip3 on PATH at configure time."
+
+cat > configure.ac <<'END'
+AC_INIT([am-optional-automake], [1.0])
+AM_INIT_AUTOMAKE([foreign])
+AM_OPTIONAL_AUTOMAKE([dist-bzip3])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+END
+: > Makefile.am
+
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE --add-missing
+./configure
+$MAKE dist
+test -s am-optional-automake-1.0.tar.gz
+test -s am-optional-automake-1.0.tar.bz3
+# These archives are not in $(DIST_ARCHIVES); distcheck must still
+# pass (its in-tree copies get cleaned before distcleancheck).
+$MAKE distcheck
+test -s am-optional-automake-1.0.tar.bz3
+
+rm -rf autom4te*.cache aclocal.m4 configure Makefile.in install-sh \
+       missing config.status config.log Makefile \
+       am-optional-automake-1.0 am-optional-automake-1.0.tar.*
+
+: "Test 2: Unknown option is a warning, not an error."
+
+cat > configure.ac <<'END'
+AC_INIT([am-optional-automake], [1.0])
+AM_INIT_AUTOMAKE([foreign])
+AM_OPTIONAL_AUTOMAKE([dist-banana dist-bzip3])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+END
+: > Makefile.am
+
+# aclocal/autoconf must succeed.  The `syntax' warning is emitted by
+# m4_warn() during macro expansion, which happens when autoconf runs
+# (aclocal only scans).  We don't promote it to error here: AM_INIT's
+# default warning set is plenty.  Capture autoconf's stderr and
+# assert the warning mentions the offending token.
+$ACLOCAL
+$AUTOCONF -Wno-error 2>stderr
+grep "dist-banana" stderr
+
+$AUTOMAKE --add-missing
+./configure
+$MAKE dist
+test -s am-optional-automake-1.0.tar.bz3
+
+rm -rf autom4te*.cache aclocal.m4 configure Makefile.in install-sh \
+       missing config.status config.log Makefile stderr \
+       am-optional-automake-1.0 am-optional-automake-1.0.tar.*
+
+: "Test 3: bzip3 missing at configure time."
+
+cat > configure.ac <<'END'
+AC_INIT([am-optional-automake], [1.0])
+AM_INIT_AUTOMAKE([foreign])
+AM_OPTIONAL_AUTOMAKE([dist-bzip3])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+END
+: > Makefile.am
+
+# Make AC_CHECK_PROG see bzip3 as absent without touching PATH, by
+# pre-seeding its cache variable to the empty string.
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE --add-missing
+ac_cv_prog_am__optional_bzip3= ./configure
+$MAKE dist
+test -s am-optional-automake-1.0.tar.gz
+test ! -e am-optional-automake-1.0.tar.bz3
+
+rm -rf autom4te*.cache aclocal.m4 configure Makefile.in \
+       install-sh missing config.status config.log Makefile \
+       am-optional-automake-1.0 am-optional-automake-1.0.tar.*
+
+: "Test 4: bzip3 present at configure, vanished at make dist."
+
+cat > configure.ac <<'END'
+AC_INIT([am-optional-automake], [1.0])
+AM_INIT_AUTOMAKE([foreign])
+AM_OPTIONAL_AUTOMAKE([dist-bzip3])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+END
+: > Makefile.am
+
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE --add-missing
+./configure  # bzip3 IS visible here, so the rule is appended
+
+# Shadow bzip3 with a stub that exits non-zero.  The recipe must
+# catch the failure, clean up the partial archive, print a warning,
+# leave the rest of `make dist' running, and exit 0.
+mkdir stub-bin
+cat > stub-bin/bzip3 <<'EOS'
+#! /bin/sh
+exit 1
+EOS
+chmod +x stub-bin/bzip3
+
+saved_PATH=$PATH
+PATH=`pwd`/stub-bin:$PATH
+$MAKE dist 2>stderr
+PATH=$saved_PATH
+
+test -s am-optional-automake-1.0.tar.gz
+test ! -e am-optional-automake-1.0.tar.bz3
+grep 'am-optional:.*dist-bzip3 failed' stderr
+
+rm -rf stub-bin autom4te*.cache aclocal.m4 configure Makefile.in \
+       install-sh missing config.status config.log Makefile stderr \
+       am-optional-automake-1.0 am-optional-automake-1.0.tar.*
+
+: "Test 5: dist-shar and dist-tarZ alongside the tar-pipe forms."
+# These two formats do not follow the single-tool tar-pipe pattern,
+# so they get their own exercise.  shar and compress are rarely
+# installed, so only assert the archive when the tool is actually
+# present; otherwise we just check that `make dist' did not fail.
+
+cat > configure.ac <<'END'
+AC_INIT([am-optional-automake], [1.0])
+AM_INIT_AUTOMAKE([foreign])
+AM_OPTIONAL_AUTOMAKE([dist-shar dist-tarZ])
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT
+END
+: > Makefile.am
+
+$ACLOCAL
+$AUTOCONF
+$AUTOMAKE --add-missing
+./configure
+$MAKE dist
+test -s am-optional-automake-1.0.tar.gz
+
+if (command -v shar) >/dev/null 2>&1; then
+  test -s am-optional-automake-1.0.shar.gz
+  # The intermediate uncompressed .shar must not be left behind.
+  test ! -e am-optional-automake-1.0.shar
+fi
+if (command -v compress) >/dev/null 2>&1; then
+  test -s am-optional-automake-1.0.tar.Z
+fi
+
+:
index 67bf7b5b98845f53e156fe445d1c4e9b4cfcdec9..e0446984e73887576086373ac9d6223b1fc96b07 100644 (file)
@@ -137,6 +137,7 @@ t/alpha2.sh \
 t/amhello-cflags.sh \
 t/amhello-cross-compile.sh \
 t/amhello-binpkg.sh \
+t/am-optional-automake.sh \
 t/aminit-moreargs-deprecation.sh \
 t/aminit-trailing-dnl-comment-pr16841.sh \
 t/amassign.sh \