Josh's decision of 6 October 2026: the Mac runs nothing the network depends on. docs/plans/hands-on-build-1.md plans node 1 and the observer (its node and tools/observer) as systemd units on the box, one hand at a time, with ports, DNS names, exec recovery, rollback and the decisions for main; infra/build-server/hands/install-hands.sh writes users, dirs, run scripts and units (inert, run on the box); move-hand.sh (dry run by default) builds 0.3.15 on the box, copies the override and snapshot, rsyncs each data dir hot then stopped, starts the unit and prints the hand's first executing line, copies the observer env by scp (mode 600), stops the Mac's observer first, unloads the launchd agents last. CLAUDE.md's running-agents rule carries the decision. lib.sh: a per-worktree lock on the box across sync, build and fetch (the shipper's collision at 18:48:56Z). remote-run.sh: the clean-tree test excuses stamps and target dirs at any depth with --untracked-files=all (a nested stamp failed every checkout of /srv/builds/igneum for twenty minutes) and removes a stale .git/index.lock; the self-test covers both. Plan rows for the three and the cuda prover pair built for the shipper. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
125 lines
9.2 KiB
Bash
Executable file
125 lines
9.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# The Windows cross-build on igneum-build-1: what proto-cuda/windows-node/cross-build.sh does on the Mac (Homebrew mingw-w64)
|
|
# and what the PC build job does in WSL2 (app/igneum-app/src/jobbuild.rs: Ubuntu 24.04 mingw-w64 posix threads, static
|
|
# libgcc), run on the box with sccache and -j 90 (sources synced and re-stamped with touch by lib.sh's bs_overlay_dir, the
|
|
# copied-sources rule). Run from a fork worktree (igneumd.exe, igneum-miner.exe) or from
|
|
# app/igneum-app (igneum-app.exe, igneum-ota-sign.exe, igneum-prove-verify.exe).
|
|
#
|
|
# tools/cross-remote.sh the default command for this crate
|
|
# tools/cross-remote.sh --compare target-integration/x86_64-pc-windows-gnu/release sha256 against the Mac's exes
|
|
# tools/cross-remote.sh -- build --release -p kaspad --features igneum-pow --target x86_64-pc-windows-gnu
|
|
# tools/cross-remote.sh --jobs 48 --target-dir target-win
|
|
#
|
|
# Output: <crate>/target-remote/x86_64-pc-windows-gnu/release/<name>.exe, one line each with size, sha256 and the DLL import
|
|
# list (x86_64-w64-mingw32-objdump -p on the box, as the Mac script prints it). With --compare <dir>, the Mac's exe of the same
|
|
# name is hashed too and the line says identical or differs.
|
|
#
|
|
# Reproducible (main's decision, 6 October 2026): -Wl,--no-insert-timestamp zeroes the PE header timestamp the mingw linker
|
|
# writes, so two builds of one tree give one hash (verified 6 Oct: two runs, both exes identical); the Mac's cross-build.sh and
|
|
# the PC job (jobbuild.rs) carry the same flag.
|
|
# Byte identity (6 October 2026): the same rustc (1.99.0 both sides, refused otherwise) and the same flags give the same Rust
|
|
# code, but two things still differ between the Mac's exe and the box's: the C and C++ objects (rocksdb, snappy, zstd, lz4,
|
|
# secp256k1, mimalloc) come from Homebrew's mingw gcc on the Mac and Ubuntu's gcc 13 here, and source paths embedded by
|
|
# rustc (panic locations, /Users/joshm/... against /srv/builds/...) differ unless both sides pass --remap-path-prefix, which
|
|
# the Mac script does not. So: identical bytes build to build ON THE BOX (sccache does not change output), a different hash
|
|
# from the Mac's exe is expected, and the number that matters is the DLL list (must be none since the fork's database/build.rs
|
|
# links libstdc++ statically) and that the exe runs on the PC. The same holds for the PC-built exes today.
|
|
#
|
|
# Environment of the build (the PC recipe; the Mac's -static-libstdc++ added, harmless since housekeeping made the C++ runtime
|
|
# static in the fork): CC/CXX/AR_x86_64_pc_windows_gnu = the posix mingw compilers, the linker the same gcc, RUSTFLAGS
|
|
# -static -static-libgcc -static-libstdc++, LIBCLANG_PATH = Ubuntu's llvm lib dir (bindgen for librocksdb-sys),
|
|
# BINDGEN_EXTRA_CLANG_ARGS pointed at /usr/x86_64-w64-mingw32, IGNEUM_WINDRES for the app's .rc.
|
|
set -euo pipefail
|
|
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck disable=SC2034 # shared with lib.sh
|
|
BS_TOOL=cross-remote
|
|
# shellcheck source=../infra/build-server/lib.sh
|
|
. "$HERE/../infra/build-server/lib.sh"
|
|
|
|
TARGET=x86_64-pc-windows-gnu
|
|
JOBS="${JOBS:-90}"; OUT=""; COMPARE=""; TARGET_DIR="target"; CARGO_ARGS=()
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--jobs) JOBS="$2"; shift 2 ;;
|
|
--out) OUT="$2"; shift 2 ;;
|
|
--compare) COMPARE="$2"; shift 2 ;;
|
|
--target-dir) TARGET_DIR="$2"; shift 2 ;;
|
|
--) shift; CARGO_ARGS=("$@"); break ;;
|
|
-h|--help) sed -n '2,30p' "$0"; exit 0 ;;
|
|
*) CARGO_ARGS=("$@"); break ;;
|
|
esac
|
|
done
|
|
[ "${CARGO_ARGS[0]:-}" = cargo ] && CARGO_ARGS=("${CARGO_ARGS[@]:1}")
|
|
|
|
bs_host
|
|
bs_context
|
|
case "$BS_KIND:$BS_CRATE_REL" in
|
|
node:*)
|
|
[ "${#CARGO_ARGS[@]}" -gt 0 ] || CARGO_ARGS=(build --release -p kaspad -p igneum-miner --features igneum-pow --target "$TARGET")
|
|
EXES="igneumd.exe igneum-miner.exe" ;;
|
|
repo:app/igneum-app)
|
|
[ "${#CARGO_ARGS[@]}" -gt 0 ] || CARGO_ARGS=(build --release --target "$TARGET")
|
|
EXES="igneum-app.exe igneum-ota-sign.exe igneum-prove-verify.exe" ;;
|
|
*) bs_die "cross-remote builds the fork (run from a vendor/igneum-node* worktree) or app/igneum-app, not $BS_CRATE_REL" ;;
|
|
esac
|
|
[ -n "$OUT" ] || OUT="$BS_CRATE/target-remote"
|
|
[ -z "$COMPARE" ] && [ -d "$BS_CRATE/target-integration/$TARGET/release" ] && COMPARE="$BS_CRATE/target-integration/$TARGET/release"
|
|
|
|
bs_log "$BS_KIND crate $BS_WT/$BS_CRATE_REL at $BS_SHA ($BS_BRANCH) -> $BS_HOST:$BS_REMOTE_CRATE; cargo ${CARGO_ARGS[*]} -j $JOBS; target dir $TARGET_DIR"
|
|
bs_toolchain_check
|
|
t_sync0=$(date +%s)
|
|
bs_sync_sources
|
|
bs_log "sources in place after $(( $(date +%s) - t_sync0 )) s"
|
|
|
|
# the build environment, as one shell string for the remote runner (every value is a literal; nothing from the Mac's env)
|
|
env_block='LLVM_LIB=$(ls -d /usr/lib/llvm-*/lib 2>/dev/null | sort -V | tail -1); [ -n "$LLVM_LIB" ] || { echo "no /usr/lib/llvm-*/lib on the box (apt clang libclang-dev)"; exit 2; }
|
|
export CC_x86_64_pc_windows_gnu=x86_64-w64-mingw32-gcc-posix CXX_x86_64_pc_windows_gnu=x86_64-w64-mingw32-g++-posix AR_x86_64_pc_windows_gnu=x86_64-w64-mingw32-ar
|
|
export CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc-posix
|
|
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"
|
|
export IGNEUM_WINDRES=x86_64-w64-mingw32-windres LIBCLANG_PATH="$LLVM_LIB"
|
|
export BINDGEN_EXTRA_CLANG_ARGS_x86_64_pc_windows_gnu="--target=x86_64-w64-mingw32 --sysroot=/usr/x86_64-w64-mingw32 -I/usr/x86_64-w64-mingw32/include"
|
|
echo "cross-remote: $(x86_64-w64-mingw32-gcc-posix --version | head -1); libclang $LLVM_LIB"'
|
|
pre="" # the same kaspa-build-info clean on a new commit as build-remote.sh (the Windows target has its own fingerprint)
|
|
[ "$BS_KIND" = node ] && pre="[ \"\$(cat '.cross-remote-sha-$TARGET_DIR' 2>/dev/null)\" = '$BS_SHA' ] || CARGO_TARGET_DIR='$TARGET_DIR' cargo clean -q --release -p kaspa-build-info --target $TARGET 2>/dev/null; "
|
|
cmd="$env_block
|
|
${pre}CARGO_TARGET_DIR='$TARGET_DIR' cargo $(printf '%q ' "${CARGO_ARGS[@]}")-j $JOBS 2>&1 | tee -a '$BS_REMOTE_WT/.cross-remote.log'; rc=\${PIPESTATUS[0]}; [ \$rc = 0 ] && echo '$BS_SHA' > '.cross-remote-sha-$TARGET_DIR'
|
|
for exe in $EXES; do f='$TARGET_DIR/$TARGET/release/'\$exe; [ -f \"\$f\" ] && echo \"cross-remote: DLLS \$exe: \$(x86_64-w64-mingw32-objdump -p \"\$f\" | awk '/DLL Name/ { print \$3 }' | sort -u | tr '\n' ' ')\"; done
|
|
( exit \$rc )" # a subshell exit: the runner reads \$? and still prints its RESULT line
|
|
label="$BS_WT/$BS_CRATE_REL cross $TARGET"
|
|
BR_KIND=$(bs_kind cross-remote "${CARGO_ARGS[0]}"); BR_COMMAND="cargo ${CARGO_ARGS[*]}"; BR_TARGET="$TARGET"
|
|
BR_ARTEFACTS=""; for exe in $EXES; do BR_ARTEFACTS="$BR_ARTEFACTS $TARGET_DIR/$TARGET/release/$exe"; done
|
|
export BR_KIND BR_COMMAND BR_TARGET BR_ARTEFACTS
|
|
t0=$(date +%s)
|
|
set +e
|
|
bs_remote_run "$BS_REMOTE_CRATE" "$label" "$cmd" 2>&1 | tee "/tmp/cross-remote-$$.log"
|
|
rc=${PIPESTATUS[0]}
|
|
set -e
|
|
secs=$(( $(date +%s) - t0 ))
|
|
result=$(grep -m1 '^build-remote: RESULT' "/tmp/cross-remote-$$.log" || true)
|
|
dlls=$(grep '^cross-remote: DLLS' "/tmp/cross-remote-$$.log" || true); rm -f "/tmp/cross-remote-$$.log"
|
|
if [ "$rc" != 0 ]; then bs_die "remote cross-build failed (rc $rc) after $(bs_fmt_secs "$secs"); $result"; fi
|
|
bs_log "remote cross-build done in $(bs_fmt_secs "$secs") wall from the Mac; ${result#build-remote: RESULT }"
|
|
|
|
mkdir -p "$OUT/$TARGET/release"
|
|
for exe in $EXES; do
|
|
dest="$OUT/$TARGET/release/$exe"
|
|
bs_rsync -p "$BS_HOST:$BS_REMOTE_CRATE/$TARGET_DIR/$TARGET/release/$exe" "$dest" || bs_die "no $exe on the box after the build"
|
|
sum=$(bs_sha256 "$dest"); line="$exe: $(bs_size "$dest") bytes, sha256 $sum"
|
|
d=$(printf '%s\n' "$dlls" | grep "DLLS $exe:" | sed 's/.*DLLS [^:]*: *//'); line="$line, DLLs: ${d:-none}"
|
|
if [ -n "$COMPARE" ] && [ -f "$COMPARE/$exe" ]; then
|
|
msum=$(bs_sha256 "$COMPARE/$exe")
|
|
if [ "$msum" = "$sum" ]; then line="$line; IDENTICAL to $COMPARE/$exe"; else line="$line; differs from $COMPARE/$exe ($(bs_size "$COMPARE/$exe") bytes, sha256 ${msum:0:16}...; expected, see the header)"; fi
|
|
fi
|
|
bs_log "$line"
|
|
# the commit-string gate (rule of 6 October 2026): a node exe without its commit in its strings fails the run
|
|
case "$BS_KIND:$exe" in node:igneumd.exe) "$HERE/ci/commit-string-check.sh" "$dest" "$BS_SHA" || bs_die "commit-string gate failed for $exe" ;; esac # only kaspad depends on kaspa-build-info
|
|
done
|
|
# an exe that imports libstdc++-6.dll (a fork before the housekeeping commit that made the C++ runtime static) needs the
|
|
# three runtime DLLs OF THIS TOOLCHAIN beside it (the PC job does the same; push-inputs.sh takes them from next to the exes)
|
|
if printf '%s\n' "$dlls" | grep -q 'libstdc++-6.dll'; then
|
|
bs_ssh 'd=$(dirname "$(x86_64-w64-mingw32-gcc-posix -print-file-name=libstdc++-6.dll)"); mkdir -p /tmp/igneum-mingw-dlls; cp "$d/libstdc++-6.dll" "$d/libgcc_s_seh-1.dll" /usr/x86_64-w64-mingw32/lib/libwinpthread-1.dll /tmp/igneum-mingw-dlls/ && ls /tmp/igneum-mingw-dlls' >/dev/null \
|
|
&& bs_rsync -p "$BS_HOST:/tmp/igneum-mingw-dlls/*.dll" "$OUT/$TARGET/release/" || bs_die "could not fetch the mingw runtime DLLs"
|
|
for dll in libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll; do bs_log "runtime $dll: $(bs_size "$OUT/$TARGET/release/$dll") bytes, sha256 $(bs_sha256 "$OUT/$TARGET/release/$dll") (GCC 13 posix, beside the exes)"; done
|
|
fi
|
|
bs_log "exes in $OUT/$TARGET/release"
|
|
bs_wt_unlock
|