Makefile (12656B)
1 # boot2 — Make-driven bootstrap pipeline. 2 # 3 # The bootN.sh scripts under boot/ are the canonical builders. This 4 # Makefile is dependency tracking: every target is a real output path, 5 # and the rule body invokes the right bootN.sh (or prep-src.sh / 6 # prep-musl.sh) script with the appropriate ARCH and DRIVER. 7 # 8 # Output layout: build/<arch>/<driver>/boot{0..6}/<artifacts>. 9 # <arch> ∈ {aarch64, amd64, riscv64} 10 # <driver> ∈ {podman, seed} 11 # 12 # Path-based builds: 13 # make build/aarch64/podman/boot6/Image 14 # make build/amd64/seed/boot6/kernel.elf 15 # make build/riscv64/podman/boot1/M1pp # only prep-src + boot0 + boot1 16 # 17 # Convenience entrypoints (default ARCH=aarch64, DRIVER=podman): 18 # make all build boot6 kernel for ARCH × DRIVER 19 # make test SUITE=<name> run a test suite (see SUITE list at bottom) 20 # make image build the per-arch container image used by tests 21 # make cloc line counts for the bootstrap sources 22 # make clean rm -rf build/ 23 # 24 # DRIVER=seed bootN stages depend on the podman-built boot6 kernel 25 # (build/<arch>/podman/boot6/<kernel-name>) since the seed driver runs 26 # scheme1 inside seed-kernel under QEMU. The seed-side dependency on 27 # the podman side is encoded in the rules. 28 29 # ── Config ─────────────────────────────────────────────────────────────── 30 31 ARCH ?= aarch64 32 DRIVER ?= podman 33 34 ALL_ARCHES := aarch64 amd64 riscv64 35 ALL_DRIVERS := podman seed 36 37 ifeq ($(filter $(ARCH),$(ALL_ARCHES)),) 38 $(error ARCH '$(ARCH)' not supported — use one of $(ALL_ARCHES)) 39 endif 40 ifeq ($(filter $(DRIVER),$(ALL_DRIVERS)),) 41 $(error DRIVER '$(DRIVER)' not supported — use one of $(ALL_DRIVERS)) 42 endif 43 44 # Per-arch metadata mirrored from boot/lib-arch.sh. 45 PLATFORM_aarch64 := linux/arm64 46 PLATFORM_amd64 := linux/amd64 47 PLATFORM_riscv64 := linux/riscv64 48 49 KERNEL_NAME_aarch64 := Image 50 KERNEL_NAME_amd64 := kernel.elf 51 KERNEL_NAME_riscv64 := kernel.elf 52 53 MUSL_ARCH_aarch64 := aarch64 54 MUSL_ARCH_amd64 := x86_64 55 MUSL_ARCH_riscv64 := riscv64 56 57 TCC_PKG := tcc-0.9.26-1147-gee75a10c 58 59 OUT_DIR := build/$(ARCH)/$(DRIVER) 60 61 .SUFFIXES: 62 63 .PHONY: all help clean cloc src package release 64 65 # ── Top-level targets ──────────────────────────────────────────────────── 66 67 all: build/$(ARCH)/$(DRIVER)/boot6/$(KERNEL_NAME_$(ARCH)) 68 69 # Prepare the canonical source tree (prep-src + prep-musl) for ARCH. 70 src: build/$(ARCH)/src/musl/.stamp 71 72 help: 73 @echo 'Targets (default ARCH=$(ARCH) DRIVER=$(DRIVER)):' 74 @echo ' make all build boot6 kernel' 75 @echo ' make src prep canonical src/ tree (incl. musl)' 76 @echo ' make package quick: package boot2-<arch>-<rev>.tar.gz from current build' 77 @echo ' make release validated: clean-rebuild x2 + verify, mint to dist/' 78 @echo ' make build/<arch>/<driver>/boot6/<kn> full chain (kn = Image | kernel.elf)' 79 @echo ' make build/<arch>/<driver>/bootN/<file> any single artifact' 80 @echo ' make test SUITE=<suite> test suite (NAMES=<filter> optional)' 81 @echo ' make image per-arch tests container image' 82 @echo ' make cloc line counts for bootstrap sources' 83 @echo ' make clean rm -rf build/' 84 85 clean: 86 rm -rf build/ 87 88 # `package`: package a per-arch tarball from the current build tree. 89 # Depends on the full chain (boot5 musl + boot6 kernel) so 90 # OUTPUT_MANIFEST.txt has real artifacts to hash. boot5 is not on the 91 # `all` dep path (boot6 doesn't link musl), so list it explicitly here. 92 # Lands at build/<arch>/release/boot2-<arch>-<rev>.tar.gz. Fast: no 93 # reproducibility or verify check. 94 package: build/$(ARCH)/$(DRIVER)/boot5/libc.a \ 95 build/$(ARCH)/$(DRIVER)/boot6/$(KERNEL_NAME_$(ARCH)) 96 DRIVER=$(DRIVER) tools/mkrelease.sh $(ARCH) 97 98 # `release`: the validated path. Cleans, rebuilds + packages twice, 99 # asserts both tarballs hash-match, extracts one and runs its 100 # verify.sh, then promotes to dist/. Slow but paranoid; this is the 101 # only way a tarball lands in dist/. 102 release: 103 DRIVER=$(DRIVER) tools/release.sh $(ARCH) 104 105 # ── prep-src + boot0..boot6 chain (rules per arch × driver) ────────────── 106 # 107 # The .stamp files are the make-rule pegs. Each rule lists its real 108 # outputs as additional targets so single-binary builds work — `make 109 # build/aarch64/podman/boot1/M1pp` resolves to the boot1 grouped target 110 # and only walks back through prep-src + boot0. 111 # 112 # Per-arch source deps for prep-src.sh. The file list mirrors what 113 # prep-src.sh actually copies, so a vendor/seed change re-triggers the 114 # canonical-tree build. 115 116 PREP_SRC_COMMON_SRCS := \ 117 bootprep/prep-src.sh boot/lib-arch.sh \ 118 bootprep/stage1-flatten.sh bootprep/libc-flatten.sh \ 119 bootprep/boot4-gen-runscm.sh bootprep/boot6-gen-runscm.sh \ 120 bootprep/assets/boot3-run.scm bootprep/assets/boot-hello.c \ 121 M1pp/M1pp.P1 hex2pp/hex2pp.P1 \ 122 P1/P1.M1pp P1/P1pp.P1pp \ 123 P1/entry-libc.P1pp P1/entry-plain.P1pp P1/elf-end.P1pp \ 124 catm/catm.P1pp \ 125 scheme1/scheme1.P1pp scheme1/prelude.scm \ 126 cc/cc.scm cc/main.scm \ 127 tcc/cc/mem.c \ 128 seed-kernel/kernel.c \ 129 vendor/musl/1.2.5.tar.gz \ 130 vendor/musl/deletes.txt 131 132 prep_src_arch_srcs = \ 133 vendor/seed/$1/hex0-seed \ 134 vendor/seed/$1/ELF.hex2 \ 135 vendor/seed/$1/hex0.hex0 vendor/seed/$1/hex1.hex0 vendor/seed/$1/hex2.hex1 \ 136 vendor/seed/$1/catm.hex2 vendor/seed/$1/M0.hex2 \ 137 P1/P1-$1.M1 P1/P1-$1.M1pp \ 138 tcc/libc/$1/start.S tcc/libc/$1/sys_stubs.S \ 139 $(wildcard seed-kernel/arch/$1/*) \ 140 $(wildcard seed-kernel/user/*) \ 141 vendor/musl/generated/$(MUSL_ARCH_$1)/alltypes.h \ 142 vendor/musl/generated/$(MUSL_ARCH_$1)/syscall.h \ 143 $(wildcard vendor/musl/skip-$1.txt) \ 144 $(shell find vendor/musl/overrides -type f 2>/dev/null) \ 145 $(shell find vendor/mes-libc -type f \( -name '*.c' -o -name '*.h' \) 2>/dev/null) \ 146 $(wildcard vendor/mes-libc/patches/*.before) \ 147 $(wildcard vendor/mes-libc/patches/*.after) \ 148 $(wildcard vendor/tcc/0.9.26.tar.gz) 149 150 # DRIVER=seed bootN stages run scheme1 under QEMU using the podman-built 151 # boot6 kernel. Add that as a make dep so a clean-tree seed build pulls 152 # in the podman side automatically. Empty for DRIVER=podman. 153 seed_kernel_dep = $(if $(filter seed,$2),build/$1/podman/boot6/$(KERNEL_NAME_$1)) 154 155 # Per-arch prep-src rule. Driver-independent. 156 # 157 # prep-src.sh produces the full canonical tree, including the filtered 158 # musl/ subtree (unpack + overrides + deletes + per-arch skip list, 159 # plus the generated boot5 enumerate / run.scm). musl/.stamp is a 160 # secondary peg that boot5 depends on — it's just touched after 161 # src/.stamp, since the musl tree is populated by the same recipe. 162 define PREP_RULES 163 build/$1/src/.stamp: $$(PREP_SRC_COMMON_SRCS) $$(call prep_src_arch_srcs,$1) 164 bootprep/prep-src.sh $1 165 @touch $$@ 166 167 build/$1/src/musl/.stamp: build/$1/src/.stamp 168 @mkdir -p $$(@D) && touch $$@ 169 endef 170 171 $(foreach a,$(ALL_ARCHES),$(eval $(call PREP_RULES,$a))) 172 173 # Per-(arch, driver) boot0..boot6 rules. 174 # 175 # Each stage's .stamp is the recipe peg; each real artifact is declared 176 # as a target depending on the .stamp with an empty recipe (`;`). We 177 # avoid grouped targets (`&:`) — GNU make 4.4 does not propagate "must 178 # remake of prereq" through grouped targets across multi-level chains, 179 # so a path-based request like `make .../boot6/Image` would skip the 180 # boot6 rebuild after a deeper change. Single-target rules with stamp- 181 # anchored declarations propagate correctly. 182 define BOOT_CHAIN_RULES 183 # boot0: hex0-seed -> hex2 / M0 / catm 184 build/$1/$2/boot0/.stamp: \ 185 build/$1/src/.stamp \ 186 boot/boot0.sh boot/lib-arch.sh boot/lib-pipeline.sh \ 187 $$(call seed_kernel_dep,$1,$2) 188 DRIVER=$2 boot/boot0.sh $1 189 @touch $$@ 190 191 build/$1/$2/boot0/hex2 build/$1/$2/boot0/M0 build/$1/$2/boot0/catm: \ 192 build/$1/$2/boot0/.stamp ; 193 194 # boot1: M1pp.P1 + hex2pp.P1 -> M1pp + hex2pp 195 build/$1/$2/boot1/.stamp: \ 196 build/$1/$2/boot0/.stamp \ 197 boot/boot1.sh boot/lib-arch.sh boot/lib-pipeline.sh \ 198 $$(call seed_kernel_dep,$1,$2) 199 DRIVER=$2 boot/boot1.sh $1 200 @touch $$@ 201 202 build/$1/$2/boot1/M1pp build/$1/$2/boot1/hex2pp: build/$1/$2/boot1/.stamp ; 203 204 # boot2: catm.P1pp + scheme1.P1pp -> catm + scheme1 205 build/$1/$2/boot2/.stamp: \ 206 build/$1/$2/boot1/.stamp build/$1/$2/boot0/.stamp \ 207 boot/boot2.sh boot/lib-arch.sh boot/lib-pipeline.sh \ 208 $$(call seed_kernel_dep,$1,$2) 209 DRIVER=$2 boot/boot2.sh $1 210 @touch $$@ 211 212 build/$1/$2/boot2/catm build/$1/$2/boot2/scheme1: build/$1/$2/boot2/.stamp ; 213 214 # boot3: cc.scm-built bootstrap tcc (tcc0) 215 build/$1/$2/boot3/.stamp: \ 216 build/$1/$2/boot2/.stamp build/$1/$2/boot1/.stamp \ 217 boot/boot3.sh boot/lib-arch.sh boot/lib-runscm.sh \ 218 $$(call seed_kernel_dep,$1,$2) 219 DRIVER=$2 boot/boot3.sh $1 220 @touch $$@ 221 222 build/$1/$2/boot3/tcc0 build/$1/$2/boot3/libc.P1pp \ 223 build/$1/$2/boot3/tcc.flat.P1pp: build/$1/$2/boot3/.stamp ; 224 225 # boot4: tcc0 -> tcc1 -> tcc2 -> tcc3 self-host chain (+ libc.a, libtcc1.a, hello) 226 build/$1/$2/boot4/.stamp: \ 227 build/$1/$2/boot3/.stamp build/$1/$2/boot2/.stamp \ 228 boot/boot4.sh boot/lib-arch.sh boot/lib-runscm.sh \ 229 $$(call seed_kernel_dep,$1,$2) 230 DRIVER=$2 boot/boot4.sh $1 231 @touch $$@ 232 233 build/$1/$2/boot4/tcc1 build/$1/$2/boot4/tcc2 \ 234 build/$1/$2/boot4/tcc3 build/$1/$2/boot4/hello \ 235 build/$1/$2/boot4/crt1.o build/$1/$2/boot4/libc.a \ 236 build/$1/$2/boot4/libtcc1.a: build/$1/$2/boot4/.stamp ; 237 238 # boot5: musl libc + hello (consumes prep-musl output) 239 build/$1/$2/boot5/.stamp: \ 240 build/$1/$2/boot4/.stamp build/$1/$2/boot2/.stamp \ 241 build/$1/src/musl/.stamp \ 242 boot/boot5.sh boot/lib-arch.sh boot/lib-runscm.sh \ 243 $$(call seed_kernel_dep,$1,$2) 244 DRIVER=$2 boot/boot5.sh $1 245 @touch $$@ 246 247 build/$1/$2/boot5/libc.a build/$1/$2/boot5/crt1.o \ 248 build/$1/$2/boot5/crti.o build/$1/$2/boot5/crtn.o \ 249 build/$1/$2/boot5/hello: build/$1/$2/boot5/.stamp ; 250 251 # boot6: seed-kernel ELF/Image, built with boot4's tcc3 252 build/$1/$2/boot6/.stamp: \ 253 build/$1/$2/boot4/.stamp build/$1/$2/boot2/.stamp \ 254 boot/boot6.sh boot/lib-arch.sh boot/lib-runscm.sh \ 255 $$(call seed_kernel_dep,$1,$2) 256 DRIVER=$2 boot/boot6.sh $1 257 @touch $$@ 258 259 build/$1/$2/boot6/$$(KERNEL_NAME_$1): build/$1/$2/boot6/.stamp ; 260 endef 261 262 $(foreach a,$(ALL_ARCHES),$(foreach d,$(ALL_DRIVERS),$(eval $(call BOOT_CHAIN_RULES,$a,$d)))) 263 264 # ── Top-level catm'd cc bundle (arch-independent) ──────────────────────── 265 # 266 # scheme1 reads cc as one file: prelude + cc + main concatenated. The 267 # output is arch-independent, so we keep a single repo-level copy that 268 # every test invocation reuses regardless of arch. Plain `cat` is byte- 269 # equivalent to running the bootN catm on these inputs. 270 271 build/cc.scm: scheme1/prelude.scm cc/cc.scm cc/main.scm 272 mkdir -p $(@D) 273 cat $^ > $@ 274 275 # ── cloc ───────────────────────────────────────────────────────────────── 276 # 277 # Default covers every arch; `make cloc ARCH=<a>` narrows the per-arch 278 # pieces (P1-<a>.M1pp/.M1, vendor/seed/<a>/*) to one arch. 279 280 CLOC_SEED_BASES := hex0.hex0 hex1.hex0 hex2.hex1 catm.hex2 M0.hex2 ELF.hex2 281 282 ifeq ($(origin ARCH),file) 283 CLOC_ARCHES := $(ALL_ARCHES) 284 else 285 CLOC_ARCHES := $(ARCH) 286 endif 287 288 CLOC_FILES := \ 289 $(foreach a,$(CLOC_ARCHES),$(foreach f,$(CLOC_SEED_BASES),vendor/seed/$(a)/$(f))) \ 290 $(foreach a,$(CLOC_ARCHES),P1/P1-$(a).M1) \ 291 M1pp/M1pp.P1 \ 292 hex2pp/hex2pp.P1 \ 293 $(foreach a,$(CLOC_ARCHES),vendor/seed/$(a)/ELF.hex2) \ 294 $(foreach a,$(CLOC_ARCHES),P1/P1-$(a).M1pp) \ 295 P1/P1.M1pp \ 296 P1/P1pp.P1pp \ 297 scheme1/scheme1.P1pp \ 298 scheme1/prelude.scm \ 299 cc/cc.scm 300 301 cloc: 302 @sh tools/count-lines.sh $(CLOC_FILES) 303 304 # ── Test infrastructure (suites + their build deps) ────────────────────── 305 # tests/Makefile owns the test-build rules and the `test` / `image` / 306 # `tables` targets. ARCH/DRIVER and per-arch metadata above are visible 307 # to the include. REPO_ROOT signals to tests/Makefile that it is being 308 # included from the top-level (rather than invoked standalone via 309 # `make -C tests`). 310 REPO_ROOT := $(CURDIR) 311 include tests/Makefile