boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs

commit 5804adb9c84acf6eb8e80dc06626dd3354df8ce5
parent 0696a381fa35277134e0c1dd22511fc2de886e96
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Tue, 21 Apr 2026 08:58:22 -0700

build: pull podman image once per arch, run with --pull=never

Prevents silent registry round-trips on every `podman run` — even a
digest-pinned reference was re-resolving the manifest. One explicit
`podman pull` per arch registers the image in the local store via a
build/$(ARCH)/.image stamp; every other invocation sets --pull=never
so cross-arch leaks or a bumped digest fail loudly instead of quietly
re-pulling.

Also drops stale kaem-minimal references left behind when that program
was removed.

Diffstat:
MMakefile | 39++++++++++++++++++++++++++-------------
1 file changed, 26 insertions(+), 13 deletions(-)

diff --git a/Makefile b/Makefile @@ -23,20 +23,18 @@ PROG ?= hello ARCH ?= aarch64 -# Programs live in src/ (real: lisp, kaem-minimal) or tests/ (smoke: -# hello, demo). Resolve PROG.M1 against both so the user doesn't have -# to remember which bucket a program is in. +# Programs live in src/ (real: lisp) or tests/ (smoke: hello, demo). +# Resolve PROG.M1 against both so the user doesn't have to remember +# which bucket a program is in. PROG_SRC := $(firstword $(wildcard src/$(PROG).M1 tests/$(PROG).M1)) ifeq ($(PROG_SRC),) $(error PROG '$(PROG)' not found — no src/$(PROG).M1 or tests/$(PROG).M1) endif # Per-program runtime arguments. The lisp interpreter reads its script -# from argv[1] (docs/LISP.md step 8); kaem-minimal reads its build -# script. Default each to its smoke fixture so `make run-all` stays a -# smoke test. +# from argv[1] (docs/LISP.md step 8). Default to its smoke fixture so +# `make run-all` stays a smoke test. RUN_ARGS_lisp ?= tests/lisp/00-identity.scm -RUN_ARGS_kaem-minimal ?= tests/kaem.run RUN_ARGS := $(RUN_ARGS_$(PROG)) # Map P1 ARCH -> Linux-platform tag for the container. @@ -83,11 +81,22 @@ UPSTREAM_STAMP := $(UPSTREAM_DIR)/.stamp # Single podman view: curdir mounted at /work. Toolchain build, assembly, # link, and run all share this view. Keeping it narrow means nothing # outside the repo is visible to the container. -PODMAN := podman run --rm --platform $(PLATFORM) \ +# +# --pull=never: the image is fetched exactly once per arch by the +# $(IMAGE_STAMP) rule below. Every other podman invocation must find it +# already in the local store, otherwise fail loudly — so cross-arch pulls +# or a bumped digest show up as errors, not silent registry traffic. +PODMAN := podman run --rm --pull=never --platform $(PLATFORM) \ -v $(CURDIR):/work \ -w /work \ $(RUNTIME_IMAGE) +# Per-arch image stamp. One explicit `podman pull` registers the +# digest-pinned image in the local store; subsequent runs use --pull=never +# and never contact the registry. Bumping $(RUNTIME_IMAGE) requires +# `make clean` (or removing build/$(ARCH)/.image) to repull. +IMAGE_STAMP := $(OUT_DIR)/.image + # --- Targets --------------------------------------------------------------- .PHONY: all toolchain populate-upstream run run-all test-lisp test-lisp-all clean @@ -101,6 +110,10 @@ populate-upstream: $(UPSTREAM_STAMP) $(OUT_DIR) $(TOOLS_DIR): mkdir -p $@ +$(IMAGE_STAMP): | $(OUT_DIR) + podman pull --platform $(PLATFORM) $(RUNTIME_IMAGE) + @touch $@ + # Mirror the upstream seed + hex0/1/2/catm/M0/ELF files we need from # $(UPSTREAM) into build/upstream/. Host-side so the container mount stays # minimal. The stamp doubles as an order marker and avoids re-copying on @@ -118,7 +131,7 @@ $(UPSTREAM_DIR)/%: $(UPSTREAM_STAMP) ; # One shot per arch — see bootstrap.sh for the phase-by-phase chain. # # Grouped target (&:) so all five outputs come from a single recipe run. -$(TOOLS_DIR)/M0 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm $(TOOLS_DIR)/hex0 $(TOOLS_DIR)/hex1 &: bootstrap.sh $(UPSTREAM_STAMP) | $(TOOLS_DIR) +$(TOOLS_DIR)/M0 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm $(TOOLS_DIR)/hex0 $(TOOLS_DIR)/hex1 &: bootstrap.sh $(UPSTREAM_STAMP) | $(TOOLS_DIR) $(IMAGE_STAMP) $(PODMAN) sh bootstrap.sh $(ARCH) /work/$(TOOLS_DIR) # Assemble: lint first, then combine per-arch defs + program and feed to M0. @@ -130,7 +143,7 @@ $(TOOLS_DIR)/M0 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm $(TOOLS_DIR)/hex0 $(TOOLS_ # M0 takes a single positional input (no -f flag), so we catm the two # sources together first. The intermediate .combined.M1 is kept in OUT_DIR # so it gets cleaned along with everything else. -$(OUT_DIR)/$(PROG).hex2: $(PROG_SRC) $(OUT_DIR)/p1_$(ARCH).M1 lint.sh $(TOOLS_DIR)/M0 $(TOOLS_DIR)/catm | $(OUT_DIR) +$(OUT_DIR)/$(PROG).hex2: $(PROG_SRC) $(OUT_DIR)/p1_$(ARCH).M1 lint.sh $(TOOLS_DIR)/M0 $(TOOLS_DIR)/catm | $(OUT_DIR) $(IMAGE_STAMP) ./lint.sh $(OUT_DIR)/p1_$(ARCH).M1 $(PROG_SRC) $(PODMAN) sh -ec ' \ $(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).combined.M1 $(OUT_DIR)/p1_$(ARCH).M1 $(PROG_SRC) ; \ @@ -142,12 +155,12 @@ $(OUT_DIR)/$(PROG).hex2: $(PROG_SRC) $(OUT_DIR)/p1_$(ARCH).M1 lint.sh $(TOOLS_DI # base address 0x00600000 (no --base-address flag), which is why the ELF # header references `&ELF_base` symbolically rather than baking in a # concrete VA — the header travels to whatever base the linker chose. -$(OUT_DIR)/$(PROG): $(OUT_DIR)/$(PROG).hex2 $(UPSTREAM_DIR)/$(ARCH_DIR)/ELF-$(ARCH).hex2 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm +$(OUT_DIR)/$(PROG): $(OUT_DIR)/$(PROG).hex2 $(UPSTREAM_DIR)/$(ARCH_DIR)/ELF-$(ARCH).hex2 $(TOOLS_DIR)/hex2-0 $(TOOLS_DIR)/catm | $(IMAGE_STAMP) $(PODMAN) sh -ec ' \ $(TOOLS_DIR)/catm $(OUT_DIR)/$(PROG).linked.hex2 $(UPSTREAM_DIR)/$(ARCH_DIR)/ELF-$(ARCH).hex2 $(OUT_DIR)/$(PROG).hex2 ; \ $(TOOLS_DIR)/hex2-0 $(OUT_DIR)/$(PROG).linked.hex2 $(OUT_DIR)/$(PROG)' -run: $(OUT_DIR)/$(PROG) +run: $(OUT_DIR)/$(PROG) | $(IMAGE_STAMP) $(PODMAN) ./$(OUT_DIR)/$(PROG) $(RUN_ARGS) # `-` prefix: continue past non-zero exit. demo.M1 exits with the computed @@ -167,7 +180,7 @@ run-all: # final fixnum payload), so we ignore exit codes and only diff stdout. LISP_TESTS := $(sort $(wildcard tests/lisp/*.scm)) -test-lisp: +test-lisp: | $(IMAGE_STAMP) @$(MAKE) --no-print-directory PROG=lisp ARCH=$(ARCH) build/$(ARCH)/lisp @pass=0; fail=0; \ for scm in $(LISP_TESTS); do \