diff --git a/infra/cloud-devnet/provision.sh b/infra/cloud-devnet/provision.sh index 31082b2ce..f13772134 100755 --- a/infra/cloud-devnet/provision.sh +++ b/infra/cloud-devnet/provision.sh @@ -4,13 +4,14 @@ # ./provision.sh source tarball -> builder VM -> build/bin/{igneumd,igneum-miner} -> every node (systemd units) # ./provision.sh build only the build (creates the builder VM if build/builder.ip is missing, deletes it afterwards) # ./provision.sh install only the install on the nodes (needs build/bin/igneumd) +# BIN_SOURCE=mac ./provision.sh [build|all] no builder VM: cross-compile on this Mac (infra/cross/build-linux.sh, +# cargo-zigbuild, x86_64-unknown-linux-gnu glibc 2.36) and take build/bin from its output # -# Why a builder VM and not a cross-compile on the Mac: rustup here has only aarch64-apple-darwin and -# x86_64-pc-windows-gnu installed and there is no x86_64 Linux linker (no musl-cross, zig, cross or docker; checked -# 3 Oct 2026). A Linux cross-compile would need `rustup target add x86_64-unknown-linux-gnu` plus a linker -# (`brew install filosottile/musl-cross/musl-cross` and a musl target), and the rocksdb bindgen trick of the Windows -# cross-build again. The builder costs about EUR 0.05 for the hour (cpx41, approximate) and the Mac stays free. -# The binaries are cached in build/bin; REBUILD=1 forces a new build. +# Two ways to get the binaries. BIN_SOURCE=builder (the default, 3 Oct 2026): a builder VM compiles the source +# tarball (10 to 25 minutes on 8 vCPU, about EUR 0.05). BIN_SOURCE=mac (4 Oct 2026): cargo-zigbuild on this Mac +# cross-compiles for x86_64 Linux glibc 2.36 (zig is the C/C++ toolchain and linker; rocksdb, blst, secp256k1 and the +# execution layer link; infra/cross/build-linux.sh), no VM at all. The binaries are cached in build/bin; REBUILD=1 +# forces a new build. . "$(dirname "$0")/lib/common.sh" @@ -23,6 +24,13 @@ do_build() { log "build/bin/igneumd exists ($(cat "$BIN_DIR/version.txt" 2>/dev/null | tr '\n' ' ')); REBUILD=1 to rebuild" return fi + if [ "${BIN_SOURCE:-builder}" = mac ]; then + log "BIN_SOURCE=mac: cross-compiling on this Mac (no builder VM) from $NODE_SRC" + NODE_SRC="$NODE_SRC" OUT_DIR="$BIN_DIR" "$HERE/../cross/build-linux.sh" + printf 'exported %s\nigneum-node (%s): HEAD %s (%s), cross-compiled on the Mac\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$NODE_SRC" "$(git -C "$NODE_SRC" rev-parse --short HEAD)" "$(git -C "$NODE_SRC" rev-parse --abbrev-ref HEAD)" > "$BIN_DIR/src.stamp" + log "binaries in $BIN_DIR: $(cat "$BIN_DIR/version.txt" | tr '\n' ' ')" + return + fi "$HERE/make-source.sh" if [ ! -s "$BUILD_DIR/builder.ip" ]; then YES="${YES:-0}" "$HERE/create.sh" builder diff --git a/infra/cross/.gitignore b/infra/cross/.gitignore new file mode 100644 index 000000000..89f9ac04a --- /dev/null +++ b/infra/cross/.gitignore @@ -0,0 +1 @@ +out/ diff --git a/infra/cross/build-linux.sh b/infra/cross/build-linux.sh new file mode 100755 index 000000000..003fab4cd --- /dev/null +++ b/infra/cross/build-linux.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Cross-compile igneumd and igneum-miner for x86_64 Linux (Debian 12, glibc 2.36) on the Mac, so a server never +# builds. Uses cargo-zigbuild (zig as the C/C++ cross toolchain and linker); rocksdb (librocksdb-sys, C++), blst, +# secp256k1 and the execution layer's crates all link through it. +# +# infra/cross/build-linux.sh build from NODE_SRC (default vendor/igneum-node-v4) into TARGET_DIR +# NODE_SRC=... TARGET_DIR=... JOBS=4 infra/cross/build-linux.sh +# +# Output: $TARGET_DIR/x86_64-unknown-linux-gnu/release/{igneumd,igneum-miner} and $OUT_DIR/{igneumd,igneum-miner,version.txt} +# (OUT_DIR default infra/cross/out). First build: about 500 crates; later builds are incremental. +# +# One-off setup (4 Oct 2026, done on this Mac): brew install zig (0.17.0); cargo install cargo-zigbuild (0.23.4); +# rustup target add x86_64-unknown-linux-gnu. Docker is not installed here, so `cross` is not an option. +set -euo pipefail +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO="$(cd "$HERE/../.." && pwd)" +NODE_SRC="${NODE_SRC:-$REPO/vendor/igneum-node-v4}" +TARGET_DIR="${TARGET_DIR:-$REPO/vendor/igneum-node/target-linux}" +OUT_DIR="${OUT_DIR:-$HERE/out}" +TARGET="${TARGET:-x86_64-unknown-linux-gnu}" +GLIBC="${GLIBC:-2.36}" +JOBS="${JOBS:-4}" +CARGO="${CARGO:-$HOME/.cargo/bin/cargo}" +log() { printf '%s %s\n' "$(date -u +%H:%M:%S)" "$*"; } +die() { log "ERROR: $*" >&2; exit 1; } + +command -v zig >/dev/null 2>&1 || die "zig is not installed (brew install zig)" +[ -x "$HOME/.cargo/bin/cargo-zigbuild" ] || command -v cargo-zigbuild >/dev/null 2>&1 || die "cargo-zigbuild is not installed (cargo install cargo-zigbuild)" +"$HOME/.cargo/bin/rustup" target list --installed | grep -q "^$TARGET\$" || { log "adding rust target $TARGET"; "$HOME/.cargo/bin/rustup" target add "$TARGET"; } +[ -f "$NODE_SRC/Cargo.toml" ] || die "no node source at $NODE_SRC" + +log "cross-compiling igneumd and igneum-miner for $TARGET.$GLIBC from $NODE_SRC ($(git -C "$NODE_SRC" rev-parse --short HEAD), $(git -C "$NODE_SRC" rev-parse --abbrev-ref HEAD)); target dir $TARGET_DIR; zig $(zig version); $JOBS jobs at nice 19" +start=$(date +%s) +( cd "$NODE_SRC" && CARGO_TARGET_DIR="$TARGET_DIR" nice -n 19 "$CARGO" zigbuild --release -j "$JOBS" \ + --target "$TARGET.$GLIBC" -p kaspad -p igneum-miner --features kaspad/igneum-pow ) +secs=$(( $(date +%s) - start )) +log "cross-compile finished in $secs s ($(( secs / 60 )) min)" + +mkdir -p "$OUT_DIR" +cp "$TARGET_DIR/$TARGET/release/igneumd" "$TARGET_DIR/$TARGET/release/igneum-miner" "$OUT_DIR/" +chmod +x "$OUT_DIR/igneumd" "$OUT_DIR/igneum-miner" +ver=$(grep -m1 '^version' "$NODE_SRC/Cargo.toml" | sed 's/.*"\(.*\)".*/\1/') +{ + printf 'igneumd %s\n' "$ver" + printf 'cross-compiled on %s from %s (%s) with cargo-zigbuild, target %s.%s, %s s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$(git -C "$NODE_SRC" rev-parse --short HEAD)" "$(git -C "$NODE_SRC" rev-parse --abbrev-ref HEAD)" "$TARGET" "$GLIBC" "$secs" + git -C "$NODE_SRC" rev-parse --short HEAD +} > "$OUT_DIR/version.txt" +file "$OUT_DIR/igneumd" "$OUT_DIR/igneum-miner" | sed 's/^/ /' +ls -la "$OUT_DIR" | sed 's/^/ /' +log "binaries in $OUT_DIR" diff --git a/infra/seed-nodes/README.md b/infra/seed-nodes/README.md index d65aa26e2..5baaa3a40 100644 --- a/infra/seed-nodes/README.md +++ b/infra/seed-nodes/README.md @@ -30,7 +30,10 @@ rollback. `health.sh` reports `unit=active-v4` once the v4 unit serves. The runb Settings in `config.sh`: provider, name, type, location, image, ssh key (`~/.ssh/igneum_ed25519`), `SSH_SOURCE` (`any`, `me` or a CIDR for port 22), `NETWORK_ARGS` (`--devnet`; a private network: `--devnet --devnet-suffix=20`), -`SEED_PEERS`, `BUILD_WHERE` (`seed` builds on the VM; `bin` reuses `../cloud-devnet/build/bin`). The Hetzner token is +`SEED_PEERS`, `BUILD_WHERE` (`seed` builds on the VM; `bin` reuses `../cloud-devnet/build/bin`). `BIN_SOURCE=mac +./stage-v4.sh` (4 Oct 2026) skips the VM build: `infra/cross/build-linux.sh` cross-compiles the devnet-v4 worktree +on the Mac with cargo-zigbuild (x86_64 Linux, glibc 2.36) in minutes, uploads the binaries to `/root/v4/out` and +installs them exactly as a VM build would (the seed's own build took 55 minutes on its 2 vCPU). The Hetzner token is read from `~/.config/igneum/hetzner-token` and never printed. Node flags (`node/run-seed.sh`): `--devnet --appdir=/var/lib/igneum --rpclisten=127.0.0.1:26610 diff --git a/infra/seed-nodes/stage-v4.sh b/infra/seed-nodes/stage-v4.sh index 9c69919bf..4dfdfd1e5 100755 --- a/infra/seed-nodes/stage-v4.sh +++ b/infra/seed-nodes/stage-v4.sh @@ -9,6 +9,9 @@ # a finished build is not repeated), then wait and install; for a failed post-build step # ./stage-v4.sh [seed-name] wait only wait for a build already started and install when it is done # ./stage-v4.sh [seed-name] status one line: build state, installed version, unit states +# BIN_SOURCE=mac ./stage-v4.sh [seed-name] no build on the VM: cross-compile on this Mac (infra/cross/build-linux.sh, +# cargo-zigbuild, x86_64 Linux glibc 2.36, minutes instead of the VM's 40 to 60), +# upload the binaries to /root/v4/out and install exactly as the VM build would # The build runs under `systemd-run` with MemoryHigh 1600M, MemoryMax 2400M, nice 19, idle IO, 1 cargo job (the VM has # 2 vCPU and 4 GB; the v3 node uses about 1.5 GB; the 6 GB swap file from provision-seed.sh takes the overflow), so a # dropped ssh session does not kill it and it cannot starve the running node. Expect 40 to 60 minutes (approximate: the @@ -73,8 +76,25 @@ wait_build() { log "staged. Nothing was switched: $(remote_status)" } +# BIN_SOURCE=mac (4 Oct 2026): cross-compile on the Mac, upload, install; the VM never builds. +if [ "${BIN_SOURCE:-seed}" = mac ] && [ "$mode" = start ]; then + for try in $(seq 1 10); do sssh "$ip" true >/dev/null 2>&1 && break; log "waiting for ssh ($try)"; sleep 10; done + sssh "$ip" true || die "ssh to $ip failed" + [ -f "$V4_SRC/Cargo.toml" ] || die "no devnet-v4 worktree at $V4_SRC" + log "BIN_SOURCE=mac: cross-compiling $V4_SRC ($(git -C "$V4_SRC" rev-parse --short HEAD), $(git -C "$V4_SRC" rev-parse --abbrev-ref HEAD)) on this Mac; nothing builds on $name" + NODE_SRC="$V4_SRC" OUT_DIR="$BUILD_DIR/cross-out" "$HERE/../cross/build-linux.sh" | tee -a "$LOG" + log "uploading the cross-compiled binaries to /root/v4/out on $name" + sssh "$ip" 'mkdir -p /root/v4/out && rm -f /root/v4/DONE /root/v4/FAILED' + sscp "$BUILD_DIR/cross-out/igneumd" "$BUILD_DIR/cross-out/igneum-miner" "$BUILD_DIR/cross-out/version.txt" "$SSH_USER@$ip:/root/v4/out/" + sssh "$ip" 'chmod +x /root/v4/out/igneumd /root/v4/out/igneum-miner && /root/v4/out/igneumd --version | head -1 && touch /root/v4/DONE' | tee -a "$LOG" + install_v4 + log "staged from the Mac. Nothing was switched: $(remote_status)" + exit 0 +fi + case "$mode" in status) remote_status ;; + wait) wait_build ;; resume) sssh "$ip" "test -f /root/v4/src.tar.gz" || die "nothing to resume on $name (no /root/v4/src.tar.gz; run start)"