Christian Franke wrote:
A small step towards reproducible packaging...

Currently only tested with upcoming smartmontools package - contains only exe, man, doc files (no dll, lib, ...). Multiple cygport runs produce binary identical distribution tarballs if SOURCE_DATE_EPOCH (from the past) is specified in the cygport file.


A slightly enhanced patch is attached. For .gz files, 'gzip -n' is now used as suggested.


Result from a test with the cygwin-3.4.8-1 source package:

These two lines were added to cygwin.cygport:

 VERSION="3.4.8"
 RELEASE="1"
+export SOURCE_DATE_EPOCH=$(date -d '2023-08-18 UTC' +%s)
+export TZ=UTC

(TZ=UTC is a workaround for a minor bug in SOURCE_DATE_EPOCH handling of gropdf)

These distribution files are always identical:
- cygwin-3.4.8-1-src.tar.xz
- cygwin-doc-3.4.8-1.tar.xz

These individual files differ if the build-path of multiple test-builds is always the same:

- usr/bin/cygwin1.dll:
-- Contains a build timestamp not based on '__DATE__ __TIME__'. Could be easily addressed in scripts/mkvers.sh. -- The PE Export Tables header contains a timestamp independent from the PE header. There is possibly no easy way to fix this without an extra tool (or further enhancing peflags).

- usr/lib/lib*.a:
-- Object file time timestamps differ. Could be addressed by adding 'ar' option 'D' (deterministic) in Makefiles and scripts/speclib.

- usr/lib/debug/usr/bin/cygwin1.dll.dbg: As expected, because cygwin1.dll differ.

If the build-path changes, more files differ (cygcheck.exe, ldd.exe, cygserver.exe) because __FILE__ is used and expands to an absolute path name.

--
Regards,
Christian

From 73dde4d2dabb74b7b9ee40655204f84e1d4086d6 Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Mon, 28 Aug 2023 16:24:36 +0200
Subject: [PATCH] Add initial support for SOURCE_DATE_EPOCH

If specified, set the header timestamps of executables and
patch files to SOURCE_DATE_EPOCH.
Suppress the header timestamp of .gz files.
Instruct tar to avoid more recent modification times and
to sort all entries by name.
---
 bin/cygport.in           | 17 +++++++++++++++--
 lib/pkg_pkg.cygpart      | 20 ++++++++++++++++++--
 lib/src_postinst.cygpart | 22 +++++++++++++++++++---
 3 files changed, 52 insertions(+), 7 deletions(-)

diff --git a/bin/cygport.in b/bin/cygport.in
index 3f89ac67..e2fe1785 100755
--- a/bin/cygport.in
+++ b/bin/cygport.in
@@ -231,8 +231,9 @@ source ${_privlibdir}/check_funcs.cygpart
 ###
 
 # check now for all mandatory programs
-for _myprog in bzip2 cat chmod cp diff diffstat dos2unix file find gawk grep 
gzip \
-               install ln mkdir mv patch rm rsync sed sort tar xargs which xz
+for _myprog in bzip2 cat chmod cp date diff diffstat dos2unix file find gawk 
grep \
+               gzip install ln mkdir mv patch rm rsync sed sort tar touch 
which \
+               xargs xz
 do
        if ! check_prog ${_myprog}
        then
@@ -490,6 +491,18 @@ do
 done
 unset restrict
 
+if [ "${SOURCE_DATE_EPOCH+y}" = "y" ]
+then
+       if [ -n "$(echo "${SOURCE_DATE_EPOCH}" | sed -e 's/^$/X/' -e 
's/[0-9]//g')" ]
+       then
+               error "Malformed SOURCE_DATE_EPOCH: '${SOURCE_DATE_EPOCH}'"
+       fi
+       case $(peflags --version 2>/dev/null | sed -n '1s/^.* //p') in
+               4.6.[6-9]|4.[7-9]*|[5-9]*) ;;
+               *) error "SOURCE_DATE_EPOCH requires peflags 4.6.6 or later"
+       esac
+fi
+
 
 
################################################################################
 #
