bug#68808: subsecond mtime discovery code insufficient

Erik A Johnson <[email protected]> Mon, 27 May 2024 20:59:37 +0000
Newsgroups gmane.comp.sysutils.automake.bugs
Message-ID <[email protected]>
Thanks, Karl.  I wasn't sure what version was to be patched but it looks like the current git repository has those changes, so I tested that with the macOS make:
* fine on the tests that failed previously because of macOS default make having only second resolution
* color-tests2.sh and color-tests2-w.sh fail -- logs attached.

Erik



On May 27, 2024, at 12:34 PM, Karl Berry <[email protected]> wrote:

Hi Erik and all - I (finally) made the change below to have automake
test for a make that doesn't support subsecond mtimes even when the rest
of the system is ok, as you noted happens with the make-3.81 shipped by
macOS.

I installed make-3.8.1 on my Rocky 9 system, but it did not cause the
lossage. So I'm not sure if there's an error in my test (wouldn't
surprise me) or if it's something about the mac system specifically.

As far as I could tell, the new code does succeed on Rocky 9. So I hope
it also succeeds on other current systems where the whole
subsecond-mtime thing is intended.

Erik, can you either apply this patch (and remake and run the tests), or
get the current automake dev sources and try it?

If anyone (Zack, Bogdan, Jim, ...) else could look at, and/or try, the
new code and see if any problems show up, that would be great.

I hope this works and we can start pushing towards the release. --thanks, karl.

--- a/m4/sanity.m4
+++ b/m4/sanity.m4
@@ -24,7 +24,7 @@ AS_IF([sleep 0.001 2>/dev/null], [am_cv_sleep_fractional_seconds=true],
# 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.
+# granularity, so we don't check for anything finer than that. (See below.)
AC_DEFUN([_AM_FILESYSTEM_TIMESTAMP_RESOLUTION], [dnl
AC_REQUIRE([_AM_SLEEP_FRACTIONAL_SECONDS])
AC_CACHE_CHECK([filesystem timestamp resolution], am_cv_filesystem_timestamp_resolution, [dnl
@@ -34,6 +34,9 @@ am_cv_filesystem_timestamp_resolution=2
# Only try to go finer than 1s if sleep can do it.
am_try_resolutions=1
if $am_cv_sleep_fractional_seconds; then
+  # Even a millisecond often causes a bunch of false positives,
+  # so just try a hundredth of a second. The time saved between .001 and
+  # .01 is not terribly consequential.
  am_try_resolutions="0.01 0.1 $am_try_resolutions"
fi

@@ -48,12 +51,13 @@ rm -f conftest.ts?
: > conftest.ts3

# Make sure ls -t actually works.  Do 'set' in a subshell so we don't
-# clobber the current shell's arguments.
+# clobber the current shell's arguments. (Outer-level square brackets
+# are for m4; be careful, it's easy to get confused.)
if (
     set X `[ls -t conftest.ts[12]]` &&
     {
-       test "$[*]" != "X conftest.ts1 conftest.ts2" ||
-       test "$[*]" != "X conftest.ts2 conftest.ts1";
+       test "$[]*" != "X conftest.ts1 conftest.ts2" ||
+       test "$[]*" != "X conftest.ts2 conftest.ts1";
     }
); then :; else
  # If neither matched, then we have a broken ls.  This can happen
@@ -64,7 +68,7 @@ if (
    ["Bad output from ls -t: \"`[ls -t conftest.ts[12]]`\""],
    [AS_MESSAGE_LOG_FD])
  AC_MSG_FAILURE([ls -t produces unexpected output.
-Make sure there is not a broken alias in your environment.])
+Make sure there is not a broken ls alias in your environment.])
fi

for am_try_res in $am_try_resolutions; do
@@ -84,11 +88,47 @@ for am_try_res in $am_try_resolutions; do
      test "$[]2" = conftest.ts3 &&
      test "$[]3" = conftest.ts2 &&
      test "$[]4" = conftest.ts1); then
-    am_cv_filesystem_timestamp_resolution=$am_try_res
-    break
+    #
+    # Ok, ls -t worked. We have one more thing to check: make.
+    # It can happen that everything else supports the subsecond mtimes,
+    # but make doesn't, notably on macOS, which ships make 3.81 from
+    # 2006 (the last one released under GPLv2). https://urldefense.com/v3/__https://bugs.gnu.org/68808__;!!LIr3w8kk_Xxm!rq87CiefTKIMZV02HfASxRvKLnpaw6kZk70cv22sBPSJkGDk112T-JXYGUYvKK_BgnEp19HKx-M5RVFS$
+    #
+    # So, first let's create a Makefile:
+    rm -f conftest.mk
+    echo 'conftest.ts1: conftest.ts2' >conftest.mk
+    echo ' touch conftest.ts2' >>conftest.mk
+    #
+    # Now, running
+    #   touch conftest.ts1; touch conftest.ts2; make
+    # should touch ts1 because ts2 is newer. This could happen by luck,
+    # but most often, it will fail if make's support is insufficient. So
+    # test for several consecutive successes.
+    #
+    # (We reuse conftest.ts[12] because we still want to modify existing
+    # files, not create new ones, per above.)
+    n=0
+    make_ok=true
+    until test $n -eq 4; do
+      echo one > conftest.ts1
+      sleep $am_try_res
+      echo two > conftest.ts2 # ts2 should now be newer than ts1
+      if make -f conftest.mk | grep 'up to date' >/dev/null; then
+        make_ok=false
+        break # out of $n loop
+      fi
+      n=`expr $n + 1`
+    done
+    if $make_ok; then
+      # Everything we know to check worked out, so call this resolution good.
+      am_cv_filesystem_timestamp_resolution=$am_try_res
+      break # out of resolution loop
+    fi
+    # Otherwise, we'll go on to check the next resolution.
  fi
done
rm -f conftest.ts?
+# (end _am_filesystem_timestamp_resolution)
])])

