igneum/proto-cuda/windows-node/cross-build.sh
igneum-labs 67ae1e4c6d Reproducible builds: SOURCE_DATE_EPOCH from the commit's author time, TZ=UTC and one fixed target path in every build path; self-test
Main's rule of 6 October 2026 from the 0.3.14 repro (docs/evidence/reproduced/0.3.14.md): prost's protowire.rs embeds OUT_DIR,
libmimalloc-sys embeds __DATE__/__TIME__, sccache hid both. lib.sh bs_repro_env exports SOURCE_DATE_EPOCH=<author time> TZ=UTC in
front of every remote command (build-remote.sh, cross-remote.sh, workers-remote.sh); remote-run.sh exports BR_SDE too and logs it
as source_date_epoch; proto-cuda/windows-node/cross-build.sh exports the same; the PC job carries node.commit_time in the manifest
(push-build-inputs.sh) and exports it before every cargo build of a stage (jobbuild.rs, unit test asserts it; 4 of 4 pass on the
box). Target dirs stay one fixed path per target. tools/build-remote.sh --self-test-repro [--full] from a fork worktree, run on
the box: igneum-miner twice a minute apart without sccache (RUSTC_WRAPPER=/usr/bin/env, an empty value is unset to cargo) MATCH
91e130f5..., a per-run target path differs (OUT_DIR shown); --full: kaspad with libmimalloc-sys recompiled a minute later MATCH
70219bc2..., without the epoch differs (__DATE__ shown).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-06 20:09:23 +00:00

60 lines
5.3 KiB
Bash
Executable file

#!/usr/bin/env bash
# Cross-compiles igneumd.exe and igneum-miner.exe for x86_64-pc-windows-gnu on the Mac (Homebrew mingw-w64 and llvm).
# Worked on 3 October 2026 (8 min 25 s with -j 6 at nice 19 on a loaded M5 Max); see WINDOWS-MINER.md, "Run your own node".
# What each variable is for:
# CC/CXX/AR_x86_64_pc_windows_gnu the cc crate builds rocksdb (C++), snappy, zstd, lz4, bzip2, zlib, secp256k1 and
# mimalloc with the mingw compilers
# LIBCLANG_PATH librocksdb-sys runs bindgen on rocksdb/c.h; Homebrew llvm's libclang
# BINDGEN_EXTRA_CLANG_ARGS_... bindgen's clang must parse the mingw headers for the Windows target, not the Mac's
# CARGO_TARGET_..._RUSTFLAGS -static: ld takes libstdc++.a, libgcc.a and libwinpthread.a instead of the import
# libraries, so the exe does not need libstdc++-6.dll, libgcc_s_seh-1.dll or
# libwinpthread-1.dll on the PC (the first build linked libstdc++-6.dll because
# -static-libstdc++ means nothing to the gcc driver rustc links with)
# Usage: windows-node/cross-build.sh [node worktree, default vendor/igneum-node-win] [cargo jobs, default 6]
set -euo pipefail
HERE="$(cd "$(dirname "$0")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
NODE="${1:-$ROOT/vendor/igneum-node-win}"
JOBS="${2:-6}"
MINGW="/opt/homebrew/opt/mingw-w64/toolchain-x86_64/x86_64-w64-mingw32"
[ -d "$MINGW" ] || { echo "no mingw-w64 at $MINGW (brew install mingw-w64)" >&2; exit 1; }
[ -f /opt/homebrew/opt/llvm/lib/libclang.dylib ] || { echo "no libclang at /opt/homebrew/opt/llvm/lib (brew install llvm)" >&2; exit 1; }
export PATH="$HOME/.cargo/bin:/opt/homebrew/bin:$PATH"
rustup target list --installed | grep -q x86_64-pc-windows-gnu || rustup target add x86_64-pc-windows-gnu
export CARGO_TARGET_DIR="${CARGO_TARGET_DIR:-target}"
export CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc
export CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++
export AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar
export LIBCLANG_PATH=/opt/homebrew/opt/llvm/lib
export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--target=x86_64-w64-mingw32 --sysroot=$MINGW -I$MINGW/include"
# 5 October 2026 (0.3.7): -static-libstdc++ added as a try; -static alone left libstdc++-6.dll in the import table of
# every exe so far (0.3.5's and the PC's), which is why the payload must ship the DLLs of the toolchain that linked
# the exes (push-inputs.sh takes them from next to the exes first, then from this toolchain) and why push-inputs.sh
# refuses an exe that imports a symbol the shipped DLL does not export.
# 5 October 2026 (housekeeping, after 0.3.8): the fork's database/build.rs now links libstdc++ statically on this
# target (the toolchain's libstdc++.a offered as libstdc++.dll.a in a search dir), so a node built from a fork at or
# after that commit imports no mingw DLL and the pairing rule is moot for it; the gate and the DLL copy stay for any
# older fork. The PC-built exe (GCC 13) died at its first rocksdb call with the dynamic runtime; see the
# release-0.3.6 plan, section 10.
# 6 October 2026 (main's decision): -Wl,--no-insert-timestamp makes the exe reproducible (the PE header timestamp was the one
# byte-level difference between two builds of one tree on igneum-build-1); the box (tools/cross-remote.sh) and the PC job carry it too
export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUSTFLAGS="-C link-arg=-static -C link-arg=-static-libgcc -C link-arg=-static-libstdc++ -C link-arg=-Wl,--no-insert-timestamp"
cd "$NODE"
# reproducible (main, 6 October 2026, the 0.3.14 repro): the commit's author time for mimalloc's __DATE__/__TIME__, UTC, and ONE
# target dir per target (CARGO_TARGET_DIR above is fixed, never a per-run name; prost embeds OUT_DIR)
SOURCE_DATE_EPOCH=$(git log -1 --format=%at HEAD); export SOURCE_DATE_EPOCH TZ=UTC
# The commit hash (6 October 2026, found on igneum-build-1): kaspa-build-info's build.rs embeds the commit only when .git is
# a DIRECTORY and HEAD is a symbolic ref to a branch file, and once it has found nothing it emits no rerun-if-changed, so
# cargo never runs it again in this target dir. Two steps: clean that one crate when the commit changed since the last build
# here, and refuse a worktree (.git is a file there: the hash would be empty and tools/ci/commit-string-check.sh would fail
# the release). Build the Windows exes from the fork's main checkout or through tools/cross-remote.sh (the box checks out a branch).
sha=$(git rev-parse HEAD)
[ -d .git ] || { echo "$NODE/.git is not a directory (a worktree): kaspa-build-info would embed no commit; use tools/cross-remote.sh or the fork's main checkout" >&2; exit 1; }
[ "$(cat ".cross-build-sha-$CARGO_TARGET_DIR" 2>/dev/null)" = "$sha" ] || cargo clean -q --release -p kaspa-build-info --target x86_64-pc-windows-gnu 2>/dev/null || true
nice -n 19 cargo build --release -j "$JOBS" -p kaspad -p igneum-miner --features igneum-pow --target x86_64-pc-windows-gnu
echo "$sha" > ".cross-build-sha-$CARGO_TARGET_DIR"
for exe in igneumd igneum-miner; do
f="$CARGO_TARGET_DIR/x86_64-pc-windows-gnu/release/$exe.exe"
echo "$f: $(stat -f %z "$f") bytes; DLLs: $(x86_64-w64-mingw32-objdump -p "$f" | grep 'DLL Name' | awk '{print $3}' | sort -u | tr '\n' ' ')"
[ "$exe" = igneumd ] && "$ROOT/tools/ci/commit-string-check.sh" "$f" "$sha" # only kaspad depends on kaspa-build-info
done