#!/bin/bash
# desktop-build-sm86.sh -- runs ON THE DESKTOP, as an operator, inside the user unit bsw-build-sm86 (Nice 19, CPUQuota 1600 %,
# MemoryMax 24G, idle I/O: the EPYC carries the live seats). Lane bsw-kit, 2026-10-09. NO CARD, NO SUDO, NO NETWORK.
#
# It builds, from source only (never an installer, setup.py, a release zip or a prebuilt binary), for the desktop's smaller card's Ampere
# (sm_86), into ~/card24-strata/build/:
#   1. llama.cpp 3cf03257 (the Strata bench's pin; the vendored ggml of the engine), targets llama-server llama-bench
#      llama-cli, with the laptop bench's flags (strata-bench/build-llamacpp.sh: Release, GGML_CUDA, GGML_NATIVE,
#      LLAMA_CURL=OFF, LLAMA_BUILD_TESTS=OFF) plus the desktop window 0's two (LLAMA_USE_PREBUILT_UI=OFF, and the RPATH to the
#      staged CUDA 13.4 lib folder, desktop-window-0/STAGE.md section 4), CMAKE_CUDA_ARCHITECTURES=86 in place of 120;
#   2. Strata v0.1.39 (6f32ec07) target strata, with the desktop window 2's flags (stage/desktop-strata-build.sh: Release,
#      STRATA_ENABLE_CUDA, STRATA_BUILD_TESTS=OFF, STRATA_GGML_DIR = the llama.cpp clone, the RPATH), arch 86 in place
#      of 120.
# The SOURCES are the desktop window 0's and 2's clones under ~/bench-flashnext, READ-ONLY (the sandbox binds / read-only;
# both builds are out-of-tree). The CUDA toolkit is window 0's staged 13.4 (the desktop's system 13.1 cannot compile against
# glibc 2.43). Everything runs inside bwrap with NO network (--unshare-net), a read-only root, a minimal /dev (bwrap's
# --dev: no GPU device node exists inside, so CMake's CUDA probe cannot touch either card) and a clean environment
# (env -i, CUDA_VISIBLE_DEVICES empty besides). Only the two build folders and a fresh HOME and TMPDIR are writable.
#
#   bash ~/card24-strata/stage/desktop-build-sm86.sh
#
# Writes only under ~/card24-strata/: build/llama.cpp-3cf03257-sm86/, build/strata-6f32ec07-sm86/ (each refused if
# it exists: launch once), stage/logs/, stage/build-home/, stage/build-tmp/, stage/BUILD-RECEIPT.txt. The receipt's
# last line: BUILD DONE llama <sha256 llama-server> strata <sha256 strata> | BUILD STOP <why> | BUILD FAILED <step> rc <n>.
set -uo pipefail
B=/workshop/bench-flashnext
BS=/workshop/card24-strata
LL_FULL=3cf03257f219afbe7334045ff7c6a06ac68c627d
ST_FULL=6f32ec070f23ced9f50e704d854d775da52591ab
LL_SRC=$B/llama.cpp-3cf03257
ST_SRC=$B/strata-6f32ec07
LL_OUT=$BS/build/llama.cpp-3cf03257-sm86
ST_OUT=$BS/build/strata-6f32ec07-sm86
CUDA=$B/cuda-13.4
LIB=$CUDA/targets/x86_64-linux/lib
ST=$BS/stage
LOGS=$ST/logs
RCPT=$ST/BUILD-RECEIPT.txt
JOBS=${BSW_JOBS:-12}
export GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null GIT_OPTIONAL_LOCKS=0
mkdir -p "$LOGS"
say()  { echo "$(date -u +%FT%TZ) $*" | tee -a "$RCPT"; }
stop() { say "BUILD STOP $*"; exit 3; }
fail() { say "BUILD FAILED $1 rc $2"; exit 4; }
[ -e "$RCPT" ] && { echo "BUILD STOP $RCPT exists (launch once: read it)"; exit 3; }
say "desktop-build-sm86 start (host $(hostname), jobs $JOBS)"
# the pins, read before anything is written
got=$(git -C "$LL_SRC" --no-optional-locks rev-parse HEAD 2>/dev/null); [ "$got" = "$LL_FULL" ] || stop "$LL_SRC is at '$got', not $LL_FULL"
got=$(git -C "$ST_SRC" --no-optional-locks rev-parse HEAD 2>/dev/null); [ "$got" = "$ST_FULL" ] || stop "$ST_SRC is at '$got', not $ST_FULL"
tag=$(git -C "$ST_SRC" --no-optional-locks rev-parse 'v0.1.39^{commit}' 2>/dev/null); [ "$tag" = "$ST_FULL" ] || stop "v0.1.39^{commit} reads '$tag', not $ST_FULL"
porc=$(git -C "$ST_SRC" --no-optional-locks status --porcelain --ignored 2>&1) || stop "git status failed in $ST_SRC"
[ -z "$porc" ] || stop "$ST_SRC is not clean: $(printf '%s' "$porc" | head -3 | tr '\n' ' ')"
porc=$(git -C "$LL_SRC" --no-optional-locks status --porcelain 2>&1) || stop "git status failed in $LL_SRC"
[ -z "$porc" ] || stop "$LL_SRC has tracked changes: $(printf '%s' "$porc" | head -3 | tr '\n' ' ')"
[ -x "$CUDA/bin/nvcc" ] || stop "$CUDA/bin/nvcc is absent (window 0's staged CUDA 13.4)"
nv=$("$CUDA/bin/nvcc" --version 2>&1 | grep -o 'V13\.4\.[0-9]*' | head -1); [ -n "$nv" ] || stop "nvcc does not report CUDA 13.4"
for l in libcudart.so.13 libcublas.so.13 libcublasLt.so.13; do [ -e "$LIB/$l" ] || stop "$LIB/$l is absent"; done
for o in "$LL_OUT" "$ST_OUT"; do [ ! -e "$o" ] || stop "$o exists (launch once)"; done
/usr/bin/bwrap --ro-bind / / --dev /dev --proc /proc --unshare-net true 2>/dev/null || stop "bwrap --unshare-net does not run here"
mkdir -p "$LL_OUT" "$ST_OUT" "$ST/build-home" "$ST/build-tmp"
SANDBOX=(/usr/bin/bwrap --unshare-net --die-with-parent --ro-bind / / --dev /dev --proc /proc --tmpfs /run
         --bind "$LL_OUT" "$LL_OUT" --bind "$ST_OUT" "$ST_OUT"
         --bind "$ST/build-home" "$ST/build-home" --bind "$ST/build-tmp" "$ST/build-tmp" --
         /usr/bin/env -i PATH="$CUDA/bin:/usr/bin:/bin" HOME="$ST/build-home" TMPDIR="$ST/build-tmp" LANG=C.UTF-8
         CUDA_VISIBLE_DEVICES= GIT_CONFIG_NOSYSTEM=1 GIT_CONFIG_GLOBAL=/dev/null GIT_OPTIONAL_LOCKS=0)
