Monorepo for Aesthetic.Computer aesthetic.computer
at main 760 lines 30 kB view raw
1#!/bin/bash 2# AC Native OS — Minimal reproducible build 3# Builds: C binary → initramfs → kernel → vmlinuz 4# Input: /src (repo), Output: /out/vmlinuz 5set -eu 6 7SRC="${AC_SRC:-/repo}" 8OUT="${AC_OUT:-/out}" 9NATIVE="$SRC/fedac/native" 10BUILD="$NATIVE/build" 11KVER="${KERNEL_VERSION:-6.19.9}" 12KMAJOR="${KVER%%.*}" 13MEDIA_LAYOUT_LIB="$NATIVE/scripts/media-layout.sh" 14 15log() { echo -e "\033[0;36m[ac-os]\033[0m $*"; } 16err() { echo -e "\033[0;31m[ac-os]\033[0m $*" >&2; } 17 18# shellcheck source=/workspaces/aesthetic-computer/fedac/native/scripts/media-layout.sh 19source "$MEDIA_LAYOUT_LIB" 20 21# Always build in /tmp inside the container to avoid bind-mount permission issues 22BUILD="/tmp/ac-build" 23mkdir -p "$BUILD" "$OUT" 24 25# ── Git info ── 26GIT_HASH="${AC_GIT_HASH:-$(cd "$SRC" 2>/dev/null && git rev-parse --short HEAD 2>/dev/null || echo unknown)}" 27BUILD_TS="${AC_BUILD_TS:-$(date -u '+%Y-%m-%dT%H:%M')}" 28BUILD_NAME="${AC_BUILD_NAME:-docker-build}" 29HANDLE="${AC_HANDLE:-jeffrey}" 30KERNEL_JOBS="${AC_KERNEL_JOBS:-$(nproc)}" 31 32show_kernel_error_context() { 33 local log_file="$1" 34 local line 35 local start 36 local end 37 38 line=$(grep -n -m 1 -E '(^|[^[:alpha:]])error:|Error [0-9]+|No rule to make target|undefined reference' "$log_file" | cut -d: -f1 || true) 39 if [ -n "$line" ]; then 40 start=$((line > 80 ? line - 80 : 1)) 41 end=$((line + 160)) 42 err " Kernel error context (lines ${start}-${end}):" 43 sed -n "${start},${end}p" "$log_file" >&2 44 fi 45 err " Kernel build tail (last 220 lines):" 46 tail -220 "$log_file" >&2 || true 47} 48 49run_make_with_heartbeat() { 50 local log_file="$1" 51 shift 52 local heartbeat_secs="${AC_KERNEL_HEARTBEAT_SECS:-20}" 53 local last_count="-1" 54 local last_line="" 55 local line_count 56 local current_line 57 58 : > "$log_file" 59 "$@" >"$log_file" 2>&1 & 60 local make_pid=$! 61 62 while kill -0 "$make_pid" 2>/dev/null; do 63 sleep "$heartbeat_secs" 64 [ -f "$log_file" ] || continue 65 line_count=$(wc -l <"$log_file" 2>/dev/null || echo 0) 66 current_line=$(tail -1 "$log_file" 2>/dev/null || true) 67 current_line="${current_line:0:180}" 68 if [ "$line_count" != "$last_count" ] || [ "$current_line" != "$last_line" ]; then 69 log " [kernel] running... lines=$line_count last=$current_line" 70 last_count="$line_count" 71 last_line="$current_line" 72 else 73 log " [kernel] running... lines=$line_count (no new lines yet)" 74 fi 75 done 76 77 if wait "$make_pid"; then 78 return 0 79 fi 80 return $? 81} 82 83# ── ccache setup (persisted via Docker volume at /ccache) ── 84export CCACHE_DIR="${CCACHE_DIR:-/ccache}" 85mkdir -p "$CCACHE_DIR" 86export CCACHE_MAXSIZE="${CCACHE_MAXSIZE:-2G}" 87export CCACHE_COMPRESS=1 88if command -v ccache &>/dev/null; then 89 CC_USE="ccache gcc" 90 log "ccache: enabled (dir=$CCACHE_DIR, max=$CCACHE_MAXSIZE)" 91 ccache -s 2>/dev/null | grep -E "cache size|hit rate" || true 92else 93 CC_USE="gcc" 94fi 95 96log "Building $BUILD_NAME ($GIT_HASH)" 97 98# ══════════════════════════════════════════════ 99# Step 1: Compile ac-native binary 100# ══════════════════════════════════════════════ 101log "Step 1/4: Compiling ac-native..." 102cd "$NATIVE" 103 104# Use cached QuickJS (from Docker image) or download 105if [ ! -f "$BUILD/quickjs/quickjs.h" ]; then 106 if [ -d /cache/quickjs ]; then 107 log " Using cached QuickJS..." 108 cp -a /cache/quickjs-2024-01-13 "$BUILD/" 109 ln -sf quickjs-2024-01-13 "$BUILD/quickjs" 110 else 111 log " Downloading QuickJS..." 112 cd "$BUILD" 113 curl -sL https://bellard.org/quickjs/quickjs-2024-01-13.tar.xz | tar xJ 114 ln -sf quickjs-2024-01-13 quickjs 115 fi 116 cd "$NATIVE" 117fi 118 119make -j$(nproc) CC="${CC_USE}" BUILDDIR="$BUILD" \ 120 BUILD_TS="$BUILD_TS" GIT_HASH="$GIT_HASH" BUILD_NAME="$BUILD_NAME" \ 121 > "$BUILD/.make.log" 2>&1 || true 122 123[ -f "$BUILD/ac-native" ] || { tail -60 "$BUILD/.make.log" >&2; err "Binary compilation failed"; exit 1; } 124log " Binary: $(stat -c%s "$BUILD/ac-native") bytes" 125log " NEEDED libs:" 126readelf -d "$BUILD/ac-native" 2>/dev/null | grep NEEDED | sed 's/.*\[/ /' | sed 's/\]//' 127ldd "$BUILD/ac-native" 2>&1 | grep -i "not found" && err " MISSING LIBS DETECTED" || log " All libs resolved" 128 129# CL build happens after initramfs (step 2) — see below 130 131# ══════════════════════════════════════════════ 132# Step 2: Build initramfs from scratch 133# ══════════════════════════════════════════════ 134log "Step 2/4: Building initramfs..." 135IROOT="$BUILD/initramfs-root" 136rm -rf "$IROOT" 137mkdir -p "$IROOT"/{bin,lib64,lib/firmware/i915,dev,proc,sys,tmp,run,scripts,etc,etc/pki/tls/certs} 138 139# ── 2a: Static busybox (no shared lib deps for shell) ── 140BUSYBOX=$(command -v busybox) 141cp "$BUSYBOX" "$IROOT/bin/busybox" 142for cmd in sh sleep mkdir mount umount cat echo ls cp mv rm ln chmod chown \ 143 date dd find grep head kill ps sed sort tail tee test touch tr wc which \ 144 mktemp printf seq stat basename dirname env expr true false readlink \ 145 realpath rmdir uniq yes tar gzip gunzip hostname id ip modprobe \ 146 mkswap swapon vi df du diff xargs nohup pgrep killall cut whoami awk \ 147 sync poweroff reboot halt mknod udhcpc; do 148 ln -s busybox "$IROOT/bin/$cmd" 149done 150# udhcpc also expected at /sbin/ by wifi.c 151ln -sf ../bin/busybox "$IROOT/sbin/udhcpc" 2>/dev/null || true 152 153# ── 2a2: curl + wget for HTTP fetches (captive portals, OTA, API calls) ── 154CURL_BIN=$(command -v curl) 155if [ -n "$CURL_BIN" ]; then 156 cp "$CURL_BIN" "$IROOT/bin/curl" 157 for lib in $(ldd "$CURL_BIN" 2>/dev/null | grep -oP '/\S+'); do 158 [ -f "$lib" ] && cp -nL "$lib" "$IROOT/lib64/" 2>/dev/null || true 159 done 160fi 161# busybox wget as fallback 162ln -s busybox "$IROOT/bin/wget" 2>/dev/null || true 163# Minimal udhcpc script to configure interface after getting lease 164mkdir -p "$IROOT/usr/share/udhcpc" 165cat > "$IROOT/usr/share/udhcpc/default.script" << 'UDHCPC_SCRIPT' 166#!/bin/sh 167case "$1" in 168 bound|renew) 169 ip addr flush dev "$interface" 170 ip addr add "$ip/${mask:-24}" dev "$interface" 171 [ -n "$router" ] && ip route add default via "$router" dev "$interface" 172 # DNS: use DHCP-provided + fallback to Google/Cloudflare 173 { 174 for d in $dns; do echo "nameserver $d"; done 175 echo "nameserver 8.8.8.8" 176 echo "nameserver 1.1.1.1" 177 } > /etc/resolv.conf 178 ;; 179 deconfig) 180 ip addr flush dev "$interface" 181 ;; 182esac 183UDHCPC_SCRIPT 184chmod +x "$IROOT/usr/share/udhcpc/default.script" 185 186# ── 2b: Init script ── 187cp "$NATIVE/initramfs/init" "$IROOT/init" 188chmod +x "$IROOT/init" 189cp "$NATIVE/initramfs-scripts/"*.sh "$IROOT/scripts/" 2>/dev/null || true 190chmod +x "$IROOT/scripts/"*.sh 2>/dev/null || true 191 192# ── 2c: ac-native binary ── 193cp "$BUILD/ac-native" "$IROOT/ac-native" 194 195# ── 2d: Pieces ── 196cp "$NATIVE/pieces/prompt.mjs" "$IROOT/piece.mjs" 197mkdir -p "$IROOT/pieces" 198cp "$NATIVE/pieces/"*.mjs "$IROOT/pieces/" 2>/dev/null || true 199log " Pieces: $(ls "$IROOT/pieces/" | wc -l)" 200 201# ── 2e: /dev nodes (needed before devtmpfs mounts) ── 202mknod "$IROOT/dev/console" c 5 1 2>/dev/null || true 203mknod "$IROOT/dev/null" c 1 3 2>/dev/null || true 204mknod "$IROOT/dev/tty" c 5 0 2>/dev/null || true 205mknod "$IROOT/dev/zero" c 1 5 2>/dev/null || true 206 207# ── 2f: Dynamic linker ── 208cp -L /lib64/ld-linux-x86-64.so.2 "$IROOT/lib64/" 209 210# ── 2g: Shared libraries (FOLLOW symlinks with cp -L) ── 211# Direct deps of ac-native 212log " Copying shared libraries..." 213for lib in $(ldd "$BUILD/ac-native" 2>/dev/null | grep -oP '/\S+'); do 214 BASENAME=$(basename "$lib") 215 REAL=$(readlink -f "$lib") 216 [ -f "$REAL" ] && cp -L "$REAL" "$IROOT/lib64/$BASENAME" 217done 218 219# SDL3 + Mesa/GPU libs — included for GPU acceleration via dlopen. 220# ac-native runs as a child of init (not PID 1), so if Mesa DRI crashes, 221# init catches the signal and restarts without SDL (AC_NO_SDL=1). 222for lib in libSDL3.so.0 \ 223 libgbm.so.1 libEGL.so.1 libEGL_mesa.so.0 libGLESv2.so.2 \ 224 libGL.so.1 libGLX_mesa.so.0 libGLdispatch.so.0 libglapi.so.0 \ 225 libdrm_intel.so.1 libdrm_amdgpu.so.1; do 226 REAL=$(readlink -f "/lib64/$lib" 2>/dev/null) 227 [ -f "$REAL" ] && cp -L "$REAL" "$IROOT/lib64/$lib" 228done 229 230# Mesa DRI drivers 231if [ -d /lib64/dri ]; then 232 mkdir -p "$IROOT/lib64/dri" 233 for drv in /lib64/dri/i915_dri.so /lib64/dri/iris_dri.so /lib64/dri/kms_swrast_dri.so /lib64/dri/swrast_dri.so; do 234 [ -f "$drv" ] && cp -L "$drv" "$IROOT/lib64/dri/" 235 done 236fi 237# Gallium megadriver 238GALLIUM=$(readlink -f /lib64/libgallium-*.so 2>/dev/null || true) 239[ -f "$GALLIUM" ] && cp -L "$GALLIUM" "$IROOT/lib64/" 240 241# Transitive deps — resolve everything in lib64 242log " Resolving transitive dependencies..." 243ADDED=1 244while [ "$ADDED" -gt 0 ]; do 245 ADDED=0 246 for elf in "$IROOT/lib64/"*.so* "$IROOT/lib64/dri/"*.so* "$IROOT/ac-native"; do 247 [ -f "$elf" ] || continue 248 for needed in $(readelf -d "$elf" 2>/dev/null | grep NEEDED | sed 's/.*\[\(.*\)\]/\1/'); do 249 if [ ! -f "$IROOT/lib64/$needed" ]; then 250 REAL=$(readlink -f "/lib64/$needed" 2>/dev/null) 251 if [ -f "$REAL" ]; then 252 cp -L "$REAL" "$IROOT/lib64/$needed" 253 ADDED=$((ADDED + 1)) 254 fi 255 fi 256 done 257 done 258done 259 260# ── 2g2: Verify DRI driver deps ── 261if [ -f "$IROOT/lib64/dri/iris_dri.so" ]; then 262 log " Checking iris_dri.so dependencies..." 263 MISSING_DRI="" 264 for needed in $(LD_LIBRARY_PATH="$IROOT/lib64" ldd "$IROOT/lib64/dri/iris_dri.so" 2>/dev/null | grep "not found" | awk '{print $1}'); do 265 MISSING_DRI="$MISSING_DRI $needed" 266 # Try to find and copy the missing lib 267 REAL=$(readlink -f "/lib64/$needed" 2>/dev/null || readlink -f "/usr/lib64/$needed" 2>/dev/null) 268 if [ -f "$REAL" ]; then 269 cp -L "$REAL" "$IROOT/lib64/$needed" 270 log " Fixed: $needed" 271 else 272 log " MISSING: $needed (not found on build system)" 273 fi 274 done 275 if [ -n "$MISSING_DRI" ]; then 276 log " iris_dri.so had missing deps:$MISSING_DRI" 277 # Re-run transitive dep resolution after fixing 278 ADDED=1 279 while [ "$ADDED" -gt 0 ]; do 280 ADDED=0 281 for elf in "$IROOT/lib64/"*.so* "$IROOT/lib64/dri/"*.so*; do 282 [ -f "$elf" ] || continue 283 for needed in $(readelf -d "$elf" 2>/dev/null | grep NEEDED | sed 's/.*\[\(.*\)\]/\1/'); do 284 if [ ! -f "$IROOT/lib64/$needed" ]; then 285 REAL=$(readlink -f "/lib64/$needed" 2>/dev/null || readlink -f "/usr/lib64/$needed" 2>/dev/null) 286 if [ -f "$REAL" ]; then 287 cp -L "$REAL" "$IROOT/lib64/$needed" 288 ADDED=$((ADDED + 1)) 289 fi 290 fi 291 done 292 done 293 done 294 else 295 log " iris_dri.so: all deps OK" 296 fi 297 # Final ldd check 298 FINAL_MISSING=$(LD_LIBRARY_PATH="$IROOT/lib64" ldd "$IROOT/lib64/dri/iris_dri.so" 2>&1 | grep -c "not found" || true) 299 log " iris_dri.so final check: ${FINAL_MISSING:-0} missing deps" 300fi 301 302# ── 2h: Verify NO broken symlinks ── 303BROKEN=$(find "$IROOT/lib64/" -type l ! -exec test -e {} \; -print 2>/dev/null | wc -l) 304if [ "$BROKEN" -gt 0 ]; then 305 err " WARNING: $BROKEN broken symlinks found, fixing..." 306 for broken in $(find "$IROOT/lib64/" -type l ! -exec test -e {} \; -print 2>/dev/null); do 307 name=$(basename "$broken") 308 real=$(readlink -f "/lib64/$name" 2>/dev/null || readlink -f "/usr/lib64/$name" 2>/dev/null) 309 if [ -f "$real" ]; then 310 rm -f "$broken" 311 cp "$real" "$broken" 312 else 313 rm -f "$broken" 314 log " Removed unfixable: $name" 315 fi 316 done 317fi 318 319LIB_COUNT=$(ls "$IROOT/lib64/"*.so* 2>/dev/null | wc -l) 320log " Libraries: $LIB_COUNT" 321 322# ── 2i: WiFi tools ── 323for tool in wpa_supplicant wpa_cli iw dhclient rfkill; do 324 SRC_BIN=$(command -v "$tool" 2>/dev/null || true) 325 if [ -n "$SRC_BIN" ]; then cp -L "$SRC_BIN" "$IROOT/bin/"; fi 326 # Copy their deps too 327 if [ -n "$SRC_BIN" ]; then 328 for dep in $(ldd "$SRC_BIN" 2>/dev/null | grep -oP '/\S+'); do 329 BASENAME=$(basename "$dep") 330 [ ! -f "$IROOT/lib64/$BASENAME" ] && cp -L "$(readlink -f "$dep")" "$IROOT/lib64/$BASENAME" 2>/dev/null || true 331 done 332 fi 333done 334 335# ── 2i2: Disk/EFI tools (for HD install + OTA flash) ── 336for tool in sfdisk mkfs.vfat efibootmgr partprobe; do 337 SRC_BIN=$(command -v "$tool" 2>/dev/null || true) 338 if [ -n "$SRC_BIN" ]; then 339 cp -L "$SRC_BIN" "$IROOT/bin/" 340 for dep in $(ldd "$SRC_BIN" 2>/dev/null | grep -oP '/\S+'); do 341 BASENAME=$(basename "$dep") 342 [ ! -f "$IROOT/lib64/$BASENAME" ] && cp -L "$(readlink -f "$dep")" "$IROOT/lib64/$BASENAME" 2>/dev/null || true 343 done 344 fi 345done 346 347# ── 2j: Firmware (trimmed to common Intel WiFi + GPU chips) ── 348log " Copying firmware..." 349FWDIR="" 350for d in /usr/lib/firmware /lib/firmware; do 351 [ -d "$d/i915" ] && FWDIR="$d" && break 352done 353if [ -n "$FWDIR" ]; then 354 # WiFi — only common Intel chip families (covers ThinkPad, NUC, Surface) 355 # Each family: 3160, 7260, 7265, 8000, 8265, 9000, 9260, ax200, ax201, ax210, ax211, be200 356 for chip in 3160 3168 7260 7265 8000C 8265 9000 9260 \ 357 ax200 ax201 ax210 ax211 be200 gl; do 358 for fw in "$FWDIR"/iwlwifi-${chip}*.ucode "$FWDIR"/iwlwifi-${chip}*.ucode.zst "$FWDIR"/iwlwifi-${chip}*.ucode.xz; do 359 [ -f "$fw" ] && cp -L "$fw" "$IROOT/lib/firmware/" 360 done 361 done 362 # Regulatory 363 cp -L "$FWDIR/regulatory.db" "$IROOT/lib/firmware/" 2>/dev/null || true 364 cp -L "$FWDIR/regulatory.db.p7s" "$IROOT/lib/firmware/" 2>/dev/null || true 365 # GPU — only KBL/SKL/CFL/ICL/TGL/ADL/RPL (covers ~95% of target hardware) 366 for pattern in kbl skl cfl icl tgl adl dg1 rkl rpl mtl; do 367 for fw in "$FWDIR"/i915/${pattern}_*; do 368 [ -f "$fw" ] && cp -L "$fw" "$IROOT/lib/firmware/i915/" 369 done 370 done 371 # DMC firmware (display power management) 372 for fw in "$FWDIR"/i915/*_dmc_*.bin "$FWDIR"/i915/*_dmc_*.bin.zst; do 373 [ -f "$fw" ] && cp -L "$fw" "$IROOT/lib/firmware/i915/" 374 done 375else 376 log " WARNING: No firmware directory found!" 377fi 378# Decompress any .zst or .xz files 379for zst in "$IROOT/lib/firmware/"*.zst "$IROOT/lib/firmware/i915/"*.zst; do 380 [ -f "$zst" ] && zstd -d --rm "$zst" 2>/dev/null || true 381done 382for xzf in "$IROOT/lib/firmware/"*.xz "$IROOT/lib/firmware/i915/"*.xz; do 383 [ -f "$xzf" ] && xz -d "$xzf" 2>/dev/null || true 384done 385FW_COUNT=$(find "$IROOT/lib/firmware" -type f | wc -l) 386FW_SIZE=$(du -sh "$IROOT/lib/firmware" | cut -f1) 387log " Firmware: $FW_COUNT files ($FW_SIZE)" 388 389# ── 2k: SSL certs ── 390cp /etc/pki/tls/certs/ca-bundle.crt "$IROOT/etc/pki/tls/certs/" 2>/dev/null || true 391 392# ── 2l: ALSA config ── 393if [ -d /usr/share/alsa ]; then 394 mkdir -p "$IROOT/usr/share" 395 cp -a /usr/share/alsa "$IROOT/usr/share/" 2>/dev/null || true 396fi 397 398# ── 2m: /etc/group and /etc/passwd ── 399echo "root:x:0:" > "$IROOT/etc/group" 400echo "root:x:0:root" > "$IROOT/etc/passwd" 401 402# ── 2n: Claude Code ── 403CLAUDE_BIN="" 404for p in /claude-bin /usr/local/bin/claude-native /usr/local/bin/claude /root/.local/share/claude/versions/* /home/me/.local/share/claude/versions/*; do 405 [ -f "$p" ] && CLAUDE_BIN="$p" && break 406done 407if [ -n "$CLAUDE_BIN" ]; then 408 cp "$CLAUDE_BIN" "$IROOT/bin/claude" 409 chmod +x "$IROOT/bin/claude" 410 # Copy its shared libs 411 for lib in $(ldd "$CLAUDE_BIN" 2>/dev/null | grep -oP '/\S+'); do 412 [ -f "$lib" ] && cp -nL "$lib" "$IROOT/lib64/" 2>/dev/null || true 413 done 414 log " Claude Code: $(du -sh "$IROOT/bin/claude" | cut -f1)" 415else 416 log " Claude Code: not available (mount with -v /path/to/claude:/claude-bin)" 417fi 418 419# ── 2o: KidLisp bundle ── 420if command -v npx &>/dev/null && [ -f "$SRC/system/public/aesthetic.computer/lib/kidlisp.mjs" ]; then 421 log " Bundling KidLisp..." 422 cd "$SRC" 423 npx esbuild system/public/aesthetic.computer/lib/kidlisp.mjs \ 424 --bundle --format=iife --global-name=KidLispModule --platform=neutral \ 425 --external:https --external:http --external:net --external:fs --external:path \ 426 --outfile=/tmp/kidlisp-bundle.js 2>&1 || true 427 if [ -f /tmp/kidlisp-bundle.js ]; then 428 mkdir -p "$IROOT/jslib" 429 cp /tmp/kidlisp-bundle.js "$IROOT/jslib/" 430 fi 431 cd "$NATIVE" 432fi 433 434# ── 2p: ES module lib files for pieces (clock.mjs needs these) ── 435# These are pure JS with no DOM/browser deps — work in QuickJS as-is. 436# The module loader resolves "../lib/X.mjs" → "/lib/X.mjs" in the initramfs. 437mkdir -p "$IROOT/lib" 438for libmjs in melody-parser.mjs notepat-convert.mjs note-colors.mjs num.mjs; do 439 SRC_LIB="$SRC/system/public/aesthetic.computer/lib/$libmjs" 440 if [ -f "$SRC_LIB" ]; then 441 cp "$SRC_LIB" "$IROOT/lib/$libmjs" 442 log " Bundled lib: $libmjs ($(stat -c%s "$SRC_LIB") bytes)" 443 fi 444done 445 446# ── 2q: Web pieces that work natively (clock.mjs unmodified) ── 447for webpiece in clock.mjs; do 448 SRC_PIECE="$SRC/system/public/aesthetic.computer/disks/$webpiece" 449 if [ -f "$SRC_PIECE" ]; then 450 cp "$SRC_PIECE" "$IROOT/pieces/$webpiece" 451 log " Bundled web piece: $webpiece ($(stat -c%s "$SRC_PIECE") bytes)" 452 fi 453done 454 455# ── Final verification ── 456BROKEN_FINAL=$(find "$IROOT" -type l ! -exec test -e {} \; -print 2>/dev/null | wc -l) 457TOTAL_FILES=$(find "$IROOT" -type f | wc -l) 458log " Initramfs: $TOTAL_FILES files, $BROKEN_FINAL broken symlinks" 459[ "$BROKEN_FINAL" -gt 0 ] && err " WARNING: broken symlinks remain!" 460 461# Write build metadata (C variant by default, CL overrides below) 462mkdir -p "$IROOT/etc" 463printf '%s\n%s\n%s\nc\n' "$BUILD_NAME" "$GIT_HASH" "$BUILD_TS" > "$IROOT/etc/ac-build" 464 465# ── Embed SBCL + Swank for live CL development ── 466log "Step 2b: Embedding SBCL + Swank..." 467 468# Build libquickjs.so for CL CFFI bindings 469QJSDIR="/cache/quickjs-2024-01-13" 470log " Building libquickjs.so..." 471gcc -shared -fPIC -O2 -Wl,-soname,libquickjs.so \ 472 -o /lib64/libquickjs.so \ 473 "$QJSDIR/quickjs.c" "$QJSDIR/libunicode.c" \ 474 "$QJSDIR/libregexp.c" "$QJSDIR/cutils.c" "$QJSDIR/libbf.c" \ 475 '-DCONFIG_VERSION="0.8.0"' -lm 2>&1 || { err "libquickjs.so build failed"; } 476 477CL_DIR="$NATIVE/cl" 478log " Building libquickjs-shim.so..." 479gcc -shared -fPIC -O2 -Wl,-soname,libquickjs-shim.so \ 480 -o /lib64/libquickjs-shim.so \ 481 "$CL_DIR/quickjs-shim.c" \ 482 -I"$QJSDIR" -L/lib64 -lquickjs -lm 2>&1 || { err "shim build failed"; } 483 484ldconfig 2>/dev/null || true 485 486# Copy shared libs into initramfs 487cp /lib64/libquickjs.so "$IROOT/lib64/" 488cp /lib64/libquickjs-shim.so "$IROOT/lib64/" 489for lib in /lib64/libzstd.so*; do 490 [ -f "$lib" ] && cp -L "$lib" "$IROOT/lib64/" 2>/dev/null 491done 492 493# Build SBCL Swank server image (standalone binary with Swank preloaded) 494export LD_LIBRARY_PATH="/lib64:${LD_LIBRARY_PATH:-}" 495log " Building SBCL Swank image..." 496sbcl --non-interactive \ 497 --eval '(load "/opt/quicklisp/setup.lisp")' \ 498 --eval '(require :asdf)' \ 499 --eval "(push #P\"$CL_DIR/\" asdf:*central-registry*)" \ 500 --eval '(asdf:load-system :ac-native)' \ 501 --eval "(sb-ext:save-lisp-and-die \"$BUILD/ac-swank\" 502 :toplevel #'ac-native:main 503 :executable t 504 :compression t)" \ 505 2>&1 || { err "SBCL Swank build failed (non-fatal)"; } 506 507if [ -f "$BUILD/ac-swank" ]; then 508 cp "$BUILD/ac-swank" "$IROOT/ac-swank" 509 chmod +x "$IROOT/ac-swank" 510 log " SBCL Swank: $(stat -c%s "$BUILD/ac-swank") bytes" 511else 512 log " SBCL Swank: skipped (build failed, C-only build)" 513fi 514 515# Copy CL pieces (only runnable .lisp pieces from pieces/ dir, not cl/ library) 516if ls "$NATIVE/pieces/"*.lisp 1>/dev/null 2>&1; then 517 cp "$NATIVE/pieces/"*.lisp "$IROOT/pieces/" 518fi 519CL_PIECES=$(ls "$IROOT/pieces/"*.lisp 2>/dev/null | wc -l) 520log " CL pieces: $CL_PIECES" 521 522# ══════════════════════════════════════════════ 523# Step 3: Pack initramfs (cpio + lz4) 524# ══════════════════════════════════════════════ 525log "Step 3/4: Packing initramfs..." 526cd "$IROOT" 527find . -print0 | cpio --null -ov --format=newc 2>/dev/null | lz4 -l -9 -f - "$BUILD/initramfs.cpio.lz4" 528INITRAMFS_SIZE=$(stat -c%s "$BUILD/initramfs.cpio.lz4") 529log " Initramfs: $((INITRAMFS_SIZE / 1048576))MB compressed" 530 531# ══════════════════════════════════════════════ 532# Step 4: Build kernel with embedded initramfs 533# ══════════════════════════════════════════════ 534log "Step 4/4: Building kernel..." 535LINUX_DIR="$BUILD/linux-$KVER" 536 537# Use cached kernel source or download 538if [ ! -f "$LINUX_DIR/Makefile" ]; then 539 if [ -d "/cache/linux-$KVER" ]; then 540 log " Using cached Linux $KVER..." 541 cp -a "/cache/linux-$KVER" "$BUILD/" 542 else 543 log " Downloading Linux $KVER..." 544 cd "$BUILD" 545 curl -sL "https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-${KVER}.tar.xz" | tar xJ 546 fi 547fi 548 549# Copy config 550cp "$NATIVE/kernel/config-minimal" "$LINUX_DIR/.config" 551 552# Stage built-in firmware blobs referenced by CONFIG_EXTRA_FIRMWARE 553# into a container-local directory and rewrite CONFIG_EXTRA_FIRMWARE_DIR. 554FIRMWARE_ABS="$BUILD/firmware" 555mkdir -p "$FIRMWARE_ABS" 556 557HOST_FWDIR="" 558for d in /usr/lib/firmware /lib/firmware; do 559 if [ -d "$d" ]; then 560 HOST_FWDIR="$d" 561 break 562 fi 563done 564 565copy_builtin_fw_blob() { 566 local rel="$1" 567 local src_base="$2" 568 local dst="$FIRMWARE_ABS/$rel" 569 mkdir -p "$(dirname "$dst")" 570 if [ -f "$src_base/$rel" ]; then 571 cp -L "$src_base/$rel" "$dst" 572 return 0 573 fi 574 if [ -f "$src_base/$rel.zst" ]; then 575 zstd -d "$src_base/$rel.zst" -o "$dst" 2>/dev/null && return 0 576 fi 577 if [ -f "$src_base/$rel.xz" ]; then 578 xz -dc "$src_base/$rel.xz" >"$dst" 2>/dev/null && return 0 579 fi 580 return 1 581} 582 583FW_LIST=$(sed -n 's/^CONFIG_EXTRA_FIRMWARE="\([^"]*\)"/\1/p' "$LINUX_DIR/.config" | head -1) 584if [ -n "$FW_LIST" ]; then 585 if [ -z "$HOST_FWDIR" ]; then 586 err "Kernel config requests built-in firmware but no /usr/lib/firmware or /lib/firmware directory was found." 587 exit 1 588 fi 589 590 missing_fw="" 591 for fw in $FW_LIST; do 592 if ! copy_builtin_fw_blob "$fw" "$HOST_FWDIR"; then 593 missing_fw="$missing_fw $fw" 594 fi 595 done 596 597 if [ -n "$missing_fw" ]; then 598 err "Missing built-in firmware blobs:$missing_fw" 599 err "Looked under: $HOST_FWDIR (including .zst/.xz variants)" 600 exit 1 601 fi 602 603 sed -i "s|^CONFIG_EXTRA_FIRMWARE_DIR=.*|CONFIG_EXTRA_FIRMWARE_DIR=\"$FIRMWARE_ABS\"|" "$LINUX_DIR/.config" 604 log " Built-in firmware dir: $FIRMWARE_ABS" 605 log " Built-in firmware files: $FW_LIST" 606fi 607 608# Copy initramfs into kernel tree 609cp "$BUILD/initramfs.cpio.lz4" "$LINUX_DIR/initramfs.cpio.lz4" 610 611# Configure — force-disable bloated GPU drivers that olddefconfig enables 612cd "$LINUX_DIR" 613KCFG_LOG="$BUILD/kernel-olddefconfig.log" 614make olddefconfig >"$KCFG_LOG" 2>&1 || { err "Kernel olddefconfig failed"; tail -80 "$KCFG_LOG" >&2; exit 1; } 615tail -3 "$KCFG_LOG" || true 616 617# Strip GPU drivers that Fedora defaults enable (they cause KALLSYMS overflow) 618scripts/config --disable DRM_AMDGPU 619scripts/config --disable DRM_NOUVEAU 620scripts/config --disable DRM_RADEON 621scripts/config --disable DRM_QXL 622scripts/config --disable DRM_BOCHS 623scripts/config --disable DRM_CIRRUS_QEMU 624scripts/config --disable DRM_VIRTIO_GPU 625scripts/config --enable DRM_SIMPLEDRM 626make olddefconfig >>"$KCFG_LOG" 2>&1 || { err "Kernel olddefconfig (post-config) failed"; tail -120 "$KCFG_LOG" >&2; exit 1; } 627tail -1 "$KCFG_LOG" || true 628 629# With ccache, skip make clean — stale objects get cache misses anyway, 630# and clean destroys the build tree that ccache relies on for hits. 631# Without ccache, clean to avoid config mismatch errors. 632if ! command -v ccache &>/dev/null; then 633 make clean 2>/dev/null || true 634fi 635 636# Build 637log " Compiling (${KERNEL_JOBS} cores)..." 638KERNEL_LOG="$BUILD/kernel-build.log" 639if ! run_make_with_heartbeat "$KERNEL_LOG" make -j"${KERNEL_JOBS}" CC="${CC_USE}" KALLSYMS_EXTRA_PASS=1 bzImage; then 640 err "Kernel compile failed while building bzImage (parallel pass)." 641 show_kernel_error_context "$KERNEL_LOG" 642 if [ "${KERNEL_JOBS}" -gt 1 ]; then 643 err "Retrying kernel build in serial mode (-j1, V=1) for deterministic diagnostics..." 644 make clean 2>/dev/null || true 645 KERNEL_LOG_RETRY="$BUILD/kernel-build-retry.log" 646 if ! run_make_with_heartbeat "$KERNEL_LOG_RETRY" make -j1 V=1 CC="${CC_USE}" KALLSYMS_EXTRA_PASS=1 bzImage; then 647 err "Kernel compile failed again in serial retry." 648 show_kernel_error_context "$KERNEL_LOG_RETRY" 649 exit 1 650 fi 651 KERNEL_LOG="$KERNEL_LOG_RETRY" 652 log " Serial retry succeeded." 653 else 654 exit 1 655 fi 656fi 657tail -3 "$KERNEL_LOG" || true 658 659# Copy output 660if [ ! -f arch/x86/boot/bzImage ]; then 661 err "Kernel build completed but arch/x86/boot/bzImage is missing." 662 show_kernel_error_context "$KERNEL_LOG" 663 exit 1 664fi 665cp arch/x86/boot/bzImage "$BUILD/vmlinuz" 666cp arch/x86/boot/bzImage "$OUT/vmlinuz" 2>/dev/null || true 667 668VMLINUZ_SIZE=$(stat -c%s "$BUILD/vmlinuz") 669SHA=$(sha256sum "$BUILD/vmlinuz" | awk '{print $1}') 670 671# ══════════════════════════════════════════════ 672# Step 4b: Build slim kernel (no embedded initramfs) for Mac EFI boot 673# ══════════════════════════════════════════════ 674log "Step 4b: Building slim kernel for Mac..." 675sed -i 's|^CONFIG_INITRAMFS_SOURCE=.*|CONFIG_INITRAMFS_SOURCE=""|' .config 676make olddefconfig >>"$KCFG_LOG" 2>&1 || { err "Kernel olddefconfig failed before slim build"; tail -120 "$KCFG_LOG" >&2; exit 1; } 677if ! command -v ccache &>/dev/null; then make clean 2>/dev/null || true; fi 678rm -f usr/initramfs_data.o usr/.initramfs_data.o.cmd 679if run_make_with_heartbeat "$BUILD/kernel-slim.log" make -j"${KERNEL_JOBS}" CC="${CC_USE}" bzImage; then 680 cp arch/x86/boot/bzImage "$BUILD/vmlinuz-slim" 681 cp arch/x86/boot/bzImage "$OUT/vmlinuz-slim" 2>/dev/null || true 682 SLIM_SIZE=$(stat -c%s "$BUILD/vmlinuz-slim") 683 log " Slim kernel: $((SLIM_SIZE / 1048576))MB" 684else 685 err "Slim kernel build failed (non-fatal)" 686fi 687# Restore config for any subsequent builds 688sed -i 's|^CONFIG_INITRAMFS_SOURCE=.*|CONFIG_INITRAMFS_SOURCE="initramfs.cpio.lz4"|' .config 689make olddefconfig >>"$KCFG_LOG" 2>&1 || { err "Kernel olddefconfig failed while restoring initramfs config"; tail -120 "$KCFG_LOG" >&2; exit 1; } 690 691# Export initramfs as gzip (for systemd-boot on Mac) 692log " Packing initramfs.cpio.gz..." 693lz4 -d "$BUILD/initramfs.cpio.lz4" -c 2>/dev/null | gzip -c > "$BUILD/initramfs.cpio.gz" 694cp "$BUILD/initramfs.cpio.gz" "$OUT/initramfs.cpio.gz" 2>/dev/null || true 695log " initramfs.cpio.gz: $(($(stat -c%s "$BUILD/initramfs.cpio.gz") / 1048576))MB" 696 697# ══════════════════════════════════════════════ 698# Step 5: Generate UEFI-bootable ISO 699# ══════════════════════════════════════════════ 700log "Step 5: Building ISO..." 701ISO_DIR="$BUILD/iso-root" 702EFI_IMG="$BUILD/efi.img" 703ISO_OUT="$BUILD/ac-os.iso" 704CONFIG_TMP="$BUILD/identity-config.json" 705 706rm -rf "$ISO_DIR" "$EFI_IMG" "$CONFIG_TMP" 707 708ac_media_write_identity_config "$CONFIG_TMP" 709ac_media_stage_boot_tree "$ISO_DIR" "$BUILD/vmlinuz" "$CONFIG_TMP" 710ac_media_create_fat_image "$ISO_DIR" "$EFI_IMG" "AC_NATIVE" 711 712# Build hybrid ISO with xorriso (EFI boot via El Torito + GPT for dd/USB) 713# Uses the same chainloader-first staged tree as ac-os generate_template_iso(). 714if command -v xorriso &>/dev/null; then 715 ac_media_build_hybrid_iso "$ISO_DIR" "$EFI_IMG" "$ISO_OUT" "AC_NATIVE" 716else 717 # Fallback: raw EFI disk image (dd-able) 718 log " No xorriso — creating raw disk image" 719 cp "$EFI_IMG" "$ISO_OUT" 720fi 721 722rm -f "$CONFIG_TMP" 723 724if [ -f "$ISO_OUT" ]; then 725 ISO_SIZE=$(stat -c%s "$ISO_OUT") 726 ISO_SHA=$(sha256sum "$ISO_OUT" | awk '{print $1}') 727 cp "$ISO_OUT" "$OUT/ac-os.iso" 2>/dev/null || true 728 log " ISO: $((ISO_SIZE / 1048576))MB (sha256: ${ISO_SHA:0:16}...)" 729else 730 log " ISO generation failed — vmlinuz still available" 731fi 732 733log "" 734log "═══════════════════════════════════════════" 735log " Build complete: $BUILD_NAME" 736log " Kernel: $((VMLINUZ_SIZE / 1048576))MB" 737log " SHA256: $SHA" 738if [ -f "$ISO_OUT" ]; then 739 log " ISO: $((ISO_SIZE / 1048576))MB" 740fi 741if command -v ccache &>/dev/null; then 742 CCACHE_HIT=$(ccache -s 2>/dev/null | grep "cache hit rate" | head -1 || true) 743 CCACHE_SIZE=$(ccache -s 2>/dev/null | grep "cache size" | head -1 || true) 744 log " ccache: ${CCACHE_HIT:-n/a} | ${CCACHE_SIZE:-n/a}" 745fi 746log "═══════════════════════════════════════════" 747 748# Write metadata 749cat > "$OUT/build.json" << EOF 750{ 751 "name": "$BUILD_NAME", 752 "git_hash": "$GIT_HASH", 753 "build_ts": "$BUILD_TS", 754 "size": $VMLINUZ_SIZE, 755 "sha256": "$SHA", 756 "iso_size": ${ISO_SIZE:-0}, 757 "iso_sha256": "${ISO_SHA:-}", 758 "handle": "$HANDLE" 759} 760EOF