]> git.ipfire.org Git - thirdparty/automake.git/commitdiff
mdate-sh: support $SOURCE_DATE_EPOCH.
authorKarl Berry <karl@freefriends.org>
Tue, 20 May 2025 16:17:04 +0000 (09:17 -0700)
committerKarl Berry <karl@freefriends.org>
Tue, 20 May 2025 16:17:04 +0000 (09:17 -0700)
From https://bugs.gnu.org/77805.

* lib/mdate-sh: if $SOURCE_DATE_EPOCH is set, use it
instead of the mtime of any file. Debian was removing
@value{UPDATED}, etc., from manuals because of varying output.
Try date --date, then date -r, then perl gmtime, then
fall back to the file's mtime.
Also, warn if more than one file argument is given.
(mon_to_month): factor out conversion of Jan to January.
* t/mdate5.sh: test it.
* doc/automake.texi (Texinfo): mention it.
* NEWS: mention it.

NEWS
doc/automake.texi
lib/mdate-sh
t/mdate5.sh

diff --git a/NEWS b/NEWS
index 30c0cd2c7bf5faf9386e2161ff63842b8b04e379..fdff666cb6a9c58728f100a8bc09490900fc71e4 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,9 @@ New in 1.17.92:
     the tar-v7 and other explicit options to force a particular tar
     format are unchanged and still override the default. (bug#74847)
 
+  - mdate-sh uses SOURCE_DATE_EPOCH, if set, instead of a file's mtime.
+    (bug#77805)
+
   - New option dist-bzip3 for bzip3 compression of distributions. (bug#73795)
 
   - New option --stderr-prefix for tap-driver.sh, to prefix each line of
@@ -34,7 +37,7 @@ New in 1.17.92:
     is set to "false", do nothing but exit unsuccessfully, also to match
     previous behavior. (bug#74434)
 
-  - The no-dist-built-sources Automake option hopefully now operates as
+  - The no-dist-built-sources Automake option now operates (hopefully) as
     intended, i.e., omits the dependency on $(BUILT_SOURCES) for the
     distdir target. (bug#69908)
 
@@ -42,7 +45,7 @@ New in 1.17.92:
     error. (bug#19964)
 
   - The compile script is more robust to Windows configurations;
-    specifically, avoiding double-path translation on MSYS. (bug#75939)
+    specifically, avoids double-path translation on MSYS. (bug#75939)
 
   - The test infrastructure sets the CONFIG_SITE environment variable to
     /dev/null, to avoid the local system's Autoconf site defaults from
index 3cbbff2504341cb99737d79e3f00f8f3b8065947..608c0a8d60ef3d7510064aff8012f08d693d284a 100644 (file)
@@ -8327,7 +8327,7 @@ All of these targets can be extended using @samp{-local} rules
 @cindex @code{EDITION} Texinfo flag
 @cindex @code{UPDATED-MONTH} Texinfo flag
 
-@cindex @file{mdate-sh}
+@cindex @file{mdate-sh}, Automake helper script
 
 If a @file{.texi} file @code{@@include}s @file{version.texi} (actually
 any file named @file{vers...texi}, then that file will be
@@ -8336,18 +8336,20 @@ Texinfo flags you can reference using @code{@@value@{EDITION@}},
 @code{@@value@{VERSION@}}, @code{@@value@{UPDATED@}}, and
 @code{@@value@{UPDATED-MONTH@}}.
 
+@vindex SOURCE_DATE_EPOCH@r{, and @command{mdate-sh}}
 @table @code
 @item EDITION
 @itemx VERSION
 Both of these flags hold the version number of your program.  They are
-kept separate for clarity.
+kept separate for historical reasons.
 
 @item UPDATED
-This holds the date the primary @file{.texi} file was last modified.
+This holds the date the primary @file{.texi} file was last modified,
+or the date of the @env{SOURCE_DATE_EPOCH} variable (see below).
 
 @item UPDATED-MONTH
 This holds the name of the month in which the primary @file{.texi} file
-was last modified.
+was last modified, or the month of @env{SOURCE_DATE_EPOCH}.
 @end table
 
 The @file{version.texi} support requires the @command{mdate-sh}
@@ -8355,6 +8357,17 @@ script; this script is supplied with Automake and automatically
 included when @command{automake} is invoked with the
 @option{--add-missing} option.
 
+@cindex reproducible builds
+@cindex epoch, and reproducible builds
+If the @env{SOURCE_DATE_EPOCH} environment variable is set,
+@command{mdate-sh} uses its value, instead of the modification time of
+any file.  This variable is typically set globally for the sake of
+creating a reproducible build across a distribution.  Its value is an
+integer, the number of seconds since the Unix epoch (January 1, 1970,
+00:00:00 UTC).  If for any reason the @env{SOURCE_DATE_EPOCH} value
+cannot be converted into the human-readable date strings,
+@command{mdate-sh} falls back to using the given file's mtime.
+
 If you have multiple Texinfo files, and you want to use the
 @file{version.texi} feature, then you have to have a separate version
 file for each Texinfo file.  Automake will treat any include in a
index 51fb44bcfd006d68fa07f37b033fe2fb01fe1161..9c195dbd06f95621d90199babf9dd751f7505713 100755 (executable)
@@ -1,7 +1,8 @@
 #!/bin/sh
-# Get modification time of a file or directory and pretty-print it.
+# Get modification time of a file or directory, or value of
+# $SOURCE_DATE_EPOCH, and pretty-print it, formatted like 1 January 2000.
 
-scriptversion=2024-12-03.03; # UTC
+scriptversion=2025-05-21.01; # UTC
 
 # Copyright (C) 1995-2025 Free Software Foundation, Inc.
 # written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, June 1995
@@ -49,6 +50,10 @@ Usage: mdate-sh [--help] [--version] FILE
 Pretty-print the modification day of FILE, in the format:
 1 January 1970
 
+If the environment variable SOURCE_DATE_EPOCH is set, use its value (in
+epoch-seconds) for the date instead of any FILE mtime.  The FILE
+argument is still required in this case, but ignored.
+
 Report bugs to <bug-automake@gnu.org>.
 GNU Automake home page: <https://www.gnu.org/software/automake/>.
 General help using GNU software: <https://www.gnu.org/gethelp/>.
@@ -61,12 +66,35 @@ EOF
     ;;
 esac
 
+# Warn if more than one file given.
+if test $# -ne 1; then
+  echo "$0: warning: multiple files given, using first: $*" >&2
+fi
+
 error ()
 {
   echo "$0: $1" >&2
   exit 1
 }
 
+# set $month ("January") and $nummonth (1) given arg MON ("Jan").
+mon_to_month ()
+{
+  case $1 in
+    Jan) month=January; nummonth=1;;
+    Feb) month=February; nummonth=2;;
+    Mar) month=March; nummonth=3;;
+    Apr) month=April; nummonth=4;;
+    May) month=May; nummonth=5;;
+    Jun) month=June; nummonth=6;;
+    Jul) month=July; nummonth=7;;
+    Aug) month=August; nummonth=8;;
+    Sep) month=September; nummonth=9;;
+    Oct) month=October; nummonth=10;;
+    Nov) month=November; nummonth=11;;
+    Dec) month=December; nummonth=12;;
+  esac
+}
 
 # Prevent date giving response in another language.
 LANG=C