# the sandbox's own proof, written to the receipt: no network interface but lo, no GPU device node
probe=$("${SANDBOX[@]}" /bin/sh -c 'echo "$(awk -F: "NR>2 {gsub(/ /, \"\", \$1); print \$1}" /proc/net/dev | tr "\n" " ")| nvidia nodes: $(ls /dev | grep -c nvidia)"' 2>&1)
say "sandbox probe: interfaces ${probe}"
case "$probe" in "lo | nvidia nodes: 0") ;; *) stop "the sandbox is not sealed: '$probe'" ;; esac
say "llama.cpp configure (CUDA $nv, arch 86)"
"${SANDBOX[@]}" cmake -S "$LL_SRC" -B "$LL_OUT" -G Ninja -DCMAKE_BUILD_TYPE=Release -DGGML_CUDA=ON \
  -DCMAKE_CUDA_ARCHITECTURES=86 -DCMAKE_CUDA_COMPILER="$CUDA/bin/nvcc" -DGGML_NATIVE=ON -DLLAMA_CURL=OFF \
  -DLLAMA_BUILD_TESTS=OFF -DLLAMA_USE_PREBUILT_UI=OFF -DCMAKE_BUILD_RPATH="$LIB" > "$LOGS/llama-configure.log" 2>&1 || fail llama-configure $?
