diff --git a/Makefile.inc1 b/Makefile.inc1 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -603,6 +603,12 @@ SOURCE_DATE_EPOCH= ${PKG_TIMESTAMP} .endif +# ZFS sets uarch on a number of files. UFS does not. +MTREE_DIST= mtree -c -i -p . -R flags +.if ${MK_REPRODUCIBLE_BUILD} == "yes" +MTREE_DIST+= -R time | awk 'BEGIN { print "/set time=${SOURCE_DATE_EPOCH}.000000000" }; { print }' +.endif + PKG_NAME_PREFIX?= FreeBSD PKG_MAINTAINER?= re@FreeBSD.org PKG_WWW?= https://www.FreeBSD.org @@ -1569,7 +1575,8 @@ ${XZ_CMD} > ${PACKAGEDIR}/${dist}.txz .else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ - ${TAR_CMD} cvf - --exclude usr/lib/debug . | \ + ${MTREE_DIST} > ${WORLDTMP}/package-${dist}.mtree; \ + ${TAR_CMD} cvf - --exclude '*.bak' --exclude '*.old' --exclude usr/lib/debug @${WORLDTMP}/package-${dist}.mtree | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}.txz .endif .endfor @@ -1581,7 +1588,7 @@ ${XZ_CMD} > ${PACKAGEDIR}/${dist}-dbg.txz . else ${_+_}cd ${DESTDIR}/${DISTDIR}/${dist}; \ - ${TAR_CMD} cvLf - usr/lib/debug | \ + ${TAR_CMD} cvLf - --include usr/lib/debug @${WORLDTMP}/package-${dist}.mtree | \ ${XZ_CMD} > ${PACKAGEDIR}/${dist}-dbg.txz . endif .endfor @@ -1974,22 +1981,24 @@ .else .if !defined(NO_INSTALLKERNEL) cd ${DESTDIR}/${DISTDIR}/kernel; \ - ${TAR_CMD} cvf - --exclude '*.debug' . | \ + ${MTREE_DIST} > ${WORLDTMP}/package-kernel.mtree; \ + ${TAR_CMD} cvf - --exclude '*.debug' @${WORLDTMP}/package-kernel.mtree | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.txz .endif .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel; \ - ${TAR_CMD} cvf - --include '*/*/*.debug' $$(eval find .) | \ + ${TAR_CMD} cvf - --include '*/*/*.debug' @${WORLDTMP}/package-kernel.mtree | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel-dbg.txz .endif .if ${BUILDKERNELS:[#]} > 1 && ${NO_INSTALLEXTRAKERNELS} != "yes" .for _kernel in ${BUILDKERNELS:[2..-1]} cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ - ${TAR_CMD} cvf - --exclude '*.debug' . | \ + ${MTREE_DIST} > ${WORLDTMP}/package-kernel.${_kernel}.mtree; \ + ${TAR_CMD} cvf - --exclude '*.debug' @${WORLDTMP}/package-kernel.${_kernel}.mtree | \ ${XZ_CMD} > ${PACKAGEDIR}/kernel.${_kernel}.txz .if ${MK_DEBUG_FILES} != "no" cd ${DESTDIR}/${DISTDIR}/kernel.${_kernel}; \ - ${TAR_CMD} cvf - --include '*/*/*.debug' $$(eval find .) | \ + ${TAR_CMD} cvf - --include '*/*/*.debug' @${WORLDTMP}/package-kernel.${_kernel}.mtree | \ ${XZ_CMD} > ${DESTDIR}/${DISTDIR}/kernel.${_kernel}-dbg.txz .endif .endfor diff --git a/release/scripts/reproducible.sh b/release/scripts/reproducible.sh new file mode 100644 --- /dev/null +++ b/release/scripts/reproducible.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# +# sh reproducible.sh /tmp/norepro REVISION -DWITHOUT_REPRODUCIBLE_BUILD +# sh reproducible.sh /tmp/repro1 REVISION -DWITH_REPRODUCIBLE_BUILD +# sh reproducible.sh /tmp/repro2 REVISION -DWITH_REPRODUCIBLE_BUILD +# comm -1 -2 /tmp/repro1 /tmp/norepro +# comm -1 -2 /tmp/repro1 /tmp/repro2 + +set -eu + +SRC_ROOT=/tmp/repro-freebsd +RELEASE_ROOT=${SRC_ROOT}/release +TIMESTAMP=1733729386 + +make="env -i SRCCONF=/dev/null __MAKE_CONF=/dev/null make -s" + +main() { + local dest commit args + parse_args "${@}" + + local objdir relobjdir + setup_src + + chflags -R noschg ${objdir} + rm -rf ${objdir} + + ${make} -C ${SRC_ROOT} -j $(sysctl -n hw.ncpu) ${args} buildworld buildkernel + ${make} -C ${RELEASE_ROOT} -DNOPORTS -DNOSRC ${args} PKG_TIMESTAMP=${TIMESTAMP} packagesystem + cp ${relobjdir}/MANIFEST ${dest} +} + +parse_args() { + if [ "${#}" -lt 3 ]; then + cat << EOF 1>&2 +Usage: + sh reproducible.sh /tmp/norepro REVISION -DWITHOUT_REPRODUCIBLE_BUILD + sh reproducible.sh /tmp/repro1 REVISION -DWITH_REPRODUCIBLE_BUILD + sh reproducible.sh /tmp/repro2 REVISION -DWITH_REPRODUCIBLE_BUILD + comm -1 -2 /tmp/repro1 /tmp/norepro + comm -1 -2 /tmp/repro1 /tmp/repro2 +EOF + + exit 1 + fi + + dest=${1}; shift + commit=${1}; shift + args="${@}" + # check dest dir + local d + d=$(dirname ${dest}) + realpath ${d} > /dev/null +} + +setup_src() { + rm -rf ${SRC_ROOT} + git clone $(git rev-parse --show-toplevel) ${SRC_ROOT} + git -C ${SRC_ROOT} checkout ${commit} + + # check RELEASE_ROOT first because it creates the objdir, + # which changes obdir for SRC_ROOT + relobjdir=$(make -C ${RELEASE_ROOT} -V .OBJDIR) + objdir=$(make -C ${SRC_ROOT} -V .OBJDIR) +} + +main "${@}" diff --git a/share/man/man7/release.7 b/share/man/man7/release.7 --- a/share/man/man7/release.7 +++ b/share/man/man7/release.7 @@ -283,6 +283,17 @@ .Fa /usr/ports is expected to exist by alternative means. .El +.Sh REPRODUCIBLE BUILDS (EXPERIMENTAL) +Different builders can produce bit-identical release tarballs using +PKG_TIMESTAMP and REVISION. + +git checkout + +make -DWITH_REPRODUCIBLE_BUILD buildworld buildkernel + +make -DWITH_REPRODUCIBLE_BUILD PKG_TIMESTAMP= packagesystem + +Note: make packagesystem must be run without -j. .Sh EMBEDDED BUILDS The following .Fa release.conf diff --git a/sys/conf/newvers.sh b/sys/conf/newvers.sh --- a/sys/conf/newvers.sh +++ b/sys/conf/newvers.sh @@ -255,7 +255,7 @@ fi if [ -n "$git_cmd" ] ; then - git=$($git_cmd rev-parse --verify --short HEAD 2>/dev/null) + git=$($git_cmd rev-parse --verify --short=12 HEAD 2>/dev/null) if [ "$($git_cmd rev-parse --is-shallow-repository)" = false ] ; then git_cnt=$($git_cmd rev-list --first-parent --count HEAD 2>/dev/null) if [ -n "$git_cnt" ] ; then