From 7d2eca8271df926117bee9387f849ca087e7d7b4 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Sun, 25 Oct 2026 19:28:03 +0100 Subject: [PATCH] Faster ISO builds (69 s to 9 s) and a faster write.sh check make iso recompiled the kernel and every command each time: iso and ramroot wiped build/kernel, build/lib and build/cmd to make sure nothing was compiled for the wrong RAMROOT setting. Only kernel/proc/proc.c and kernel/dev/ramdisk.c read RAMDISK_ROOT, so only their objects go now. Even then a no-op make iso took 69 s, because sysroot.stamp lists every staged file with its mtime and two things re-copied files on every run: the plain cp in etc-install (now cp -p) and fastfetch's install (now install -C). A changed stamp rebuilds the Alpine root, the root image and its zstd -19 compression. A no-op, and a one-file kernel change, now take about 9 s. write.sh read the stick back with a buffered cmp, slowly and possibly from the page cache. It now reads 4 MB at a time with iflag=direct, which checks the stick itself; --no-verify skips the check. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_015f73FesGZGYPkmDvUcj6QX --- Makefile | 27 ++++++++++++++++++--------- ports/fastfetch/Makefile | 2 +- write.sh | 25 +++++++++++++++++++------ 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/Makefile b/Makefile index 0acbd29..d975eb5 100644 --- a/Makefile +++ b/Makefile @@ -528,11 +528,18 @@ endif # # Evaluated when the Makefile is read, so the wipe happens before anything is # compiled against the wrong setting. +# +# Only the two objects that read RAMDISK_ROOT are dropped: kernel/proc/proc.c +# (ROOTDEV is used nowhere else) and kernel/dev/ramdisk.c. The whole of +# build/kernel, build/lib and build/cmd used to go, which made every `make iso` +# recompile the kernel and every command, rebuild the root image from the new +# binaries and compress it again at zstd -19 -- minutes, for a one-line change. +# If another file ever starts to use ROOTDEV or RAMDISK_ROOT, add it here. RAMROOT_STATE := $(if $(RAMROOT),on,off) RAMROOT_STAMP := $(BUILD)/ramroot.state $(shell mkdir -p $(BUILD); \ if [ "$$(cat $(RAMROOT_STAMP) 2>/dev/null)" != "$(RAMROOT_STATE)" ]; then \ - rm -rf $(BUILD)/kernel $(BUILD)/lib $(BUILD)/cmd; \ + rm -f $(BUILD)/kernel/proc/proc.o $(BUILD)/kernel/dev/ramdisk.o; \ printf '%s' '$(RAMROOT_STATE)' > $(RAMROOT_STAMP); \ fi) @@ -738,7 +745,6 @@ qemu-uefi: esp $(BUILD)/schfs.img $(OVMF_CODE) $(OVMF_VARS) # silently reused for the other, leaving ROOTDEV pointing at the wrong device. # Dropping the compiled objects first makes the switch always correct. ramroot: - rm -rf $(BUILD)/kernel $(BUILD)/lib $(BUILD)/cmd $(MAKE) RAMROOT=1 cmd: $(CMDBINS) @@ -781,7 +787,6 @@ projects: # CFLAGS and make cannot see that. # ----------------------------------------------------------------------- iso: - rm -rf $(BUILD)/kernel $(BUILD)/lib $(BUILD)/cmd $(MAKE) RAMROOT=1 schos.iso # One El Torito entry, pointing at a FAT image holding the loader and kernel. @@ -1458,19 +1463,23 @@ LOGO_ASCII = docs/branding/Ascii/ascii_50.txt # proves the same thing far less clearly. SRV_FILES = $(wildcard srv/*) +# cp -p, not cp: sysroot.stamp lists every staged file with its mtime, and the +# Alpine root, the root image and its zstd -19 compression are all rebuilt when +# it changes. A plain cp gave every copy a new mtime, so every `make iso` +# rebuilt all of them -- over a minute of CPU for no change at all. etc-install: $(SCHINIT_JOBS) $(ETC_PLAIN) $(ETC_ZSH) $(LOGO_ASCII) $(SRV_FILES) @mkdir -p $(SYSROOT)/etc/schinit $(SYSROOT)/etc/zsh $(SYSROOT)/usr/share/schos $(SYSROOT)/srv - @cp $(ETC_PLAIN) $(SYSROOT)/etc/ - @cp $(SCHINIT_JOBS) $(SYSROOT)/etc/schinit/ - @cp $(ETC_ZSH) $(SYSROOT)/etc/zsh/ - @cp $(LOGO_ASCII) $(SYSROOT)/usr/share/schos/logo.txt - @cp $(SRV_FILES) $(SYSROOT)/srv/ + @cp -p $(ETC_PLAIN) $(SYSROOT)/etc/ + @cp -p $(SCHINIT_JOBS) $(SYSROOT)/etc/schinit/ + @cp -p $(ETC_ZSH) $(SYSROOT)/etc/zsh/ + @cp -p $(LOGO_ASCII) $(SYSROOT)/usr/share/schos/logo.txt + @cp -p $(SRV_FILES) $(SYSROOT)/srv/ @# The device-name databases, where lspci and lsusb look for them: the @# hwdata package's directory. They were at /usr/share/usb.ids, which no @# tool searches, so lsusb printed numbers only. @mkdir -p $(SYSROOT)/usr/share/hwdata @rm -f $(SYSROOT)/usr/share/usb.ids - @for f in usb.ids pci.ids; do [ -f dist/$$f ] && cp dist/$$f $(SYSROOT)/usr/share/hwdata/; done; true + @for f in usb.ids pci.ids; do [ -f dist/$$f ] && cp -p dist/$$f $(SYSROOT)/usr/share/hwdata/; done; true @echo "installed $(words $(ETC_PLAIN)) /etc file(s), $(words $(ETC_ZSH)) zsh file(s) and $(words $(SCHINIT_JOBS)) schinit manifest(s)" # The mount points. Created here rather than by init, because a mkdir on a diff --git a/ports/fastfetch/Makefile b/ports/fastfetch/Makefile index 3df7b99..2662db0 100644 --- a/ports/fastfetch/Makefile +++ b/ports/fastfetch/Makefile @@ -91,7 +91,7 @@ $(BUILDDIR)/fastfetch: $(BUILDDIR)/toolchain.cmake build: $(BUILDDIR)/fastfetch install: $(BUILDDIR)/fastfetch | $(SYSROOT)/bin - install -m 0755 $(BUILDDIR)/fastfetch $(SYSROOT)/bin/fastfetch + install -C -m 0755 $(BUILDDIR)/fastfetch $(SYSROOT)/bin/fastfetch clean: rm -rf $(PORTDIR) diff --git a/write.sh b/write.sh index 1740a71..db21b0d 100644 --- a/write.sh +++ b/write.sh @@ -3,6 +3,7 @@ # # ./write.sh sdc or ./write.sh /dev/sdc # ./write.sh sdc other.iso to write a different image +# ./write.sh sdc --no-verify skip the read-back check (halves the time) # # Shows what is on the device, asks you to type the device's name back, then # asks for your password and writes. Everything on the device is destroyed; @@ -26,7 +27,7 @@ fi die() { printf '%swrite.sh:%s %s\n' "$R" "$N" "$*" >&2; exit 1; } usage() { - echo "usage: ./write.sh [image.iso] [--any-disk]" + echo "usage: ./write.sh [image.iso] [--any-disk] [--no-verify]" echo " ./write.sh sdc writes schos.iso to /dev/sdc" echo echo "Removable disks on this machine:" @@ -36,11 +37,13 @@ usage() { } ANY=0 +VERIFY=1 DEV= ISO= for a in "$@"; do case "$a" in --any-disk) ANY=1 ;; + --no-verify) VERIFY=0 ;; -h|--help) usage ;; *) if [ -z "$DEV" ]; then DEV=$a; elif [ -z "$ISO" ]; then ISO=$a; else usage; fi ;; esac @@ -127,12 +130,22 @@ sync # --- check ------------------------------------------------------------------ # Read it back. A stick that takes the write and keeps none of it is common -# enough among cheap ones to be worth thirty seconds. -echo "Checking what was written..." -if sudo cmp -n "$ISO_BYTES" "$ISO" "$DEV"; then - echo "${G}Done.${N} $DEV holds $(basename "$ISO") and can be removed." +# enough among cheap ones to be worth the time. +# +# iflag=direct and 4 MB reads: a plain `cmp ISO /dev/sdX` reads through the page +# cache in small requests, which a USB stick serves slowly -- and may be answered +# from the cache rather than from the stick, checking nothing. Direct reads +# check the stick itself, and at the speed it can actually deliver. +if [ "$VERIFY" = 1 ]; then + echo "Checking what was written..." + BLOCKS=$(( (ISO_BYTES + 4194303) / 4194304 )) + if sudo dd if="$DEV" bs=4M count="$BLOCKS" iflag=direct status=none | cmp -n "$ISO_BYTES" - "$ISO"; then + echo "${G}Done.${N} $DEV holds $(basename "$ISO") and can be removed." + else + die "the device does not read back what was written -- the stick may be failing" + fi else - die "the device does not read back what was written -- the stick may be failing" + echo "${G}Done.${N} $DEV holds $(basename "$ISO") (not verified) and can be removed." fi # The kernel re-reads the partition table so the new layout shows up here