diff --git a/lib/pkg_pkg.cygpart b/lib/pkg_pkg.cygpart
index 2a2bb663..4e6a7cd2 100644
--- a/lib/pkg_pkg.cygpart
+++ b/lib/pkg_pkg.cygpart
@@ -42,7 +42,7 @@ TAR_COMPRESSION_EXT="${TAR_COMPRESSION_EXT:-xz}"
 #****
 
 __tar() {
-       local TAR_COMPRESSION_OPT;
+       local TAR_COMPRESSION_OPT TAR_SOURCE_DATE_OPTS;
 
        # We could use --auto-compress, but this also constrains the extension
        # to the currently valid set. We could probe if tar supports the
@@ -65,7 +65,14 @@ __tar() {
                        error "tar option for 
TAR_COMPRESSION_EXT='${TAR_COMPRESSION_EXT}' unknown"
                        ;;
        esac
-       tar ${TAR_COMPRESSION_OPT} --owner=Guest:501 --group=None:513 -cvf "$@"
+
+       if [ -n "${SOURCE_DATE_EPOCH}" ]
+       then
+               # Ensure reproducible sort order and last modification times <= 
SOURCE_DATE_EPOCH
+               TAR_SOURCE_DATE_OPTS="--sort=name --mtime=@${SOURCE_DATE_EPOCH} 
--clamp-mtime"
+       fi
+
+       tar ${TAR_COMPRESSION_OPT} ${TAR_SOURCE_DATE_OPTS} --owner=Guest:501 
--group=None:513 -cvf "$@"
 }
 
 __pkg_binpkg() {
@@ -319,6 +326,7 @@ __pkg_diff() {
        local difflevel;
        local exclude;
        local optional_patchfiles;
+       local source_date;
 
        default_excludes="CYGWIN-PATCHES aclocal.m4~ aclocal.m4t autom4te.cache
                config.cache config.guess config.log config.status config.sub
@@ -498,6 +506,14 @@ __pkg_diff() {
 
        sed -b -e '/^diff -u/d' -i ${optional_patchfiles} 
${patchdir}/${src_patchfile};
 
+       if [ -n "${SOURCE_DATE_EPOCH}" ]
+       then
+               # Ensure that the timestamp comment of the new file is 
reproducible
+               source_date=$(date -d @"${SOURCE_DATE_EPOCH}" -u +'%Y-%m-%d 
%H:%M:%S.000000000 +0000')
+               sed -b -e  "s|^\(+++ [^\t]*\t\).*\$|\1${source_date}|" \
+                       -i ${optional_patchfiles} ${patchdir}/${src_patchfile}
+       fi
+
        diffstat -p${difflevel} ${optional_patchfiles} 
${patchdir}/${src_patchfile};
 }
 
diff --git a/lib/src_postinst.cygpart b/lib/src_postinst.cygpart
index dd947311..53eaa71a 100644
--- a/lib/src_postinst.cygpart
+++ b/lib/src_postinst.cygpart
@@ -41,7 +41,7 @@ __prep_fonts_dir() {
        for catalogue in ${D}${cataloguedir}/*
        do
                fontdir=$(readlink -f ${catalogue})
-               find ${D}${fontdir} -name '*.pcf' -exec gzip -q '{}' +
+               find ${D}${fontdir} -name '*.pcf' -exec gzip -q 
${SOURCE_DATE_EPOCH:+-n} '{}' +
                mkfontscale ${D}${fontdir}
                mkfontdir ${D}${fontdir}
        done
@@ -775,7 +775,7 @@ __prepman() {
                while read -d $'\0' manpage
                do
                        echo "        ${manpage##*/}";
-                       gzip -q "${manpage}";
+                       gzip -q ${SOURCE_DATE_EPOCH:+-n} "${manpage}";
                done
        fi
 }
@@ -819,7 +819,7 @@ __prepinfo() {
                while read -d $'\0' infopage
                do
                        echo "        ${infopage##*/}";
-                       gzip -q "${infopage}";
+                       gzip -q ${SOURCE_DATE_EPOCH:+-n} "${infopage}";
                done
        fi
 }
@@ -989,6 +989,12 @@ __prepstrip_one() {
 
        objdump=${objcopy/copy/dump}
 
+       if [ -n "${SOURCE_DATE_EPOCH}" ]
+       then
+               # Let objcopy preserve the timestamps
+               objcopy+=" --enable-deterministic-archives --preserve-dates"
+       fi
+
        # Static libraries should not be fully stripped, but we can
        # still provide split debuginfo if desired
        case "${exe}" in
@@ -1074,6 +1080,16 @@ __prepstrip_one() {
        # keep sticky bit if present
        chmod u+w,a+x "${exe}";
 
+       if [ -n "${SOURCE_DATE_EPOCH}" ]
+       then
+               case "${exe}" in
+               *.exe|*.dll|*.so|*.so.*|*.oct|*.mex|*.cmxs)
+                       # Ensure PE header timestamp is reproducible and 
checksum is correct
+                       # objcopy later inherits the timestamp to debug info 
and stripped file
+                       peflags --checksum=1 --timestamp=${SOURCE_DATE_EPOCH} 
${exe} >/dev/null ;;
+               esac
+       fi
+
        if defined _CYGPORT_RESTRICT_debuginfo_
        then
                ${objcopy} --strip-all "${exe}";
-- 
2.39.0

Reply via email to