autoconf: Warn if AC_INIT or AC_OUTPUT are missing from configure.ac (#107986)

https://github.com/libsdl-org/autoconf/commit/96a8e5f1c96b1d1d98588e8c5b81a1ac7d482451

From 96a8e5f1c96b1d1d98588e8c5b81a1ac7d482451 Mon Sep 17 00:00:00 2001
From: Zack Weinberg <[EMAIL REDACTED]>
Date: Fri, 14 Aug 2020 13:16:58 -0400
Subject: [PATCH] Warn if AC_INIT or AC_OUTPUT are missing from configure.ac
 (#107986)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

It is almost always incorrect for a configure script to omit either
AC_INIT or AC_OUTPUT.  Issue warnings in the ‘syntax’ category for
this.

The implementation is, unfortunately, a bit of a kludge.  To check for
the _absence_ of a macro invocation, we can use m4_provide_if inside a
m4_wrap hook.  However, if we activate the m4_wrap hook directly from
general.m4, we get spurious warnings at freeze time.  We also get
warnings whenever a script that’s missing AC_INIT and/or AC_OUTPUT
is *traced*, which means we get double warnings from autoconf, and
autoheader and aclocal complain about it too, which seems unnecessary.

A clean way to deal with this would be to make the hook look for a
special macro that’s defined only when autoconf (the program) is
invoked without any --trace arguments.  Unfortunately, autom4te
doesn’t pass --define down to M4, and changing that would involve
coordinating with Automake (the project), so instead I’ve gone for the
kludge: a new file lib/autoconf/trailer.m4 that calls m4_wrap.  This
file is *not* included in autoconf.m4f, but it’s installed, and it’s
added to the m4 invocation by autoconf (the program) only when not
tracing.  (It still uses m4_wrap, because we pass it to m4 *before*
configure.ac, because otherwise we get nonsense locations for any
*other* diagnostics coming out of this autoconf invocation.  I don’t
know why.)

The additional checks in autoreconf are intended to make sure that if
autoreconf skips a directory entirely, you get told why.

Lots of tests in the testsuite didn’t bother with AC_OUTPUT, and
somewhat fewer didn’t bother with AC_INIT; where possible I just added
them.

Suggested by David A. Wheeler, who submitted a patch, but I didn’t
wind up using any of his code.  (His implementation used an extra
tracing pass, only checked for a missing AC_INIT, and invented a new
command-line option to turn off this specific warning.  I thought this
was tidier overall, despite the kludge.)

* lib/autoconf/general.m4 (_AC_FINALIZE): New macro: code to be run
  when generating configure, after the entire configure.ac is
  processed. Currently only checks that AC_INIT and AC_OUTPUT were
  called at some point, issuing syntax-category warnings if not.
  (AC_INIT, AC_OUTPUT): m4_provide self.
* lib/autoconf/trailer.m4: New file that just calls m4_wrap([_AC_FINALIZE]).
* lib/local.mk: Install new file.

* bin/autoconf.as: Add trailer.m4 to the final invocation of autom4te,
  but only when not tracing.
* bin/autoreconf.in (autoreconf_current_directory): Distinguish in
  diagnostics between “directory skipped because it doesn’t have a
  configure.ac or configure.in” (e.g. Cygnus configure) and “directory
  has a configure.ac but it doesn’t appear to be autoconf input.”

* tests/*.at: Fix all tests affected by the new warnings.
---
 NEWS                    |  3 +++
 bin/autoconf.as         | 14 +++++++++++++-
 bin/autoreconf.in       | 14 ++++++++++----
 lib/autoconf/general.m4 | 23 +++++++++++++++++++++++
 lib/autoconf/status.m4  |  6 ++++++
 lib/autoconf/trailer.m4 |  4 ++++
 lib/local.mk            |  3 ++-
 tests/base.at           | 33 ++++++++++++++++++++++++++-------
 tests/c.at              |  1 +
 tests/compile.at        | 26 +++++++++++++++++++++++---
 tests/m4sh.at           |  5 ++++-
 tests/semantics.at      | 12 ++++++------
 tests/tools.at          | 28 +++++++++++++++++++++++-----
 tests/torture.at        | 13 ++++++++-----
 tests/wrapper.as        |  3 ++-
 15 files changed, 154 insertions(+), 34 deletions(-)
 create mode 100644 lib/autoconf/trailer.m4

diff --git a/NEWS b/NEWS
index e0cdd069..ccf0f40a 100644
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,9 @@ GNU Autoconf NEWS - User visible changes.
 ** The use of the long-deprecated name 'configure.in' for the autoconf
    input file now elicits a warning in the 'obsolete' category.
 
+** autoconf will now issue warnings (in the ‘syntax’ category) if the
+   input file is missing a call to AC_INIT and/or AC_OUTPUT.
+
 ** Older version of automake and aclocal (< 1.8) are no longer supported
    by autoreconf.
 
diff --git a/bin/autoconf.as b/bin/autoconf.as
index 581eb81e..ab6e3c52 100644
--- a/bin/autoconf.as
+++ b/bin/autoconf.as
@@ -85,6 +85,7 @@ exit_missing_arg='
 
 # Variables.
 : ${AUTOM4TE='@bindir@/@autom4te-name@'}
+: ${trailer_m4='@pkgdatadir@/autoconf/trailer.m4'}
 autom4te_options=
 outfile=
 verbose=false
@@ -183,9 +184,20 @@ 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" \"\$infile\"
+  --language=autoconf --output=\"\$outfile\" "$traces" \
+  $trailer_m4 \"\$infile\"
 shift
 $verbose && AS_ECHO(["$as_me: running $AUTOM4TE $*"]) >&2
 exec "$AUTOM4TE" "$@"
diff --git a/bin/autoreconf.in b/bin/autoreconf.in
index 12806d3a..1ca11f28 100644
--- a/bin/autoreconf.in
+++ b/bin/autoreconf.in
@@ -245,8 +245,9 @@ sub parse_args ()
 
 # &autoreconf_current_directory
 # -----------------------------
-sub autoreconf_current_directory ()
+sub autoreconf_current_directory ($)
 {
+  my ($directory) = @_;
   my $configure_ac = find_configure_ac;
 
   # ---------------------- #
@@ -266,10 +267,15 @@ sub autoreconf_current_directory ()
 	  # See below for why we look for gettext here.
 	  $uses_gettext = 1  if /^AM_GNU_GETTEXT_VERSION/;
 	}
+      if (!$uses_autoconf)
+        {
+          error "$configure_ac: AC_INIT not found; not an autoconf script?";
+          return;
+        }
     }
-  if (!$uses_autoconf)
+  else
     {
-      verb "$configure_ac: not using Autoconf";
+      verb "neither configure.ac nor configure.in present in $directory";
       return;
     }
 
@@ -598,7 +604,7 @@ sub autoreconf ($)
   chdir $directory
     or error "cannot chdir to $directory: $!";
 
-  autoreconf_current_directory;
+  autoreconf_current_directory ($directory);
 
   # The format is not free: taken from Emacs, itself using GNU Make's
   # format.
diff --git a/lib/autoconf/general.m4 b/lib/autoconf/general.m4
index 4d5f021d..81360b78 100644
--- a/lib/autoconf/general.m4
+++ b/lib/autoconf/general.m4
@@ -1477,9 +1477,32 @@ _AC_ARG_VAR_PRECIOUS([host_alias])AC_SUBST([host_alias])dnl
 _AC_ARG_VAR_PRECIOUS([target_alias])AC_SUBST([target_alias])dnl
 dnl
 AC_LANG_PUSH(C)
+dnl
+dnl Record that AC_INIT has been called.  It doesn't make sense to
+dnl AC_REQUIRE AC_INIT, but it _does_ make sense for macros to say
+dnl AC_BEFORE([self], [AC_INIT]) sometimes.  Also, _AC_FINALIZE checks
+dnl for AC_INIT having been called.
+m4_provide([AC_INIT])dnl
 ])
 
 
+# _AC_FINALIZE
+# ------------
+# Code to be run after the entire configure.ac is processed, but only
+# when generating configure.  This macro should only be called from
+# trailer.m4, which is fed to m4 after configure.ac by autoconf (the
+# program).  We don't just call m4_wrap([_AC_FINALIZE]), because then
+# it would run at freeze time and when tracing configure.ac for
+# autoheader etc.
+#
+# Currently this doesn't emit anything; it just checks that AC_INIT
+# and AC_OUTPUT were expanded at some point.  Leaving either of these
+# out of a configure script is likely to be a bug.
+m4_define([_AC_FINALIZE],
+  [m4_provide_if([AC_INIT], [],
+    [m4_warn([syntax], [AC_INIT was never used])])dnl
+   m4_provide_if([AC_OUTPUT], [],
+    [m4_warn([syntax], [AC_OUTPUT was never used])])])
 
 
 ## ------------------------------------------------------------- ##
diff --git a/lib/autoconf/status.m4 b/lib/autoconf/status.m4
index dd2cb407..2d3f92ba 100644
--- a/lib/autoconf/status.m4
+++ b/lib/autoconf/status.m4
@@ -1310,6 +1310,12 @@ AC_PROVIDE_IFELSE([AC_CONFIG_SUBDIRS], [_AC_OUTPUT_SUBDIRS()])dnl
 if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then
   AC_MSG_WARN([unrecognized options: $ac_unrecognized_opts])
 fi
+dnl
+dnl Record that AC_OUTPUT has been called.  It doesn't make sense to
+dnl AC_REQUIRE AC_OUTPUT, but it _does_ make sense for macros to say
+dnl AC_BEFORE([self], [AC_OUTPUT]).  Also, _AC_FINALIZE checks
+dnl for AC_OUTPUT having been called.
+m4_provide([AC_OUTPUT])dnl
 ])# AC_OUTPUT
 
 
diff --git a/lib/autoconf/trailer.m4 b/lib/autoconf/trailer.m4
new file mode 100644
index 00000000..7c14cbff
--- /dev/null
+++ b/lib/autoconf/trailer.m4
@@ -0,0 +1,4 @@
+dnl This file is part of Autoconf.			-*- Autoconf -*-
+dnl This file exists solely to invoke _AC_FINALIZE at the right time.
+dnl See the definition of _AC_FINALIZE (in general.m4) for further explanation.
+m4_wrap([_AC_FINALIZE])dnl
diff --git a/lib/local.mk b/lib/local.mk
index 9612a14c..5cc9d44a 100644
--- a/lib/local.mk
+++ b/lib/local.mk
@@ -97,7 +97,8 @@ dist_autoconflib_DATA = \
   lib/autoconf/headers.m4 \
   lib/autoconf/types.m4 \
   lib/autoconf/libs.m4 \
-  lib/autoconf/programs.m4
+  lib/autoconf/programs.m4 \
+  lib/autoconf/trailer.m4
 
 nodist_autoconflib_DATA = lib/autoconf/autoconf.m4f
 CLEANFILES += $(nodist_autoconflib_DATA)
diff --git a/tests/base.at b/tests/base.at
index 98125173..4042a8aa 100644
--- a/tests/base.at
+++ b/tests/base.at
@@ -57,7 +57,10 @@ test -z "$test1" &&
 AS_EXIT(0)
 ]])
 
-AT_CHECK_AUTOCONF
+AT_CHECK_AUTOCONF([], [0], [],
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+]])
 AT_CHECK_CONFIGURE
 
 AT_CLEANUP
@@ -117,7 +120,10 @@ case $multi_test:$single_test in
 esac
 ]])
 
-AT_CHECK_AUTOCONF([], 0, [])
+AT_CHECK_AUTOCONF([], 0, [],
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+]])
 
 AT_CHECK_CONFIGURE
 
@@ -160,7 +166,10 @@ case $multi_test:$single_test in
 esac
 ]])
 
-AT_CHECK_AUTOCONF([], 0, [])
+AT_CHECK_AUTOCONF([], 0, [],
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+]])
 AT_CHECK_CONFIGURE
 
 AT_CLEANUP
@@ -192,7 +201,10 @@ case $inner_test in
 esac
 ]])
 
-AT_CHECK_AUTOCONF
+AT_CHECK_AUTOCONF([], 0, [],
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+]])
 AT_CHECK_CONFIGURE
 
 AT_CLEANUP
@@ -208,6 +220,7 @@ AT_SETUP([AC_INIT])
 
 AT_DATA([configure.ac],
 [[AC_INIT([GNU fu], [1.0], [bug-fu@gnu.org])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -354,6 +367,7 @@ words that may be matched by scanners for legal things,
 causing extra work for distributors.
 Multi-line values should be supported.
 ]])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -403,8 +417,7 @@ if ${my_cv_variable+false} :; then
   AC_MSG_ERROR([AC@&@&t@t@_CACHE_VAL did not ensure that the cache variable was set])
 fi
 
-# AC_CACHE_SAVE should be enough here, no need for AC_OUTPUT.
-AC_CACHE_SAVE
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF([], [], [], [stderr])
@@ -603,6 +616,7 @@ AC_COMPUTE_INT([invalid_expression],
 	       [invalid_expression=failed])
 test "$invalid_expression" = failed ||
   AC_MSG_ERROR([**0** evaluated to $invalid_expression instead of failing])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -633,6 +647,8 @@ if AC_TRY_COMMAND([(echo "The Cat in the Hat";
 		   grep \^The\ Hat\ in\ the\ Cat\$ >/dev/null]); then
   AC_MSG_ERROR([saw the Hat in the Cat])
 fi
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -650,9 +666,10 @@ AT_SETUP([Input/Output])
 AT_DATA([configure.ac],
 [[AC_INIT
 cat <&AS@&t@_ORIGINAL_STDIN_FD >&AS@&t@_MESSAGE_FD
+AC_OUTPUT
 ]])
 AT_CHECK_AUTOCONF
-AT_CHECK([echo Hello | CONFIG_SITE=/dev/null ./configure $configure_options | grep -v 'configure: loading site script '],, [Hello
+AT_CHECK([echo Hello | CONFIG_SITE=/dev/null ./configure $configure_options | grep -v '^configure: '],, [Hello
 ])
 AT_CHECK([echo Hello | CONFIG_SITE=/dev/null ./configure $configure_options --silent])
 
@@ -668,6 +685,7 @@ AT_SETUP([configure arguments])
 AT_DATA([configure.ac],
 [[AC_INIT
 echo "$@"
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -706,6 +724,7 @@ AC_ARG_ENABLE([c++],
 echo "use_foo: $use_foo"
 echo "with_c++: $with_c__, $choice_with"
 echo "enable_c++: $enable_c__, $choice_enable"
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
diff --git a/tests/c.at b/tests/c.at
index b65d7d44..b1dabd14 100644
--- a/tests/c.at
+++ b/tests/c.at
@@ -60,6 +60,7 @@ AT_DATA([configure.ac],
 [[AC_INIT
 CC=no-such-compiler
 AC_PROG_CC
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
diff --git a/tests/compile.at b/tests/compile.at
index 5d75ada8..fbba8e63 100644
--- a/tests/compile.at
+++ b/tests/compile.at
@@ -55,6 +55,7 @@ AC_LANG_POP([C++])
 # C C
 AC_LANG_POP([C])
 # C
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -106,7 +107,10 @@ AC_F77_2
 AS_EXIT(0)
 ]])
 
-AT_CHECK_AUTOCONF
+AT_CHECK_AUTOCONF([], [0], [],
+[[trailer.m4: warning: AC_OUTPUT was never used
+]])
+
 AT_CHECK_CONFIGURE
 
 AT_CLEANUP
@@ -129,6 +133,7 @@ int main (void)
   return 0;
 }
 ]], [], [AC_MSG_FAILURE([confdefs not included])])])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -155,6 +160,7 @@ int main (void)
   return 0;
 }
 ]], [], [AC_MSG_FAILURE([confdefs not included])])])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -185,6 +191,7 @@ AC_LANG([C])
 AC_LANG_CONFTEST(
    [AC_LANG_SOURCE([[const char hw[] = "Hello, World\n";]])])
 gcc -E -dD conftest.c || AS_EXIT([77])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -193,7 +200,7 @@ AT_CHECK_CONFIGURE([], [], [stdout])
 # Note that the output may contain more defines and lines matching
 #   # 1 "conftest.c"
 # so delete everything before the interesting output.
-AT_CHECK([sed -n 's/ *$//; /#define PACKAGE/,$p' stdout], [],
+AT_CHECK([sed -n 's/ *$//; /^configure: /d; /#define PACKAGE/,$p' stdout], [],
 [[#define PACKAGE_NAME "Hello"
 #define PACKAGE_TARNAME "hello"
 #define PACKAGE_VERSION "1.0"
@@ -230,6 +237,7 @@ AC_LANG_CONFTEST(
 [AC_LANG_PROGRAM([[const char hw[] = "Hello, World\n";]],
                  [[fputs (hw, stdout);]])])
 gcc -E -dD conftest.c || AS_EXIT([77])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -238,7 +246,7 @@ AT_CHECK_CONFIGURE([], [], [stdout])
 # Note that the output may contain more defines and lines matching
 #   # 1 "conftest.c"
 # so delete everything before the interesting output.
-AT_CHECK([sed -n 's/ *$//; /#define PACKAGE/,$p' stdout], [],
+AT_CHECK([sed -n 's/ *$//; /^configure: /d; /#define PACKAGE/,$p' stdout], [],
 [[#define PACKAGE_NAME "Hello"
 #define PACKAGE_TARNAME "hello"
 #define PACKAGE_VERSION "1.0"
@@ -271,6 +279,7 @@ AT_DATA([configure.ac],
 [[AC_INIT
 AC_COMPILE_IFELSE([int main (void) { return 0; }], [],
   [AC_MSG_ERROR([compiling trivial program failed])])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF([], [], [], [stderr])
@@ -282,6 +291,7 @@ AT_DATA([configure.ac],
 [[AC_INIT
 AC_COMPILE_IFELSE([AC_LANG_DEFINES_PROVIDED()int main (void) { return 0; }], [],
   [AC_MSG_ERROR([compiling trivial program failed])])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -296,6 +306,7 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [return 0])],
 AC_COMPILE_IFELSE([AC_LANG_PROGRAM([], [return 2])],
 	      [],
 	      [AC_MSG_ERROR([compiling `return 2' failed])])
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -330,6 +341,7 @@ AC_TRY_RUN([int main (void) { return 3; }],
 test $estatus != 3 &&
   AC_MSG_ERROR([did not get 3 as exit status: $estatus])])
 
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -375,6 +387,8 @@ d@&t@nl conftest.err not generated by AC_RUN_IFELSE?
 AC_RUN_IFELSE([AC_LANG_PROGRAM([int bad bad;], [])],
 	      [AS_EXIT([1])],
 	      [])
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -410,6 +424,8 @@ AC_COMPILE_IFELSE([
     choke me
     #endif
   ]])], [], AS_EXIT([77]))
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -428,6 +444,8 @@ AC_COMPILE_IFELSE([
     #endif
   ]])], [], AS_EXIT([77]))
 AC_LANG_POP([C++])
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -457,6 +475,8 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([class A {};], [])],
 
 AC_CHECK_HEADER([cstring])
 AC_LANG_POP([C++])
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
diff --git a/tests/m4sh.at b/tests/m4sh.at
index a18277ac..b352704d 100644
--- a/tests/m4sh.at
+++ b/tests/m4sh.at
@@ -112,6 +112,7 @@ AT_SETUP([Configure re-execs self with CONFIG_SHELL])
 AT_DATA([configure.ac],
 [[AC_INIT
 echo foobar >> quux
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -127,7 +128,9 @@ AT_CAPTURE_FILE([config.log])
 # Export CONFIG_SITE to /dev/null to avoid spurious diffs in expected
 # stdout/stderr.
 AT_CHECK([env CONFIG_SITE=/dev/null CONFIG_SHELL=./cfg-sh ./configure],
-         [0], [], [])
+         [0],
+[[configure: creating ./config.status
+]], [])
 # ./configure re-executed itself.
 AT_CHECK([test -f cfg-sh-has-run], [0])
 # And did that not to cause extra execution of later commands.
diff --git a/tests/semantics.at b/tests/semantics.at
index f10a0b53..98933aa1 100644
--- a/tests/semantics.at
+++ b/tests/semantics.at
@@ -589,10 +589,10 @@ test -z "$TOOL5" || fail=:
 AC_CHECK_PROGS(TOOL6, missing tool better,, $path)
 test "$TOOL6" = tool || fail=:
 
-# No AC-OUTPUT, we don't need config.status.
 $fail &&
   AC_MSG_ERROR([[CHECK_PROG failed]])
-AS_EXIT(0)
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -692,10 +692,10 @@ test -z "$TOOL3" || fail=:
 AC_PATH_PROGS(TOOL4, missing tool better,, $path)
 test "$TOOL4" = "$pwd/path/1/tool" || fail=:
 
-# No AC-OUTPUT, we don't need config.status.
 $fail &&
   AC_MSG_ERROR([[PATH_PROG failed]])
-AS_EXIT(0)
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
@@ -770,10 +770,10 @@ AC_PATH_PROGS_FEATURE_CHECK(TOOL7, [tool better],
 test -z "$TOOL7" || fail=:
 test "$ac_cv_path_TOOL7" = "$pwd/path/1/tool" || fail=:
 
-# No AC-OUTPUT, we don't need config.status.
 $fail &&
   AC_MSG_ERROR([[PATH_PROG failed]])
-AS_EXIT(0)
+
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
diff --git a/tests/tools.at b/tests/tools.at
index 314462ff..3093225c 100644
--- a/tests/tools.at
+++ b/tests/tools.at
@@ -390,6 +390,7 @@ AT_DATA([configure.ac],
 [[AC_INIT
 AC_CONFIG_MACRO_DIR([dir1])
 AC_CONFIG_MACRO_DIR([dir2])
+AC_OUTPUT
 ]])
 AT_CHECK_AUTOCONF([], [1], [], [stderr])
 AT_CHECK([grep 'error: AC_CONFIG_MACRO_DIR can only be used once' stderr],
@@ -421,6 +422,7 @@ AT_DATA([configure.ac],
 [[AC_INIT
 AC_CONFIG_MACRO_DIRS([dir1])
 AC_CONFIG_MACRO_DIRS([dir2])
+AC_OUTPUT
 ]])
 AT_CHECK_AUTOCONF([], [0], [], [])
 AT_CHECK_AUTOCONF([-t AC_CONFIG_MACRO_DIR], [0],
@@ -459,7 +461,9 @@ end-language: "Autoconf-without-aclocal-m4"
 ]])
 
 AT_CHECK_AUTOCONF([], 1, [],
-[[configure.ac:2: error: possibly undefined macro: m4@&t@_foo
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+configure.ac:2: error: possibly undefined macro: m4@&t@_foo
       If this token and others are legitimate, please use m4@&t@_pattern_allow.
       See the Autoconf documentation.
 configure.ac:3: error: possibly undefined macro: _m4@&t@_bar
@@ -467,8 +471,12 @@ configure.ac:4: error: possibly undefined macro: AS@&t@_FOO
 configure.ac:5: error: possibly undefined macro: _AS@&t@_BAR
 configure.ac:6: error: possibly undefined macro: d@&t@nl
 ]])
-# Second run should succeed and yield no output.
-AT_CHECK([autoconf])
+# A second run (without --force) should succeed and yield only the
+# warnings about AC_INIT and AC_OUTPUT.
+AT_CHECK_M4([autoconf], 0, [],
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+]])
 
 AT_CLEANUP
 
@@ -500,7 +508,9 @@ It would be very bad if Autoconf forgot to expand [AS_]INIT!
 ]])
 
 AT_CHECK_AUTOCONF([], 1, [],
-[[configure.ac:1: error: possibly undefined macro: AS@&t@_INIT
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+configure.ac:1: error: possibly undefined macro: AS@&t@_INIT
       If this token and others are legitimate, please use m4@&t@_pattern_allow.
       See the Autoconf documentation.
 configure.ac:7: error: possibly undefined macro: AS@&t@_ALLOWED_NOT
@@ -539,7 +549,10 @@ m4_divert([])dnl
  line that begins with a space
 ]])
 
-AT_CHECK_AUTOCONF
+AT_CHECK_AUTOCONF([], 0, [],
+[[trailer.m4: warning: AC_INIT was never used
+trailer.m4: warning: AC_OUTPUT was never used
+]])
 
 AT_CLEANUP
 
@@ -1009,6 +1022,7 @@ AT_DATA([configure.ac],
 [[AC_INIT
 OLD(1, 2)
 NEW([0, 0], [0])
+AC_OUTPUT
 ]])
 
 # Checking `autoupdate'.
@@ -1053,6 +1067,7 @@ AT_SETUP([autoupdating AC_HELP_STRING])
 AT_DATA([configure.ac],
 [[AC_INIT
 AC_ARG_ENABLE([foo], [AC_HELP_STRING([--enable-foo], [foo bar])], [:], [:])
+AC_OUTPUT
 ]])
 
 # Checking `autoupdate'.
@@ -1189,6 +1204,7 @@ AC_LANG_SAVE
 AC_LANG_RESTORE
 AC_LANG_SAVE
 AC_LANG_RESTORE
+AC_OUTPUT
 ]])
 
 # Checking `autoupdate'.
@@ -1210,6 +1226,7 @@ AT_DATA([aclocal.m4],
 AT_DATA([configure.ac],
 [[AC_INIT
 echo AC_FOREACH([myvar], [1 2 3], [' myvar'])OLD
+AC_OUTPUT
 ]])
 
 # Checking `autoupdate'.
@@ -1318,6 +1335,7 @@ chmod a-w sub
 
 AT_DATA([configure.ac],
 [[AC_INIT
+AC_OUTPUT
 ]])
 
 AT_DATA([.autom4te.cfg],
diff --git a/tests/torture.at b/tests/torture.at
index b50dfebc..37deef25 100644
--- a/tests/torture.at
+++ b/tests/torture.at
@@ -1037,12 +1037,12 @@ two], [This spans two lines.])
 AT_CHECK_AUTOCONF([], [], [], [stderr])
 dnl Older versions of m4 report error at line 5 (end of macro);
 dnl newer versions report it at line 4 (start of macro).
-AT_CHECK([[sed 's/^configure.ac:[45]: //' stderr]], [],
+AT_CHECK([[sed '/trailer\.m4:/d; s/^configure\.ac:[45]: //' stderr]], [],
 [[warning: AC_DEFINE: `one
 two' is not a valid preprocessor define value
 ]])
 AT_CHECK_AUTOHEADER([], [foo], [], [], [stderr])
-AT_CHECK([[sed 's/^configure.ac:[45]: //' stderr]], [],
+AT_CHECK([[sed 's/^configure\.ac:[45]: //' stderr]], [],
 [[warning: AC_DEFINE: `one
 two' is not a valid preprocessor define value
 ]])
@@ -1050,16 +1050,18 @@ AT_CHECK_CONFIGURE
 AT_CHECK_DEFINES([[#define foo one
 ]])
 
+rm -rf autom4te.cache
+
 AT_CONFIGURE_AC([[AC_DEFINE_UNQUOTED([foo], [one
 two], [This spans two lines.])
 ]])
 AT_CHECK_AUTOCONF([], [], [], [stderr])
-AT_CHECK([[sed 's/^configure.ac:[45]: //' stderr]], [],
+AT_CHECK([[sed '/trailer.m4:/d; s/^configure\.ac:[45]: //' stderr]], [],
 [[warning: AC_DEFINE_UNQUOTED: `one
 two' is not a valid preprocessor define value
 ]])
 AT_CHECK_AUTOHEADER([], [foo], [], [], [stderr])
-AT_CHECK([[sed 's/^configure.ac:[45]: //' stderr]], [],
+AT_CHECK([[sed 's/^configure\.ac:[45]: //' stderr]], [],
 [[warning: AC_DEFINE_UNQUOTED: `one
 two' is not a valid preprocessor define value
 ]])
@@ -1290,10 +1292,11 @@ AT_DATA([configure.ac],
 [[AC_INIT
 kill -2 $$
 exit 77
+AC_OUTPUT
 ]])
 
 AT_CHECK_AUTOCONF
-AT_CHECK_CONFIGURE([], 1, ignore, ignore)
+AT_CHECK_CONFIGURE([], [1], [ignore], [ignore])
 
 AT_CLEANUP
 
diff --git a/tests/wrapper.as b/tests/wrapper.as
index 8f77053e..f0a5c037 100644
--- a/tests/wrapper.as
+++ b/tests/wrapper.as
@@ -24,7 +24,8 @@ AUTOHEADER=autoheader
 AUTOM4TE=autom4te
 AUTOM4TE_CFG='@abs_top_builddir@/lib/autom4te.cfg'
 autom4te_perllibdir='@abs_top_srcdir@/lib'
-export AUTOCONF AUTOHEADER AUTOM4TE AUTOM4TE_CFG autom4te_perllibdir
+trailer_m4='@abs_top_srcdir@/lib/autoconf/trailer.m4'
+export AUTOCONF AUTOHEADER AUTOM4TE AUTOM4TE_CFG autom4te_perllibdir trailer_m4
 
 case '@wrap_program@' in
   ifnames)