From 7958276c9f0d230053520ba20f2a00f5ecda18e4 Mon Sep 17 00:00:00 2001 From: salmonik Date: Sun, 1 Nov 2026 15:53:29 +0100 Subject: scripts: rebuild the dev loop around build.sh/run.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `make` did not parse at all (missing separator at line 139), so every make target was dead. The folder also carried three overlapping launchers (boot.sh, qemu.sh, boot-image.sh) plus a fourth partial one in the Makefile, `cargo build --package rsdos` copy-pasted eight times, the QEMU invocation copy-pasted per script, and the test helpers (fail/wait_log/wait_qemu_exit/qemu_hmp) duplicated in five tests. New layout: - scripts/lib.sh: single config (image, OVMF, mem/smp), the one cargo invocation (build_all), the one QEMU shape (qemu_args), and the test harness (test_boot, wait_log, wait_qemu_exit, qemu_hmp, sendkeys) - scripts/build.sh: kernel + every bins/* in one pass - scripts/mksystem.sh: freshness-aware image build that now stages every bins/*.efi into /bin/cli|gui (they were never copied before) - scripts/run.sh: build + stage + boot in one command; --headless, --debug (GDB stub), --monitor (HMP socket), --fresh, --mem/--smp/ --bios/--image; on Wayland it auto-selects XWayland plus grab-on-hover so the mouse can be captured (Ctrl+Alt+G toggles) - scripts/tests/: the five automated tests on the shared harness - deleted: boot.sh, qemu.sh, boot-image.sh, lib-boot-args.sh, build-image-release.sh, build-bootloader.sh (rsboot chain dropped) The Makefile is now a thin wrapper: make run, make test, make system, make inspect, make help. Two-command flow: ./scripts/build.sh then ./scripts/run.sh (run.sh alone also works). Stale test assumptions fixed so the suite is green again: test-gfx waited for "gfx: check" while the shell prints "gfx check: PASS"; test-mouse/test-msg hardcoded msgrecv = task 3 (wm-input took slot 3 — ids are parsed from the log now); the queue-full probe targeted task 0, whose exec pump drains its own queue as completion codes (moved to the cursor task, which never recvs). Verified in QEMU: make test -> test-disk, test-gfx, test-mouse, test-msg, test-fault all PASS. --- scripts/mksystem.sh | 144 +++++++++++++++++++++++++++------------------------- 1 file changed, 75 insertions(+), 69 deletions(-) (limited to 'scripts/mksystem.sh') diff --git a/scripts/mksystem.sh b/scripts/mksystem.sh index 044c86c..053fb3b 100755 --- a/scripts/mksystem.sh +++ b/scripts/mksystem.sh @@ -2,44 +2,49 @@ # # scripts/mksystem.sh — assemble the single dd-able rs-dos system image # -# Replaces the old two-source setup (vvfat `esp/` directory for boot + -# separate /tmp/disk.img for data). One FAT32 image holds EVERYTHING: +# One FAT32 image holds EVERYTHING (boot medium and data medium are the +# same disk, mounted by the kernel at /disk//): # -# \bzImage kernel (rsbl requires this exact path) -# \initramfs.cpio.gz empty initramfs (rsbl requires it) -# \EFI\BOOT\BOOTX64.EFI boot entry: the kernel (default) or rsboot +# \EFI\BOOT\BOOTX64.EFI the kernel — the firmware boots it directly +# \bzImage kernel copy (rsbl's expected path, real hw) +# \initramfs.cpio.gz empty initramfs (rsbl requires the path) +# \bin\cli\*.efi terminal binaries (invoked by the shell) +# \bin\gui\*.efi windowed binaries (launched with no shell) # \etc\motd.txt seed config -# \bin\cli\ \bin\gui\ binary tree (empty until Phase 2/4 migrate tools) # \*.txt seed files (seed/) # -# The firmware boots from this image and the kernel mounts the same -# volume at /disk// — boot medium and data medium are one disk. +# Freshness: an existing image is only rebuilt when a kernel/binary/seed +# file is newer than it, so files written at runtime survive across +# boots. --force always re-creates (wipes runtime data). # # Usage: -# ./scripts/mksystem.sh [options] [output.img] # default /tmp/system.img +# ./scripts/mksystem.sh [options] [output.img] default: $SYSTEM_IMG # # Options: -# --force rebuild even if the image exists (Makefile stamps handle -# normal freshness; without --force an existing image is kept, -# so user files survive across reboots) +# --force rebuild even if the image is up to date # --size NM image size in MB (default 64) -# --chain \EFI\BOOT\BOOTX64.EFI = rsboot bootloader (full chain: -# firmware → rsboot → rsnix). Default is the kernel itself -# (direct: firmware → rsnix), matching the old dev loop. -# --gpt wrap the FAT volume in a GPT partition table (for dd-ing -# to real USB sticks whose firmware refuses superfloppies) # --label NAME FAT volume label (default RSDOS) +# --gpt wrap the FAT volume in a GPT partition table (for +# dd-ing to real USB sticks whose firmware refuses +# superfloppies) +# --help show this help +# +# Needs: dosfstools (mkfs.fat), mtools (mcopy/mmd), cargo (kernel+bins +# built via scripts/build.sh when missing). # set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -PROJECT_DIR="$(dirname "$SCRIPT_DIR")" -cd "$PROJECT_DIR" +# Absolute self path: lib.sh cd's to the project root, so a relative $0 +# would no longer resolve by the time --help prints the header. +SELF="$SCRIPT_DIR/$(basename "$0")" + +# shellcheck source=lib.sh +source "$SCRIPT_DIR/lib.sh" -IMG="/tmp/system.img" +IMG="$SYSTEM_IMG" SIZE_MB=64 FORCE=0 -CHAIN=0 GPT=0 LABEL="RSDOS" @@ -52,18 +57,15 @@ while [ $# -gt 0 ]; do shift SIZE_MB="$1" ;; - --chain) - CHAIN=1 - ;; - --gpt) - GPT=1 - ;; --label) shift LABEL="$1" ;; + --gpt) + GPT=1 + ;; --help | -h) - head -30 "$0" | grep "^#" | sed 's/^# \?//' + awk 'NR > 1 && /^#/ { sub(/^# ?/, ""); print; next } NR > 1 { exit }' "$SELF" exit 0 ;; -*) @@ -77,15 +79,9 @@ while [ $# -gt 0 ]; do shift done -TARGET=x86_64-unknown-uefi -PROFILE=release -OUT_DIR="target/$TARGET/$PROFILE" -KERNEL="$OUT_DIR/rsdos.efi" -BZIMAGE="esp/bzImage" -INITRD="esp/initramfs.cpio.gz" -BOOTLOADER="build/bootloader/BOOTX64.EFI" +SEED_DIR="$PROJECT_DIR/seed" -# ---- checks ---- +# ---- checks --------------------------------------------------------------- for cmd in mkfs.fat mcopy mmd; do if ! command -v "$cmd" >/dev/null 2>&1; then echo "mksystem: $cmd not found (install dosfstools + mtools)" >&2 @@ -101,29 +97,26 @@ if [ "$GPT" -eq 1 ]; then done fi -if [ -f "$IMG" ] && [ "$FORCE" -eq 0 ]; then - echo "mksystem: $IMG already exists (use --force to re-create)" - exit 0 +# ---- build (only when artifacts are missing; build.sh is the only cargo +# invocation, so an already-compiled tree costs nothing) -------------------- +if [ ! -f "$KERNEL" ] || ! ls "$OUT_DIR"/*.efi >/dev/null 2>&1; then + "$SCRIPT_DIR/build.sh" fi -# ---- build kernel ---- -if [ ! -f "$KERNEL" ]; then - echo "mksystem: building kernel..." - cargo build --release --package rsdos --target "$TARGET" -fi - -# ---- build bootloader (chain mode only) ---- -if [ "$CHAIN" -eq 1 ] && [ ! -f "$BOOTLOADER" ]; then - echo "mksystem: building bootloader..." - "$SCRIPT_DIR/build-bootloader.sh" +# ---- freshness stamp ------------------------------------------------------ +newest="" +for f in "$OUT_DIR"/*.efi "$SEED_DIR"/*; do + [ -f "$f" ] || continue + if [ -z "$newest" ] || [ "$f" -nt "$newest" ]; then + newest="$f" + fi +done +if [ "$FORCE" -eq 0 ] && [ -f "$IMG" ] && { [ -z "$newest" ] || [ ! "$newest" -nt "$IMG" ]; }; then + echo "mksystem: up to date: $IMG (use --force to re-create)" + exit 0 fi -# ---- deploy kernel artifacts to esp/ (shared with the old layout) ---- -mkdir -p esp/EFI/BOOT -cp "$KERNEL" "$BZIMAGE" -[ -f "$INITRD" ] || printf '' | gzip >"$INITRD" - -# ---- create the FAT volume ---- +# ---- create the FAT volume ------------------------------------------------ rm -f "$IMG" SIZE_BLOCKS=$((SIZE_MB * 1024)) # mkfs.fat counts 1024-byte blocks echo "mksystem: creating $IMG (${SIZE_MB}M FAT32, label $LABEL)" @@ -131,34 +124,47 @@ mkfs.fat -C -F 32 -n "$LABEL" "$IMG" "$SIZE_BLOCKS" >/dev/null echo "mksystem: staging system tree" mmd -i "$IMG" ::/EFI ::/EFI/BOOT ::/etc ::/bin ::/bin/cli ::/bin/gui -mcopy -i "$IMG" "$BZIMAGE" ::/bzImage + +# The firmware entry point and rsbl's kernel path are the same file. +mcopy -i "$IMG" "$KERNEL" ::/EFI/BOOT/BOOTX64.EFI +mcopy -i "$IMG" "$KERNEL" ::/bzImage +INITRD="$(mktemp -t rsdos-initrd.XXXXXX)" +trap 'rm -f "$INITRD"' EXIT +printf '' | gzip >"$INITRD" mcopy -i "$IMG" "$INITRD" ::/initramfs.cpio.gz -if [ "$CHAIN" -eq 1 ]; then - mcopy -i "$IMG" "$BOOTLOADER" ::/EFI/BOOT/BOOTX64.EFI - echo " \\EFI\\BOOT\\BOOTX64.EFI <- rsboot (full chain)" -else - mcopy -i "$IMG" "$KERNEL" ::/EFI/BOOT/BOOTX64.EFI - echo " \\EFI\\BOOT\\BOOTX64.EFI <- kernel (direct)" -fi -echo " \\bzImage \\initramfs.cpio.gz \\bin{\\cli,\\gui} \\etc" +echo " \\EFI\\BOOT\\BOOTX64.EFI \\bzImage \\initramfs.cpio.gz" + +# External binaries: everything under bins/ was compiled into $OUT_DIR. +# Launch surface decides the directory (ADR 0006): GUI_BINS -> /bin/gui, +# everything else -> /bin/cli. +for src in "$OUT_DIR"/*.efi; do + [ -f "$src" ] || continue + name="$(basename "$src" .efi)" + [ "$name" = "rsdos" ] && continue # the kernel is not a /bin binary + case " $GUI_BINS " in + *" $name "*) kind="gui" ;; + *) kind="cli" ;; + esac + mcopy -i "$IMG" "$src" "::/bin/$kind/$name.efi" + printf ' \\bin\\%s\\%s.efi\n' "$kind" "$name" +done # Seed files: motd lives in \etc, everything else at the volume root. -SEED_DIR="$PROJECT_DIR/seed" if [ -d "$SEED_DIR" ]; then for f in "$SEED_DIR"/*; do [ -f "$f" ] || continue name="$(basename "$f")" if [ "$name" = "motd.txt" ]; then mcopy -i "$IMG" "$f" ::/etc/"$name" - echo " \\etc\\$name" + printf ' \\etc\\%s\n' "$name" else mcopy -i "$IMG" "$f" ::/"$name" - echo " \\$name" + printf ' \\%s\n' "$name" fi done fi -# ---- optional GPT wrapper (real-hardware dd target) ---- +# ---- optional GPT wrapper (real-hardware dd target) ----------------------- if [ "$GPT" -eq 1 ]; then GPT_START=2048 # 1 MiB offset (standard alignment) GPT_IMG="$(mktemp)" @@ -179,6 +185,6 @@ fi echo "" echo "=== Done: $IMG ===" ls -lh "$IMG" -echo "Boot with: make run (or: make boot-image)" +echo "Boot with: ./scripts/run.sh (or: make run)" [ "$GPT" -eq 0 ] && echo "USB dd tip: add --gpt for firmwares that refuse superfloppies" exit 0 -- cgit v1.2.3