SchOS/.claude/memory/schunix-build-speed.md
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +02:00

2.6 KiB

name description metadata
schunix-build-speed The build is fast now (no-op ~2s); the four things that made it slow and the traps that will make it slow again
node_type type originSessionId modified
memory project a665e14b-86bd-4b41-a228-51377f3e9225 2026-08-04T17:27:56.099Z

A no-op make went from 1m48s to ~2s on 2026-08-04, and a full kernel rebuild from ~110s to ~7s. Almost none of it was compilation — with only 2.4s of CPU in that 1m48, it was I/O across WSL's /mnt/d boundary. Four causes, all still worth not reintroducing:

  • tools/mkfs built the image on the file, one lseek + one 512-byte write per block: 300,000 syscall pairs, 150,000 of them just to zero it. It now assembles the image in RAM and writes once. It also stops at the last non-zero block and ftruncates, so ~2 MB is written instead of 147 MB.
  • The image was rebuilt on every invocation. sysroot-install is phony, so everything depending on it always ran. There is now a build/sysroot.stamp whose mtime only advances when a find-based summary of the staged tree actually changes (the cmp -s ... || mv idiom — do not add a touch, and note touch -c still updates an existing file's mtime, which is exactly the bug I wrote and had to remove).
  • ports/toybox re-linked its 236 applets every build, because port.mk declares install .PHONY for every port. It hangs off $(BUILDDIR)/toybox.installed now. This was the sneaky one: it bumped the mtime of 236 files in sysroot/ and so invalidated the stamp above.
  • mkfs and mkschfs re-read every file to dedup it. They now check (st_dev, st_ino) first — toybox's applets are real hard links, so 236 of them are settled by one fstat instead of ~500 MB of reads.

Two real bugs found in the Makefile while doing this:

  • $(BUILD)/kernel/**/*.d only matched one directory level. make's wildcard has no recursive **. arch/x86_64/ and fs/schfs/ therefore had no header dependency tracking at all — editing include/fs/schfs.h left stale objects that linked fine. It uses $(shell find ...) now.
  • CFLAGS is recursively expanded and had a $(shell) appended, so the stack-protector probe re-ran on every expansion: one extra gcc per object file. Probed once into NO_SSP := now.

schunix.img is no longer .PHONY (it is a real file), and MAKEFLAGS += -j$(nproc) is on by default — use make -j1 for readable output from a failing build.

Verify with make schfs-check plus schunix-verify-by-driving-shell; the superblock checksum is printed by mkfs and should not change when only build mechanics change.