Skip to content

Commit 2dcabb1

Browse files
bryan-minimalclaude
andcommitted
zola, tamarin-prover: fix reproducibility
Both were flagged `unknown` by a build-twice audit of the 87 packages added since the last fleet run. They turned out to be completely different problems. zola — the reproducibility guide was applied halfway. The recipe already cites minimal-repro's guide and strips build paths, but never pinned codegen. rustc's default release build shards codegen across parallel units that finish in thread-completion order, so functions are EMITTED in a different order each build. Measured on 0.22.1: 16.89% of bytes differed while the total size stayed identical, and 69% of differing windows had a byte-exact twin elsewhere in the other build — a size-preserving permutation, i.e. ordering, not codegen variance. Adds `-C codegen-units=1` (ordering) and `-C symbol-mangling-version=v0` (legacy mangling embeds a per-session hash). Same fix as nushell and difftastic. tamarin-prover — not GHC, and not the toolchain. Its version banner embeds the wall-clock compile time via a TemplateHaskell splice that calls getCurrentTime while compiling; it asks the clock directly, so SOURCE_DATE_EPOCH never reaches it. Exactly 26 bytes of a 135 MB binary differ, and the Haskell codegen is otherwise bit-identical — the GHC determinism work is holding fine. Rewrites the splice to a fixed instant derived from SOURCE_DATE_EPOCH, locating the source file by content so an upstream file move fails loudly instead of silently reverting to a wall clock. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent defad31 commit 2dcabb1

2 files changed

Lines changed: 44 additions & 3 deletions

File tree

packages/tamarin-prover/build.sh

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,36 @@ grep -v '^with-compiler:' stackage-lts-24.50.cabal.config > lts-pinned.config
6767
# so the field is reachable via the type. (pkgmgr-rs#528)
6868
sed -i 's/defaultTheoryLoadOptions, maudePath, TheoryLoadError/defaultTheoryLoadOptions, TheoryLoadOptions(maudePath), TheoryLoadError/' src/Main/REPL.hs
6969

70+
# Reproducibility: tamarin's version banner embeds the WALL-CLOCK compile time
71+
#
72+
# Git revision: UNKNOWN, branch: UNKNOWN
73+
# Compiled at: 2026-07-25 03:47:54.541898723 UTC
74+
#
75+
# via a TemplateHaskell splice that calls getCurrentTime while COMPILING. It
76+
# asks the clock directly, so the sandbox's SOURCE_DATE_EPOCH never reaches it,
77+
# and two builds differ by exactly that string — measured: 26 bytes out of
78+
# 135 MB, with the Haskell codegen itself bit-identical. (The git fields are
79+
# already deterministic: no repo here, so both builds say UNKNOWN.)
80+
#
81+
# Rewrite the splice to a fixed instant derived from SOURCE_DATE_EPOCH. Located
82+
# by content rather than by path so an upstream file move fails loudly here
83+
# instead of silently reverting to a wall clock.
84+
STAMP="$(date -u -d "@${SOURCE_DATE_EPOCH:-0}" '+%Y-%m-%d %H:%M:%S UTC' 2>/dev/null \
85+
|| date -u -r "${SOURCE_DATE_EPOCH:-0}" '+%Y-%m-%d %H:%M:%S UTC')"
86+
stamp_file="$(grep -rl 'Compiled at' --include='*.hs' src lib 2>/dev/null | head -1)"
87+
if [ -z "$stamp_file" ]; then
88+
echo "ERROR: no source file embeds 'Compiled at' — tamarin's version banner moved; revisit this patch." >&2
89+
exit 1
90+
fi
91+
# Replace the runIO getCurrentTime splice with the pinned literal.
92+
sed -i "s|runIO Data\.Time\.getCurrentTime|pure (\"$STAMP\")|g; \
93+
s|runIO getCurrentTime|pure (\"$STAMP\")|g" "$stamp_file"
94+
if grep -q 'getCurrentTime' "$stamp_file"; then
95+
echo "ERROR: tamarin compile-time-clock patch did not apply in $stamp_file (splice shape changed)." >&2
96+
grep -n 'getCurrentTime' "$stamp_file" >&2
97+
exit 1
98+
fi
99+
70100
# Build + install the executable (STATIC — a normal Haskell static link; the link
71101
# was never the problem). The sandbox hides build detail, so on failure dump the
72102
# real error (compile OR link) rather than a silent "Failed to build".

packages/zola/build.sh

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,20 @@
33
set -eu
44
export CC=gcc
55
export LD=gcc
6-
# Reproducibility (per minimal-repro's guide): strip absolute build
7-
# paths (source dir + cargo registry) and disable incremental builds.
8-
export RUSTFLAGS="-C linker=gcc --remap-path-prefix=$(pwd)=/builddir --remap-path-prefix=$HOME/.cargo=/cargo"
6+
# Reproducibility (per minimal-repro's guide): strip absolute build paths
7+
# (source dir + cargo registry), disable incremental builds, and pin codegen.
8+
#
9+
# codegen-units=1 is the load-bearing one here. rustc's default release build
10+
# shards codegen across parallel units, and the units finish in whatever order
11+
# the thread pool happens to produce, so functions are EMITTED in a different
12+
# order each build. The result is a binary of identical size whose contents are
13+
# a permutation of themselves — measured on 0.22.1: 16.89% of bytes differed
14+
# with the total size unchanged, and 69% of the differing windows had a
15+
# byte-exact twin elsewhere in the other build. Not codegen variance; ordering.
16+
#
17+
# symbol-mangling-version=v0 removes the other half: the legacy mangling scheme
18+
# embeds a compilation-session hash in symbol names, which varies run to run.
19+
export RUSTFLAGS="-C linker=gcc --remap-path-prefix=$(pwd)=/builddir --remap-path-prefix=$HOME/.cargo=/cargo -C codegen-units=1 -C symbol-mangling-version=v0"
920
export CARGO_INCREMENTAL=0
1021
cargo build --release
1122
mkdir -p "$OUTPUT_DIR/usr/bin"

0 commit comments

Comments
 (0)