boot2

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

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