# AM_SANITY_CHECK

compile finished at Mon May 27 10:01:57 2024
color-tests2.log (application/octet-stream, 2.8 KB)
am-test-lib.sh: Running from installcheck: no
am-test-lib.sh: Test Protocol: none
am-test-lib.sh: PATH = /tmp/johnsone/automake_git/automake/bin:/tmp/johnsone/automake_git/automake/t/ax:.:/Users/johnsone/bin/osx:/Users/johnsone/bin:/Library/Frameworks/Python.framework/Versions/3.12/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin:/Library/TeX/texbin:/Applications/VMware Fusion.app/Contents/Public
++ pwd
/tmp/johnsone/automake_git/automake/t/color-tests2.dir
+ red='\[0;31m'
+ grn='\[0;32m'
+ lgn='\[1;32m'
+ blu='\[1;34m'
+ mgn='\[0;35m'
+ std='\[m'
+ set +e
+ expect -c 'exit 77'
+ test 77 -eq 77
+ cat
+ cat
+ MAKE=make
+ expect -f expect-check
spawn make
stdout is a tty
+ rm -f expect-check Makefile
+ cat
+ cat
+ cat
+ cat
+ cat
+ cat
+ cp fail xfail
+ cp pass xpass
+ chmod +x pass fail skip xpass xfail error
+ aclocal-1.16 -Werror
+ autoconf
+ automake-1.16 --foreign -Werror -Wall --add-missing
parallel-tests: installing './test-driver'
+ cat
+ for vpath in false :
+ false
+ srcdir=.
+ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether sleep supports fractional seconds... true
checking filesystem timestamp resolution... 1
checking whether build environment is sane... yes
checking for a race-free mkdir -p... mkdir -p
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking xargs -n works... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
+ our_make TERM=ansi
+ set MAKE=make TERM=ansi
+ env MAKE=make TERM=ansi expect -f ./expect-make
+ cat stdout
spawn make -e check
make  pass fail skip xpass xfail error
make[1]: Nothing to be done for `pass'.
make[1]: Nothing to be done for `fail'.
make[1]: Nothing to be done for `skip'.
make[1]: Nothing to be done for `xpass'.
make[1]: Nothing to be done for `xfail'.
make[1]: Nothing to be done for `error'.
make  check-TESTS
PASS: pass
+ test_color
+ cat stdout
+ grep '^\[0;32mPASS\[m: .*pass'
PASS: pass
+ cat stdout
+ grep '^\[0;31mFAIL\[m: .*fail'
+ am_exit_trap 1
+ exit_status=1
+ set +e
+ cd /tmp/johnsone/automake_git/automake
+ test none = tap
+ case $am_explicit_skips in
+ test 1 -eq 0
+ keep_testdirs=yes
+ am_keeping_testdirs
+ case $keep_testdirs in
+ return 0
+ set +x
FAIL t/color-tests2.sh (exit status: 1)
color-tests2-w.log (application/octet-stream, 2.8 KB)
./t/color-tests2-w.sh: will source ./t/color-tests2.sh
am-test-lib.sh: Running from installcheck: no
am-test-lib.sh: Test Protocol: none
am-test-lib.sh: PATH = /tmp/johnsone/automake_git/automake/bin:/tmp/johnsone/automake_git/automake/t/ax:.:/Users/johnsone/bin/osx:/Users/johnsone/bin:/Library/Frameworks/Python.framework/Versions/3.12/bin:/Library/Frameworks/Python.framework/Versions/2.7/bin:/usr/local/bin:/System/Cryptexes/App/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/local/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/bin:/var/run/com.apple.security.cryptexd/codex.system/bootstrap/usr/appleinternal/bin:/opt/X11/bin:/Library/Apple/usr/bin:/Library/TeX/texbin:/Applications/VMware Fusion.app/Contents/Public
+++ pwd
/tmp/johnsone/automake_git/automake/t/color-tests2-w.dir
++ red='\[0;31m'
++ grn='\[0;32m'
++ lgn='\[1;32m'
++ blu='\[1;34m'
++ mgn='\[0;35m'
++ std='\[m'
++ set +e
++ expect -c 'exit 77'
++ test 77 -eq 77
++ cat
++ cat
++ MAKE=make
++ expect -f expect-check
spawn make
stdout is a tty
++ rm -f expect-check Makefile
++ cat
++ cat
++ cat
++ cat
++ cat
++ cat
++ cp fail xfail
++ cp pass xpass
++ chmod +x pass fail skip xpass xfail error
++ aclocal-1.16 -Werror
++ autoconf
++ automake-1.16 --foreign -Werror -Wall --add-missing
++ cat
++ for vpath in false :
++ false
++ srcdir=.
++ ./configure
checking for a BSD-compatible install... /usr/bin/install -c
checking whether sleep supports fractional seconds... true
checking filesystem timestamp resolution... 1
checking whether build environment is sane... yes
checking for a race-free mkdir -p... mkdir -p
checking for gawk... no
checking for mawk... no
checking for nawk... no
checking for awk... awk
checking whether make sets $(MAKE)... yes
checking whether make supports nested variables... yes
checking xargs -n works... yes
checking that generated files are newer than configure... done
configure: creating ./config.status
config.status: creating Makefile
++ our_make TERM=ansi
++ set MAKE=make TERM=ansi
++ env MAKE=make TERM=ansi expect -f ./expect-make
++ cat stdout
spawn make -e check
make  pass fail skip xpass xfail error
make[1]: Nothing to be done for `pass'.
make[1]: Nothing to be done for `fail'.
make[1]: Nothing to be done for `skip'.
make[1]: Nothing to be done for `xpass'.
make[1]: Nothing to be done for `xfail'.
make[1]: Nothing to be done for `error'.
make  check-TESTS
PASS: pass
++ test_color
++ cat stdout
++ grep '^\[0;32mPASS\[m: .*pass'
PASS: pass
++ cat stdout
++ grep '^\[0;31mFAIL\[m: .*fail'
+ am_exit_trap 1
+ exit_status=1
+ set +e
+ cd /tmp/johnsone/automake_git/automake
+ test none = tap
+ case $am_explicit_skips in
+ test 1 -eq 0
+ keep_testdirs=yes
+ am_keeping_testdirs
+ case $keep_testdirs in
+ return 0
+ set +x
FAIL t/color-tests2-w.sh (exit status: 1)