# $VERSION
# --------
-$version = "autom4te (@PACKAGE_NAME@) @VERSION@
-Copyright (C) @RELEASE_YEAR@ Free Software Foundation, Inc.
+$version = "autom4te (@PACKAGE_NAME@) @VERSION@\n"
+ . ($Autom4te::FileUtils::subsecond_mtime
+ ? "Features: subsecond-mtime\n" : "")
+ . "\nCopyright (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.
use strict;
use warnings FATAL => 'all';
-use Exporter;
-use IO::File;
+BEGIN
+{
+ require Exporter;
+ our @ISA = qw (Exporter);
+ our @EXPORT = qw (&contents
+ &find_file &mtime
+ &update_file
+ &xsystem &xsystem_hint &xqx
+ &dir_has_case_matching_file &reset_dir_cache
+ &set_dir_cache_file);
+}
-# use sub-second resolution timestamps if available,
-# carry on with one-second resolution timestamps if that is all we have
-BEGIN { eval { require Time::HiRes; import Time::HiRes qw(stat) } }
+# Use sub-second resolution file timestamps if available, carry on
+# with one-second resolution timestamps if Time::HiRes is not available.
+#
+# Unfortunately, even if Time::HiRes is available, we don't get
+# timestamps to the full precision recorded by the operating system,
+# because Time::HiRes converts timestamps to floating-point, and the
+# rounding error is hundreds of nanoseconds for circa-2023 timestamps
+# in IEEE double precision. But this is the best we can do without
+# dropping down to C.
+#
+# $subsecond_mtime is not exported, but is intended for external
+# consumption, as $Autom4te::FileUtils::subsecond_mtime.
+BEGIN
+{
+ our $subsecond_mtime = 0;
+ eval
+ {
+ require Time::HiRes;
+ import Time::HiRes qw(stat);
+ $subsecond_mtime = 1;
+ }
+}
+use IO::File;
use Autom4te::Channels;
use Autom4te::ChannelDefs;
-our @ISA = qw (Exporter);
-our @EXPORT = qw (&contents
- &find_file &mtime
- &update_file
- &xsystem &xsystem_hint &xqx
- &dir_has_case_matching_file &reset_dir_cache
- &set_dir_cache_file);
-
=over 4
=item C<find_file ($file_name, @include)>
$atime,$mtime,$ctime,$blksize,$blocks) = stat ($file)
or fatal "cannot stat $file: $!";
- # Unfortunately Time::HiRes converts timestamps to floating-point, and the
- # rounding error can be hundreds of nanoseconds for circa-2023 timestamps.
- # Perhaps some day Perl will support accurate file timestamps.
- # For now, do the best we can without going outside Perl.
-
return $mtime;
}
=cut
1; # for require
+
+### 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:
[m4_define([$1], [m4_ifndef([AT_ingroup],
[m4_fatal([$1: missing AT_SETUP detected])])$2])])
+# AS_MESSAGE_LOG_FD
+# -----------------
+# File descriptor open on the log file. This needs to be defined
+# unconditionally, so that AT_TESTS_PREPARE code can use it. Usage
+# prior to the point where the log file is opened will crash the
+# testsuite.
+m4_define([AS_MESSAGE_LOG_FD], [5])
+
+# AT_JOB_FIFO_{IN,OUT}_FD
+# -----------------
+# Used by the parallel test driver.
+# The parent needs two fds to the same fifo, otherwise, there is a race
+# where the parent can read the fifo before a child opens it for writing
+_AT_DEFINE_INIT([AT_JOB_FIFO_IN_FD], [6])
+_AT_DEFINE_INIT([AT_JOB_FIFO_OUT_FD], [7])
# AT_INIT([TESTSUITE-NAME])
# -------------------------
export PATH
# Setting up the FDs.
-m4_define([AS_MESSAGE_LOG_FD], [5])
-dnl The parent needs two fds to the same fifo, otherwise, there is a race
-dnl where the parent can read the fifo before a child opens it for writing
-m4_define([AT_JOB_FIFO_IN_FD], [6])
-m4_define([AT_JOB_FIFO_OUT_FD], [7])
[#] AS_MESSAGE_LOG_FD is the log file. Not to be overwritten if '-d'.
if $at_debug_p; then
at_suite_log=/dev/null
# our command line.
: "${MAKE=make}"
export MAKE
+
+# Determine how long we need to delay in between operations that might
+# modify autom4te.cache. This depends on three factors: whether the
+# 'sleep' utility supports fractional seconds in its argument; what
+# the resolution of last-modification timestamps is on the filesystem
+# hosting the build; and whether autom4te and automake can both make
+# use of high-resolution file timestamps (this is not entirely under
+# our control because it depends on the capabilities of the Perl
+# installation).
+#
+# This series of tests mostly cribbed from automake/m4/sanity.m4.
+# We cannot rely on the execution of that code inside autoconf's own
+# configure script, because (depending on what version of automake was
+# used to generate the configure script) it might not be there at all,
+# or might be buggy. Also, even if it's present and correct, it didn't
+# probe the autom4te we just built, which is the one we care about.
+#
+# The coarsest filesystem we know of is FAT, with a resolution
+# of only two seconds, even with the most recent "exFAT" extensions.
+# The finest (e.g. ext4 with large inodes, XFS, ZFS) is one
+# nanosecond, matching clock_gettime. However, it is probably not
+# possible to delay execution of a shell script for less than one
+# millisecond, due to process creation overhead and scheduling
+# granularity, so we don't check for anything finer than that.
+
+# Default to the coarsest case.
+at_ts_resolution=2
+
+# Only try to go finer than 1s if sleep, autom4te, and automake
+# can all handle it.
+at_try_resolutions=1
+if sleep 0.001 2>/dev/null &&
+ autom4te --version 2>&1 |
+ grep 'Features:.*subsecond-mtime' > /dev/null 2>&1 &&
+ automake --version 2>&1 |
+ grep 'Features:.*subsecond-mtime' > /dev/null 2>&1
+then
+ at_try_resolutions="0.001 0.01 0.1 $at_try_resolutions"
+fi
+
+# In order to catch current-generation FAT out, we must *modify* files
+# that already exist; the *creation* timestamp is finer. Use names
+# that make ls -t sort them differently when they have equal
+# timestamps than when they have distinct timestamps, keeping
+# in mind that ls -t prints the *newest* file first.
+rm -f conftest.ts?
+: > conftest.ts1
+: > conftest.ts2
+: > conftest.ts3
+
+for at_try_res in $at_try_resolutions; do
+ # Any one fine-grained sleep might happen to cross the boundary
+ # between two values of a coarser actual resolution, but if we do
+ # two fine-grained sleeps in a row, at least one of them will fall
+ # entirely within a coarse interval.
+ echo alpha > conftest.ts1
+ sleep $at_try_res
+ echo beta > conftest.ts2
+ sleep $at_try_res
+ echo gamma > conftest.ts3
+
+ # We assume that 'ls -t' will make use of high-resolution
+ # timestamps if the operating system supports them at all.
+ set X `ls -t conftest.ts?`
+ if test "$[]2" = conftest.ts3 &&
+ test "$[]3" = conftest.ts2 &&
+ test "$[]4" = conftest.ts1; then
+ at_ts_resolution=$at_try_res
+ break
+ fi
+done
+rm -f conftest.ts?
+
+AS_ECHO(["$at_srcdir/AT_LINE: using ${at_ts_resolution}s as timestamp resolution
+"]) >&AS_MESSAGE_LOG_FD
])
# ----------------------
# Check FILE-1 and FILE-2 for equality, like 'cmp FILE-1 FILE-2'.
m4_define([AT_CMP],
-[m4_ifval([$2],, [m4_fatal([AT_CMP takes two arguments.])])[]dnl
-AT_CHECK([$at_diff "$1" "$2"])
-])# AT_CMP
-
+[m4_ifval([$2],, [m4_fatal([AT_CMP takes two arguments.])])]dnl
+[AT_CHECK([$at_diff "$1" "$2"])])
+
+# AT_MTIME_DELAY
+# --------------
+# Wait for a short time, to ensure that files created before this
+# command are considered to be older than files created afterward.
+m4_define([AT_MTIME_DELAY],
+[sleep $at_ts_resolution])
## ---------------- ##
## Testing syntax. ##
end-language: "Autoconf-without-aclocal-m4"
]])
-# Delay to make sure the cache files generated by the next command are
-# considered newer than configure.ac.
-sleep 2
+AT_MTIME_DELAY
AT_CHECK_AUTOCONF([], 1, [],
[[trailer.m4: warning: AC_INIT was never used
configure.ac:6: error: possibly undefined macro: d@&t@nl
]])
-# Delay to make sure the cache files generated by the previous command
-# are considered to have been created in the past.
-sleep 2
+AT_MTIME_DELAY
# A second run (without --force) should succeed and yield only the
# warnings about AC_INIT and AC_OUTPUT.
AT_CHECK_AUTOCONF
cp configure configure.1
-sleep 1
+AT_MTIME_DELAY
printf '%s\n' 'AC_LIBSOURCES([foo.c])dn@&t@l' >> configure.ac
# This step must not use --force.
AT_CHECK([test configure -nt configure.1])
cp configure configure.2
-sleep 1
+AT_MTIME_DELAY
printf '%s\n' \
'AC_DEFUN([unused_MACRO], [# bob was there too' \
'])' >> aclocal.m4
# -----------------------------
AT_SETUP([autom4te preselections])
-: ${sleep='sleep 1'} # Command to force different timestamps.
-# If this test should run on FAT file systems and older w32,
-# then setting $sleep correctly needs to be revisited.
-
# We use aclocal and automake. Skip broken automake wrappers.
AT_CHECK([automake --version || exit 77], [], [stdout], [ignore])
AT_CHECK([[grep '[1-9]\.[0-9]' stdout || exit 77]], [], [ignore])
[[AUTOMAKE_OPTIONS = foreign
]])
-$sleep # 'aclocal.m4' should be strictly younger than its inputs
+AT_MTIME_DELAY
# If Autoconf is too old, or the user has turned caching off, skip:
AT_CHECK([$ACLOCAL || { ret=$?; test $ret -eq 63 && ret=77; exit $ret; }],
# comparing the old and new requests is a good place to start debugging:
sort autom4te.cache/requests >old-requests
echo newer >newer
-$sleep # if 'configure' is regenerated, we want it to be strictly newer,
- # to catch the error consistently.
+# if 'configure' is regenerated, we want it to be strictly newer,
+# to catch the error consistently.
+AT_MTIME_DELAY
AT_CHECK([$ACLOCAL], [],[], [ignore])
AT_CHECK([automake --no-force --add-missing], [], [], [ignore])
AT_CHECK([autoconf])
'])
# Cannot use AT_CHECK here, autotest internals could be messed up.
-
+# This sleep is not because of file timestamps, it's to make the
+# locks conflict.
(echo AC_INIT; sleep 2; echo) \
| (autom4te --language=autoconf -o configure -; echo $? >&2 ) 2>errlog &
AT_CHECK([echo AC_INIT | autom4te --language=autoconf -o configure -])