Makefile (15844B)
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..7}/<artifacts>. 9 # <arch> ∈ {aarch64, amd64, riscv64, riscv32} 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 + boot7 toolchain 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 riscv32 35 TCC_ARCHES := aarch64 amd64 riscv64 36 ALL_DRIVERS := podman seed 37 38 ifeq ($(filter $(ARCH),$(ALL_ARCHES)),) 39 $(error ARCH '$(ARCH)' not supported — use one of $(ALL_ARCHES)) 40 endif 41 ifeq ($(filter $(DRIVER),$(ALL_DRIVERS)),) 42 $(error DRIVER '$(DRIVER)' not supported — use one of $(ALL_DRIVERS)) 43 endif 44 ifeq ($(ARCH):$(DRIVER),riscv32:seed) 45 $(error DRIVER=seed for riscv32 requires the intentionally unimplemented TCC/kernel stages; use DRIVER=podman) 46 endif 47 48 # Per-arch metadata mirrored from boot/lib-arch.sh. 49 PLATFORM_aarch64 := linux/arm64 50 PLATFORM_amd64 := linux/amd64 51 PLATFORM_riscv64 := linux/riscv64 52 # RV32 controller containers use the Podman server's native platform; 53 # tests/Makefile and tests/run.sh resolve it only when they invoke Podman. 54 PLATFORM_riscv32 := native 55 56 KERNEL_NAME_aarch64 := Image 57 KERNEL_NAME_amd64 := kernel.elf 58 KERNEL_NAME_riscv64 := kernel.elf 59 KERNEL_NAME_riscv32 := tcc-target-unimplemented 60 61 MUSL_ARCH_aarch64 := aarch64 62 MUSL_ARCH_amd64 := x86_64 63 MUSL_ARCH_riscv64 := riscv64 64 65 TCC_PKG := tcc-0.9.26-1147-gee75a10c 66 67 OUT_DIR := build/$(ARCH)/$(DRIVER) 68 69 .SUFFIXES: 70 71 .PHONY: all help clean cloc src package release kit0 72 73 # ── Top-level targets ──────────────────────────────────────────────────── 74 75 ifeq ($(filter $(ARCH),$(TCC_ARCHES)),) 76 all: build/$(ARCH)/$(DRIVER)/boot2/catm \ 77 build/$(ARCH)/$(DRIVER)/boot2/scheme1 78 else 79 all: build/$(ARCH)/$(DRIVER)/boot6/$(KERNEL_NAME_$(ARCH)) \ 80 build/$(ARCH)/$(DRIVER)/boot7/toolchain/MANIFEST.sha256 81 endif 82 83 # Prepare the canonical source tree (prep-src + prep-musl) for ARCH. 84 ifeq ($(filter $(ARCH),$(TCC_ARCHES)),) 85 src: build/$(ARCH)/src/.stamp 86 else 87 src: build/$(ARCH)/src/musl/.stamp 88 endif 89 90 help: 91 @echo 'Targets (default ARCH=$(ARCH) DRIVER=$(DRIVER)):' 92 @echo ' make all build implemented chain (RV32: through boot2)' 93 @echo ' make src prep canonical src/ tree (RV32 omits TCC/musl)' 94 @echo ' make package quick: package boot2-<arch>.tar.gz from current build' 95 @echo ' make release validated: build + input/tar/output proofs, mint to dist/' 96 @echo ' make build/<arch>/<driver>/boot6/<kn> kernel branch (kn = Image | kernel.elf)' 97 @echo ' make build/<arch>/<driver>/boot7/toolchain/MANIFEST.sha256' 98 @echo ' assemble final static toolchain' 99 @echo ' make build/<arch>/<driver>/bootN/<file> any single artifact' 100 @echo ' make test SUITE=<suite> test suite (NAMES=<filter> optional)' 101 @echo ' make kit0 KIT0_PREP=<kit-prep-dir> cc.scm + P1 build of Kit0 for ARCH' 102 @echo ' make image per-arch tests container image' 103 @echo ' make cloc line counts for bootstrap sources' 104 @echo ' make clean rm -rf build/' 105 106 KIT0_ARCHES := aarch64 amd64 riscv64 107 KIT0_PROFILE_aarch64 := aa64 108 KIT0_PROFILE_amd64 := x64 109 KIT0_PROFILE_riscv64 := rv64 110 KIT0_PROFILE ?= $(KIT0_PROFILE_$(ARCH)) 111 KIT_DIR ?= ../../kit 112 KIT0_PREP ?= $(KIT_DIR)/build/bootstrap-$(KIT0_PROFILE)/kit0-prep 113 114 ifeq ($(filter $(ARCH),$(KIT0_ARCHES)),) 115 kit0: 116 @echo "kit0 supports ARCH in {$(KIT0_ARCHES)} (got '$(ARCH)')" >&2; exit 2 117 else 118 kit0: build/$(ARCH)/$(DRIVER)/boot2/.stamp build/$(ARCH)/src/.stamp 119 KIT0_PREP='$(KIT0_PREP)' DRIVER='$(DRIVER)' boot/kit0.sh $(ARCH) 120 endif 121 122 clean: 123 rm -rf build/ 124 125 # `package`: package a per-arch tarball from the current build tree. 126 # Depends on both terminal branches: the boot6 kernel and boot7's installed 127 # toolchain (which pulls in boot5 musl), so every manifest entry exists. 128 # Lands at build/<arch>/release/boot2-<arch>.tar.gz. Fast: no 129 # reproducibility or verify check. 130 ifeq ($(filter $(ARCH),$(TCC_ARCHES)),) 131 package: 132 @echo "package is unavailable for $(ARCH): TCC support is intentionally unimplemented; the RV32 chain ends at boot2" >&2; exit 2 133 else 134 package: build/$(ARCH)/$(DRIVER)/boot6/$(KERNEL_NAME_$(ARCH)) \ 135 build/$(ARCH)/$(DRIVER)/boot7/toolchain/MANIFEST.sha256 136 DRIVER=$(DRIVER) tools/mkrelease.sh $(ARCH) 137 endif 138 139 # `release`: the validated path. Builds + packages once, regenerates 140 # and compares canonical inputs, repackages the sealed payload to prove 141 # stable tar bytes, then rebuilds from the archive and compares every 142 # output hash. This is the only way a tarball lands in dist/. 143 ifeq ($(filter $(ARCH),$(TCC_ARCHES)),) 144 release: 145 @echo "release is unavailable for $(ARCH): TCC support is intentionally unimplemented; the RV32 chain ends at boot2" >&2; exit 2 146 else 147 release: 148 DRIVER=$(DRIVER) tools/release.sh $(ARCH) 149 endif 150 151 # ── prep-src + boot0..boot7 chain (rules per arch × driver) ────────────── 152 # 153 # The .stamp files are the make-rule pegs. Each rule lists its real 154 # outputs as additional targets so single-binary builds work — `make 155 # build/aarch64/podman/boot1/M1pp` resolves to the boot1 grouped target 156 # and only walks back through prep-src + boot0. 157 # 158 # Per-arch source deps for prep-src.sh. The file list mirrors what 159 # prep-src.sh actually copies, so a vendor/seed change re-triggers the 160 # canonical-tree build. 161 162 PREP_SRC_COMMON_SRCS := \ 163 bootprep/prep-src.sh boot/lib-arch.sh \ 164 bootprep/stage1-flatten.sh bootprep/libc-flatten.sh \ 165 bootprep/boot4-gen-runscm.sh \ 166 bootprep/boot5-enumerate.sh bootprep/boot5-gen-runscm.sh \ 167 bootprep/boot6-gen-runscm.sh \ 168 bootprep/assets/boot3-run.scm bootprep/assets/boot-hello.c \ 169 bootprep/assets/toolchain-hello.c \ 170 M1pp/M1pp.P1 hex2pp/hex2pp.P1 \ 171 P1/P1.M1pp P1/P1pp.P1pp \ 172 P1/entry-libc.P1pp P1/entry-plain.P1pp P1/elf-end.P1pp \ 173 catm/catm.P1pp \ 174 scheme1/scheme1.P1pp scheme1/prelude.scm \ 175 cc/cc.scm cc/main.scm \ 176 tcc/cc/mem.c \ 177 seed-kernel/kernel.c \ 178 vendor/musl/1.2.5.tar.gz \ 179 vendor/musl/deletes.txt \ 180 $(wildcard vendor/tcc/patches-lb/*.before) \ 181 $(wildcard vendor/tcc/patches-lb/*.after) \ 182 $(wildcard vendor/tcc/patches/*.before) \ 183 $(wildcard vendor/tcc/patches/*.after) \ 184 $(wildcard vendor/tcc/patches/files/*) 185 186 prep_src_arch_srcs = \ 187 vendor/seed/$1/hex0-seed \ 188 vendor/seed/$1/ELF.hex2 \ 189 vendor/seed/$1/hex0.hex0 vendor/seed/$1/hex1.hex0 vendor/seed/$1/hex2.hex1 \ 190 vendor/seed/$1/catm.hex2 vendor/seed/$1/M0.hex2 \ 191 P1/P1-$1.M1 P1/P1-$1.M1pp \ 192 $(if $(filter $1,$(TCC_ARCHES)), \ 193 tcc/libc/$1/start.S tcc/libc/$1/sys_stubs.S \ 194 $(wildcard seed-kernel/arch/$1/*) \ 195 $(wildcard seed-kernel/user/*) \ 196 vendor/musl/generated/$(MUSL_ARCH_$1)/alltypes.h \ 197 vendor/musl/generated/$(MUSL_ARCH_$1)/syscall.h \ 198 $(wildcard vendor/musl/skip-$1.txt) \ 199 $(shell find vendor/musl/overrides -type f 2>/dev/null) \ 200 $(shell find vendor/mes-libc -type f \( -name '*.c' -o -name '*.h' \) 2>/dev/null) \ 201 $(wildcard vendor/mes-libc/patches/*.before) \ 202 $(wildcard vendor/mes-libc/patches/*.after) \ 203 $(wildcard vendor/tcc/0.9.26.tar.gz)) 204 205 # DRIVER=seed bootN stages run scheme1 under QEMU using the podman-built 206 # boot6 kernel. Add that as a make dep so a clean-tree seed build pulls 207 # in the podman side automatically. Empty for DRIVER=podman. 208 seed_kernel_dep = $(if $(filter seed,$2),build/$1/podman/boot6/$(KERNEL_NAME_$1)) 209 210 # Per-arch prep-src rule. Driver-independent. 211 # 212 # prep-src.sh produces the full canonical tree, including the filtered 213 # musl/ subtree (unpack + overrides + deletes + per-arch skip list, 214 # plus the generated boot5 enumerate / run.scm). musl/.stamp is a 215 # secondary peg that boot5 depends on — it's just touched after 216 # src/.stamp, since the musl tree is populated by the same recipe. 217 define PREP_RULES 218 build/$1/src/.stamp: $$(PREP_SRC_COMMON_SRCS) $$(call prep_src_arch_srcs,$1) 219 bootprep/prep-src.sh $1 220 @touch $$@ 221 222 build/$1/src/musl/.stamp: build/$1/src/.stamp 223 @mkdir -p $$(@D) && touch $$@ 224 endef 225 226 $(foreach a,$(ALL_ARCHES),$(eval $(call PREP_RULES,$a))) 227 228 # Per-(arch, driver) boot0..boot7 rules. 229 # 230 # Each stage's .stamp is the recipe peg; each real artifact is declared 231 # as a target depending on the .stamp with an empty recipe (`;`). We 232 # avoid grouped targets (`&:`) — GNU make 4.4 does not propagate "must 233 # remake of prereq" through grouped targets across multi-level chains, 234 # so a path-based request like `make .../boot6/Image` would skip the 235 # boot6 rebuild after a deeper change. Single-target rules with stamp- 236 # anchored declarations propagate correctly. 237 define BOOT_CHAIN_RULES 238 # boot0: hex0-seed -> hex2 / M0 / catm 239 build/$1/$2/boot0/.stamp: \ 240 build/$1/src/.stamp \ 241 boot/boot0.sh boot/lib-arch.sh boot/lib-pipeline.sh \ 242 $$(call seed_kernel_dep,$1,$2) 243 DRIVER=$2 boot/boot0.sh $1 244 @touch $$@ 245 246 build/$1/$2/boot0/hex2 build/$1/$2/boot0/M0 build/$1/$2/boot0/catm: \ 247 build/$1/$2/boot0/.stamp ; 248 249 # boot1: M1pp.P1 + hex2pp.P1 -> M1pp + hex2pp 250 build/$1/$2/boot1/.stamp: \ 251 build/$1/$2/boot0/.stamp \ 252 boot/boot1.sh boot/lib-arch.sh boot/lib-pipeline.sh \ 253 $$(call seed_kernel_dep,$1,$2) 254 DRIVER=$2 boot/boot1.sh $1 255 @touch $$@ 256 257 build/$1/$2/boot1/M1pp build/$1/$2/boot1/hex2pp: build/$1/$2/boot1/.stamp ; 258 259 # boot2: catm.P1pp + scheme1.P1pp -> catm + scheme1 260 build/$1/$2/boot2/.stamp: \ 261 build/$1/$2/boot1/.stamp build/$1/$2/boot0/.stamp \ 262 boot/boot2.sh boot/lib-arch.sh boot/lib-pipeline.sh \ 263 $$(call seed_kernel_dep,$1,$2) 264 DRIVER=$2 boot/boot2.sh $1 265 @touch $$@ 266 267 build/$1/$2/boot2/catm build/$1/$2/boot2/scheme1: build/$1/$2/boot2/.stamp ; 268 269 # boot3: cc.scm-built bootstrap tcc (tcc0) 270 build/$1/$2/boot3/.stamp: \ 271 build/$1/$2/boot2/.stamp build/$1/$2/boot1/.stamp \ 272 boot/boot3.sh boot/lib-arch.sh boot/lib-runscm.sh \ 273 $$(call seed_kernel_dep,$1,$2) 274 DRIVER=$2 boot/boot3.sh $1 275 @touch $$@ 276 277 build/$1/$2/boot3/tcc0 build/$1/$2/boot3/libc.P1pp \ 278 build/$1/$2/boot3/tcc.flat.P1pp: build/$1/$2/boot3/.stamp ; 279 280 # boot4: tcc0 -> tcc1 -> tcc2 self-host chain (+ libc.a, libtcc1.a, hello) 281 build/$1/$2/boot4/.stamp: \ 282 build/$1/$2/boot3/.stamp build/$1/$2/boot2/.stamp \ 283 boot/boot4.sh boot/lib-arch.sh boot/lib-runscm.sh \ 284 $$(call seed_kernel_dep,$1,$2) 285 DRIVER=$2 boot/boot4.sh $1 286 @touch $$@ 287 288 build/$1/$2/boot4/tcc1 build/$1/$2/boot4/tcc2 \ 289 build/$1/$2/boot4/hello \ 290 build/$1/$2/boot4/crt1.o build/$1/$2/boot4/libc.a \ 291 build/$1/$2/boot4/libtcc1.a: build/$1/$2/boot4/.stamp ; 292 293 # boot5: musl libc + hello (consumes prep-musl output) 294 build/$1/$2/boot5/.stamp: \ 295 build/$1/$2/boot4/.stamp build/$1/$2/boot2/.stamp \ 296 build/$1/src/musl/.stamp \ 297 boot/boot5.sh boot/lib-arch.sh boot/lib-runscm.sh \ 298 $$(call seed_kernel_dep,$1,$2) 299 DRIVER=$2 boot/boot5.sh $1 300 @touch $$@ 301 302 build/$1/$2/boot5/libc.a build/$1/$2/boot5/crt1.o \ 303 build/$1/$2/boot5/crti.o build/$1/$2/boot5/crtn.o \ 304 build/$1/$2/boot5/hello: build/$1/$2/boot5/.stamp ; 305 306 # boot6: seed-kernel ELF/Image, built with boot4's tcc2 307 build/$1/$2/boot6/.stamp: \ 308 build/$1/$2/boot4/.stamp build/$1/$2/boot2/.stamp \ 309 boot/boot6.sh boot/lib-arch.sh boot/lib-runscm.sh \ 310 $$(call seed_kernel_dep,$1,$2) 311 DRIVER=$2 boot/boot6.sh $1 312 @touch $$@ 313 314 build/$1/$2/boot6/$$(KERNEL_NAME_$1): build/$1/$2/boot6/.stamp ; 315 316 # boot7: install the final compiler + musl static sysroot in one tree. 317 # This is a deterministic host-side assembly stage for either DRIVER; all 318 # copied binaries were already produced through that driver's boot stages. 319 build/$1/$2/boot7/.stamp: \ 320 build/$1/$2/boot4/.stamp build/$1/$2/boot5/.stamp \ 321 build/$1/src/.stamp \ 322 boot/boot7.sh boot/lib-arch.sh 323 DRIVER=$2 boot/boot7.sh $1 324 @touch $$@ 325 326 build/$1/$2/boot7/toolchain/bin/tcc \ 327 build/$1/$2/boot7/toolchain/lib/libc.a \ 328 build/$1/$2/boot7/toolchain/lib/tcc/libtcc1.a \ 329 build/$1/$2/boot7/toolchain/lib/crt1.o \ 330 build/$1/$2/boot7/toolchain/lib/crti.o \ 331 build/$1/$2/boot7/toolchain/lib/crtn.o \ 332 build/$1/$2/boot7/toolchain/MANIFEST.sha256: build/$1/$2/boot7/.stamp ; 333 endef 334 335 BOOT_DRIVERS_aarch64 := $(ALL_DRIVERS) 336 BOOT_DRIVERS_amd64 := $(ALL_DRIVERS) 337 BOOT_DRIVERS_riscv64 := $(ALL_DRIVERS) 338 BOOT_DRIVERS_riscv32 := podman 339 340 $(foreach a,$(ALL_ARCHES),$(foreach d,$(BOOT_DRIVERS_$(a)),$(eval $(call BOOT_CHAIN_RULES,$a,$d)))) 341 342 # ── Top-level catm'd cc bundle (arch-independent) ──────────────────────── 343 # 344 # scheme1 reads cc as one file: prelude + cc + main concatenated. The 345 # output is arch-independent, so we keep a single repo-level copy that 346 # every test invocation reuses regardless of arch. Plain `cat` is byte- 347 # equivalent to running the bootN catm on these inputs. 348 349 build/cc.scm: scheme1/prelude.scm cc/cc.scm cc/main.scm 350 mkdir -p $(@D) 351 cat $^ > $@ 352 353 # ── cloc ───────────────────────────────────────────────────────────────── 354 # 355 # Default covers every arch; `make cloc ARCH=<a>` narrows the per-arch 356 # pieces (P1-<a>.M1pp/.M1, vendor/seed/<a>/*) to one arch. 357 358 CLOC_SEED_BASES := hex0.hex0 hex1.hex0 hex2.hex1 catm.hex2 M0.hex2 ELF.hex2 359 360 ifeq ($(origin ARCH),file) 361 CLOC_ARCHES := $(ALL_ARCHES) 362 else 363 CLOC_ARCHES := $(ARCH) 364 endif 365 366 CLOC_FILES := \ 367 $(foreach a,$(CLOC_ARCHES),$(foreach f,$(CLOC_SEED_BASES),vendor/seed/$(a)/$(f))) \ 368 $(foreach a,$(CLOC_ARCHES),P1/P1-$(a).M1) \ 369 M1pp/M1pp.P1 \ 370 hex2pp/hex2pp.P1 \ 371 $(foreach a,$(CLOC_ARCHES),vendor/seed/$(a)/ELF.hex2) \ 372 $(foreach a,$(CLOC_ARCHES),P1/P1-$(a).M1pp) \ 373 P1/P1.M1pp \ 374 P1/P1pp.P1pp \ 375 scheme1/scheme1.P1pp \ 376 scheme1/prelude.scm \ 377 cc/cc.scm 378 379 cloc: 380 @sh tools/count-lines.sh $(CLOC_FILES) 381 382 # ── Test infrastructure (suites + their build deps) ────────────────────── 383 # tests/Makefile owns the test-build rules and the `test` / `image` / 384 # `tables` targets. ARCH/DRIVER and per-arch metadata above are visible 385 # to the include. REPO_ROOT signals to tests/Makefile that it is being 386 # included from the top-level (rather than invoked standalone via 387 # `make -C tests`). 388 REPO_ROOT := $(CURDIR) 389 include tests/Makefile