@@ -80,6 +108,56 @@ export LC_TIME
 TZ=UTC0
 export TZ
 
+# \f
+# https://reproducible-builds.org/docs/source-date-epoch/
+if test -n "$SOURCE_DATE_EPOCH"; then
+  epoch_ok=true # be optimistic
+  date_fmt="+%d %B %Y"
+  result=`date -u --date="@$SOURCE_DATE_EPOCH" "$date_fmt" 2>/dev/null`
+  if test -z "$result"; then
+    result=`date -u -r "$SOURCE_DATE_EPOCH" "$date_fmt" 2>/dev/null`
+    if test -z "$result"; then
+      # The date command on Solaris 10 and 11 doesn't support any way
+      # to do this. Fall back to Perl.
+      #
+      perlout=`perl -e 'print scalar gmtime($SOURCE_DATE_EPOCH)' 2>/dev/null`
+      # Output is, e.g., Thu Jan  1 00:00:00 1970. Split it apart,
+      # since we need to convert "Jan" to "January".
+      # (We could use cut, but surely if a system has perl, it has awk?)
+      day=`echo $perlout | awk '{print $3}'`
+      mon=`echo $perlout | awk '{print $2}'`
+      mon_to_month $mon # sets $month
+      year=`echo $perlout | awk '{print $5}'`
+      result="$day $month $year"
+      #
+      if test -z "$result"; then
+        echo "$0: warning: SOURCE_DATE_EPOCH was set, but can't convert, ignoring: $SOURCE_DATE_EPOCH" >&2
+        epoch_ok=false
+      fi
+    fi
+  fi
+  #
+  if $epoch_ok; then
+    # Remove leading spaces and zeros. We don't want to get into the
+    # various date options to control this. (Not quoting $result here
+    # isn't important, just another way to omit leading spaces.)
+    result=`echo $result | sed 's/^[ 0]*//'`
+    if test -z "$result"; then
+      echo "$0: SOURCE_DATE_EPOCH was set, but converted to empty: $SOURCE_DATE_EPOCH" >&2
+      epoch_ok=false
+    fi
+  fi
+  if $epoch_ok; then
+    echo $result
+    exit 0
+  else
+    echo "$0: SOURCE_DATE_EPOCH failed, falling back to using mtime on: $1" >&2
+  fi
+fi
+# end of SOURCE_DATE_EPOCH support, rest is about the normal case of
+# using the mtime of the specified file.
+
+# \f
 # GNU ls changes its time format in response to the TIME_STYLE
 # variable.  Since we cannot assume 'unset' works, revert this
 # variable to its documented default.