t0=$(date +%s)
"${SANDBOX[@]}" cmake --build "$LL_OUT" --target llama-server llama-bench llama-cli -j "$JOBS" > "$LOGS/llama-build.log" 2>&1 || fail llama-build $?
say "llama.cpp built in $(( $(date +%s) - t0 )) s"
for b in llama-server llama-bench llama-cli; do [ -f "$LL_OUT/bin/$b" ] || fail "llama-no-$b" 5; done
say "strata configure (CUDA $nv, arch 86, ggml from $LL_SRC)"
"${SANDBOX[@]}" cmake -G Ninja -S "$ST_SRC" -B "$ST_OUT" -DCMAKE_BUILD_TYPE=Release -DSTRATA_ENABLE_CUDA=ON -DSTRATA_BUILD_TESTS=OFF \
  -DCMAKE_CUDA_ARCHITECTURES=86 -DCMAKE_CUDA_COMPILER="$CUDA/bin/nvcc" -DSTRATA_GGML_DIR="$LL_SRC" \
  -DCMAKE_BUILD_RPATH="$LIB" > "$LOGS/strata-configure.log" 2>&1 || fail strata-configure $?
t0=$(date +%s)
"${SANDBOX[@]}" cmake --build "$ST_OUT" --target strata -j "$JOBS" > "$LOGS/strata-build.log" 2>&1 || fail strata-build $?
say "strata built in $(( $(date +%s) - t0 )) s"
[ -f "$ST_OUT/strata" ] || fail strata-no-binary 5
# the receipts: every binary and the CUDA libraries' digests, the arch the code carries, the RUNPATH
for f in "$LL_OUT"/bin/llama-server "$LL_OUT"/bin/llama-bench "$LL_OUT"/bin/llama-cli "$LL_OUT"/bin/*.so* "$ST_OUT/strata"; do
  [ -f "$f" ] && [ ! -L "$f" ] && sha256sum "$f"
done > "$ST/BINARIES.sha256"
say "binaries: $(wc -l < "$ST/BINARIES.sha256") files hashed into stage/BINARIES.sha256"
arch=$("$CUDA/bin/cuobjdump" --list-elf "$LL_OUT/bin/libggml-cuda.so" 2>/dev/null | grep -o 'sm_[0-9]*a\?' | sort | uniq -c | tr '\n' ' ')
say "libggml-cuda.so ELF arches: ${arch:-unread}"
arch=$("$CUDA/bin/cuobjdump" --list-elf "$ST_OUT/strata" 2>/dev/null | grep -o 'sm_[0-9]*a\?' | sort | uniq -c | tr '\n' ' ')
say "strata ELF arches: ${arch:-unread}"
say "llama-server RUNPATH: $(readelf -d "$LL_OUT/bin/llama-server" 2>/dev/null | grep -o 'R.*PATH.*' | head -1)"
say "strata RUNPATH: $(readelf -d "$ST_OUT/strata" 2>/dev/null | grep -o 'R.*PATH.*' | head -1)"
say "ldd strata cuda: $(ldd "$ST_OUT/strata" 2>/dev/null | grep -E 'libcud|libcublas' | awk '{print $1"->"$3}' | tr '\n' ' ')"
ls_sha=$(sha256sum "$LL_OUT/bin/llama-server" | cut -c1-64); st_sha=$(sha256sum "$ST_OUT/strata" | cut -c1-64)
say "BUILD DONE llama $ls_sha strata $st_sha"