@@ -125,20 +203,7 @@ do
   shift
   # Add another shift to the command.
   command="$command shift;"
-  case $1 in
-    Jan) month=January; nummonth=1;;
-    Feb) month=February; nummonth=2;;
-    Mar) month=March; nummonth=3;;
-    Apr) month=April; nummonth=4;;
-    May) month=May; nummonth=5;;
-    Jun) month=June; nummonth=6;;
-    Jul) month=July; nummonth=7;;
-    Aug) month=August; nummonth=8;;
-    Sep) month=September; nummonth=9;;
-    Oct) month=October; nummonth=10;;
-    Nov) month=November; nummonth=11;;
-    Dec) month=December; nummonth=12;;
-  esac
+  mon_to_month $1
 done
 
 test -n "$month" || error "failed parsing '$ls_command /' output"
@@ -152,7 +217,6 @@ eval $command
 # Because of the dummy argument above, month is in $2.
 #
 # On a POSIX system, we should have
-#
 # $# = 5
 # $1 = file size
 # $2 = month
@@ -161,7 +225,6 @@ eval $command
 # $5 = filename
 #
 # On Darwin 7.7.0 and 7.6.0, we have
-#
 # $# = 4
 # $1 = day
 # $2 = month
@@ -169,20 +232,7 @@ eval $command
 # $4 = filename
 
 # Get the month.
-case $2 in
-  Jan) month=January; nummonth=1;;
-  Feb) month=February; nummonth=2;;
-  Mar) month=March; nummonth=3;;
-  Apr) month=April; nummonth=4;;
-  May) month=May; nummonth=5;;
-  Jun) month=June; nummonth=6;;
-  Jul) month=July; nummonth=7;;
-  Aug) month=August; nummonth=8;;
-  Sep) month=September; nummonth=9;;
-  Oct) month=October; nummonth=10;;
-  Nov) month=November; nummonth=11;;
-  Dec) month=December; nummonth=12;;
-esac
+mon_to_month $2
 
 case $3 in
   ???*) day=$1;;
@@ -207,9 +257,9 @@ case $3 in
         Nov) nummonthtod=11;;
         Dec) nummonthtod=12;;
        esac
-       # For the first six month of the year the time notation can also
+       # For the first six months of the year the time notation can also
        # be used for files modified in the last year.
-       if (expr $nummonth \> $nummonthtod) > /dev/null;
+       if (expr $nummonth \> $nummonthtod) >/dev/null;
        then
         year=`expr $year - 1`
        fi;;
index 45270c039697b51a8766bac64adf30a4f054be97..11be50f13fbd521cade399ca63e4e1de68527653 100644 (file)
@@ -14,7 +14,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
-# Test to make sure mdate-sh works correctly.
+# Test that mdate-sh works correctly.
 
 am_create_testdir=empty
 . test-init.sh
@@ -42,4 +42,18 @@ if year=$(date +%Y) && test $year -gt 2010; then
   test $year = $3 || exit 1
 fi
 
+# \f
+# Also check that mdate-sh respects SOURCE_DATE_EPOCH.
+SOURCE_DATE_EPOCH=123456 # into January 2, 1970, for no particular reason.
+export SOURCE_DATE_EPOCH
+set x $(./mdate-sh mdate-sh)
+shift
+echo "$*" # For debugging.
+
+# Check that mdate output is the expected day (1 January 1970):
+test $# = 3
+test x$1 = x2
+test x$2 = xJanuary
+test x$3 = x1970
+
 :