One ISA or several (ARCH=amd64,aarch64,riscv64, for the full
acceptance), on Debian/Ubuntu with apt-get, sudo when not root:
- common: the build, the disk-image tools (scripts/mkdiskimage.sh;
sfdisk is in fdisk), git-lfs for the logs and DoE CSVs, and the
documents -- docs/book/Makefile's own list, and pdflatex, doxygen and
graphviz for docs/formal;
- per ISA: its QEMU, its UEFI firmware at kernel/Makefile's paths, and
its compiler (aarch64's loader also needs clang and lld-link).
Then git lfs install --local, so a log or CSV committed from this
checkout goes into LFS (the 1003 plain files fixed in 027b144e came
from a checkout without it). Not installed: syft (make sbom), which is
not packaged.
Checked on Ubuntu 24.04: init for all three ISAs, then make docs and
make -C docs/formal vols build. make -C docs/formal doxygen fails in
LaTeX on "newunicodechar Error: ASCII character requested" with every
package present -- a separate fault, not a missing dependency.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GE3SDvQn5EoxdGufk84zz2
282 lines
14 KiB
Makefile
282 lines
14 KiB
Makefile
# ==============================================================================
|
|
# LithosAnanke / StarForth -- top-level Makefile
|
|
#
|
|
# Thin dispatcher. The real build lives in:
|
|
# v3/Makefile -- hosted StarForth v3 (Linux userland VM, POST, DoE)
|
|
# kernel/Makefile -- LithosAnanke kernel (UEFI loader, kernel ELF, QEMU)
|
|
# boards/<board>/ -- one directory per physical target (board.mk + boot files)
|
|
# docs/book/Makefile -- the single LaTeX book (build/docs/*.pdf)
|
|
# Every sub-make runs from this directory, so paths are always repo-relative.
|
|
#
|
|
# TARGET is either a board (PC RASPI MILKV ZYNQ7020), a hosted v3 profile
|
|
# (standard fast fastest turbo asan ...), or empty:
|
|
#
|
|
# make [all] every board that has a kernel port, the
|
|
# hosted v3 build, and the book
|
|
# make [all] TARGET=<board> that board's boot image + its book
|
|
# make [all] TARGET=<profile> hosted v3 build with that profile
|
|
#
|
|
# make boot_image TARGET=<board> only the board's boot image, with its SBOM
|
|
# (build/boards/<board>/*.spdx*) and a fresh
|
|
# source SBOM -- needs syft
|
|
# make boot_images every board that has a kernel port
|
|
# make write_stick DEVICE=/dev/sdX write the PC stick image to a USB stick, checked
|
|
# first, read back and compared after
|
|
# (scripts/write_stick.sh; Linux, sudo)
|
|
# make docs [TARGET=<board>] the book; a board adds its appendix
|
|
#
|
|
# make clean all of build/ except build/cache/
|
|
# make clean TARGET=<board> the board's image + book and its ISA's kernel objects
|
|
# make clean TARGET=<profile> build/<arch>/<profile> for every arch
|
|
# make clean-docs build/docs/
|
|
#
|
|
# make sbom SPDX SBOM of the source tree (sbom.spdx, sbom.spdx.json)
|
|
# make kernel ARCH=<arch> [GOAL=g] kernel/Makefile goal (default: all)
|
|
# make ARCH=<arch> clean qemu the kernel for <arch> (amd64 aarch64 riscv64),
|
|
# cleaned, built and booted in QEMU: the
|
|
# acceptance boot (.claude/CLAUDE.md)
|
|
# make boards list boards and whether they can be built
|
|
# make ARCH=<arch> init install everything needed to build <arch>,
|
|
# boot it in QEMU and build the documents
|
|
# (Debian/Ubuntu, apt-get; sudo if not root).
|
|
# ARCH=amd64,aarch64,riscv64 sets up all
|
|
# three, for the full acceptance
|
|
# make <other goal> goes to v3/Makefile (make help-hosted)
|
|
# ==============================================================================
|
|
|
|
# Board name (as typed on the command line) -> boards/ directory.
|
|
BOARD_DIR_PC := pc
|
|
BOARD_DIR_RASPI := raspi
|
|
BOARD_DIR_MILKV := milkv
|
|
BOARD_DIR_ZYNQ7020 := zynq7020
|
|
BOARDS := PC RASPI MILKV ZYNQ7020
|
|
empty :=
|
|
space := $(empty) $(empty)
|
|
|
|
board_field = $(if $(BOARD_DIR_$(1)),$(shell sed -n 's/^$(2) *:= *//p' boards/$(BOARD_DIR_$(1))/board.mk))
|
|
board_arch = $(call board_field,$(1),BOARD_ARCH)
|
|
# A board can be built once its ISA's kernel port exists.
|
|
board_ready = $(if $(call board_arch,$(1)),$(wildcard kernel/src/arch/$(call board_arch,$(1))/.))
|
|
|
|
READY_BOARDS := $(foreach b,$(BOARDS),$(if $(call board_ready,$(b)),$(b)))
|
|
UNREADY_BOARDS := $(filter-out $(READY_BOARDS),$(BOARDS))
|
|
|
|
TARGET := $(strip $(TARGET))
|
|
BOARD_DIR := $(if $(TARGET),$(BOARD_DIR_$(TARGET)))
|
|
|
|
SUBMAKE := $(MAKE) --no-print-directory
|
|
|
|
.DEFAULT_GOAL := all
|
|
.PHONY: all boot_image boot_images write_stick docs clean clean-docs kernel boards help help-hosted sbom sbom-board \
|
|
qemu qemu-esp qemu-gdb init
|
|
|
|
# ARCH given on the command line and no TARGET: `clean` is the kernel's for that
|
|
# ISA, as `qemu` always is (make ARCH=<arch> clean qemu).
|
|
KERNEL_CLEAN := $(if $(TARGET),,$(filter command line,$(origin ARCH)))
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# all
|
|
# ------------------------------------------------------------------------------
|
|
ifeq ($(TARGET),)
|
|
all:
|
|
@for b in $(READY_BOARDS); do \
|
|
$(SUBMAKE) boot_image TARGET=$$b || exit 1; \
|
|
done
|
|
@$(SUBMAKE) -f v3/Makefile all
|
|
@$(SUBMAKE) docs
|
|
@echo ""
|
|
@echo "Built:"
|
|
@for b in $(READY_BOARDS); do \
|
|
d=$$(echo $$b | tr A-Z a-z); ls build/boards/$$d/*.img | sed 's/^/ /'; \
|
|
done
|
|
@echo " build/docs/LithosAnanke.pdf"
|
|
@$(foreach b,$(UNREADY_BOARDS),echo "Skipped $(b): needs kernel/src/arch/$(call board_arch,$(b)), which does not exist yet";)
|
|
else ifneq ($(BOARD_DIR),)
|
|
all:
|
|
@$(SUBMAKE) boot_image TARGET=$(TARGET)
|
|
@$(SUBMAKE) docs TARGET=$(TARGET)
|
|
else
|
|
all:
|
|
@$(SUBMAKE) -f v3/Makefile all TARGET=$(TARGET)
|
|
endif
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# boot images
|
|
# ------------------------------------------------------------------------------
|
|
boot_image:
|
|
@if [ -z "$(BOARD_DIR)" ]; then \
|
|
echo "Usage: make boot_image TARGET=<$(subst $(space),|,$(BOARDS))>"; \
|
|
[ -z "$(TARGET)" ] || echo "Unknown board TARGET='$(TARGET)'"; \
|
|
exit 1; \
|
|
fi
|
|
@if [ -z "$(call board_ready,$(TARGET))" ]; then \
|
|
echo "Error: $(TARGET) needs the $(call board_arch,$(TARGET)) kernel port (kernel/src/arch/$(call board_arch,$(TARGET))), which does not exist yet."; \
|
|
echo " $(call board_field,$(TARGET),BOARD_DESC)"; \
|
|
exit 1; \
|
|
fi
|
|
@# Both systems in one image, v4 unless the boot arguments say system=v3
|
|
@# (boards/BRINGUP.md A6, ruled 2026-10-14; v4 the default, same day). A clean build every time: the
|
|
@# kernel objects are shared with `make qemu`, and make does not see a
|
|
@# change of compile flags -- an image could otherwise carry objects built
|
|
@# without v4, or without the bring-up console.
|
|
@$(SUBMAKE) -f kernel/Makefile ARCH=$(call board_arch,$(TARGET)) TARGET=kernel clean
|
|
@$(SUBMAKE) -f kernel/Makefile \
|
|
BOARD=$(BOARD_DIR) ARCH=$(call board_arch,$(TARGET)) TARGET=kernel \
|
|
STARFORTH_V4=1 V4_DEFAULT=1 boot_image
|
|
@# Its SBOM, and the source tree's, are part of the build (2026-10-14).
|
|
@$(SUBMAKE) sbom-board TARGET=$(TARGET)
|
|
@$(SUBMAKE) sbom
|
|
|
|
boot_images:
|
|
@for b in $(READY_BOARDS); do \
|
|
$(SUBMAKE) boot_image TARGET=$$b || exit 1; \
|
|
done
|
|
@$(foreach b,$(UNREADY_BOARDS),echo "Skipped $(b): needs kernel/src/arch/$(call board_arch,$(b)), which does not exist yet";)
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# SBOMs (SPDX 2.3, by syft; config mk/syft.yaml) -- part of every board build
|
|
# ------------------------------------------------------------------------------
|
|
# The source SBOM covers what the product is built from: the tracked files of
|
|
# the kernel, v3, v4, capsules, boards, build tooling and proofs, each with its
|
|
# SHA-256 -- copied to build/sbom/src first, so nothing untracked in the
|
|
# working directory is ever swept in. Not logs, documents, experiment data or
|
|
# the disk images. A board's SBOM covers
|
|
# its image and every file on it, copied off the image, so the SBOM is of what
|
|
# is on the medium, not of what was meant to be. Versioned as the kernel
|
|
# version and the commit. No syft: the build stops, as for any missing tool.
|
|
SYFT ?= syft
|
|
SBOM_VERSION = $(shell sed -n 's/^LITHOS_VERSION *?= *//p' kernel/Makefile)+$(shell git describe --always --dirty 2>/dev/null)
|
|
# What the product is built from: tracked files only, in these places (never
|
|
# untracked or ignored files in the working directory).
|
|
SBOM_SOURCES := kernel v3 v4 capsules boards mk tools scripts proof configs \
|
|
Makefile Kconfig Kconfig.arch Kconfig.heartbeat Kconfig.kernel \
|
|
Kconfig.physics Kconfig.variant LICENSE NOTICE.md
|
|
GIT_PLAIN := git -c filter.lfs.process= -c filter.lfs.smudge=cat -c filter.lfs.required=false
|
|
|
|
define need_syft
|
|
@command -v $(SYFT) >/dev/null 2>&1 || { \
|
|
echo "Error: syft not found -- the SBOM is part of this build."; \
|
|
echo " Install a release from https://github.com/anchore/syft/releases"; \
|
|
echo " (verify it against the release's checksums), or SYFT=/path/to/syft."; exit 1; }
|
|
endef
|
|
|
|
sbom:
|
|
$(need_syft)
|
|
@echo " SBOM source tree -> sbom.spdx, sbom.spdx.json ($(SBOM_VERSION))"
|
|
@rm -rf build/sbom/src && mkdir -p build/sbom/src && \
|
|
$(GIT_PLAIN) ls-files -z -- $(SBOM_SOURCES) | xargs -0 cp --parents -t build/sbom/src && \
|
|
$(SYFT) -q -c mk/syft.yaml dir:build/sbom/src \
|
|
--source-name LithosAnanke --source-version "$(SBOM_VERSION)" \
|
|
-o spdx-json=sbom.spdx.json -o spdx=sbom.spdx
|
|
|
|
sbom-board:
|
|
$(need_syft)
|
|
@if [ -z "$(BOARD_DIR)" ]; then echo "Usage: make sbom-board TARGET=<$(subst $(space),|,$(BOARDS))>"; exit 1; fi
|
|
@img=build/boards/$(BOARD_DIR)/$(call board_field,$(TARGET),BOARD_IMAGE); \
|
|
if [ ! -f "$$img" ]; then echo "Error: $$img not built (make boot_image TARGET=$(TARGET))"; exit 1; fi; \
|
|
rm -rf build/boards/$(BOARD_DIR)/contents && mkdir -p build/boards/$(BOARD_DIR)/contents && \
|
|
mcopy -s -n -i "$$img@@1M" '::/*' build/boards/$(BOARD_DIR)/contents/ && \
|
|
echo " SBOM $$img -> $${img%.img}.spdx, $${img%.img}.spdx.json" && \
|
|
$(SYFT) -q -c mk/syft.yaml dir:build/boards/$(BOARD_DIR) \
|
|
--exclude './*.spdx' --exclude './*.spdx.json' \
|
|
--source-name "LithosAnanke-$(BOARD_DIR)" --source-version "$(SBOM_VERSION)" \
|
|
-o spdx-json=$${img%.img}.spdx.json -o spdx=$${img%.img}.spdx
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# docs
|
|
# ------------------------------------------------------------------------------
|
|
docs:
|
|
@if [ -n "$(TARGET)" ] && [ -z "$(BOARD_DIR)" ]; then \
|
|
echo "Error: make docs takes TARGET=<$(subst $(space),|,$(BOARDS))> or no TARGET; '$(TARGET)' is not a board."; \
|
|
exit 1; \
|
|
fi
|
|
@$(SUBMAKE) -f docs/book/Makefile BOARD=$(BOARD_DIR) BOARD_NAME=$(TARGET)
|
|
|
|
clean-docs:
|
|
rm -rf build/docs
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# clean
|
|
# ------------------------------------------------------------------------------
|
|
ifneq ($(KERNEL_CLEAN),)
|
|
clean:
|
|
@$(SUBMAKE) -f kernel/Makefile ARCH=$(ARCH) clean
|
|
else ifeq ($(TARGET),)
|
|
clean:
|
|
@if [ -d build ]; then find build -mindepth 1 -maxdepth 1 ! -name cache -exec rm -rf {} +; fi
|
|
rm -f v3/include/version.h
|
|
rm -f v3/src/*.gcda v3/src/*.gcno v3/src/word_source/*.gcda v3/src/word_source/*.gcno
|
|
@echo "Cleaned build/ (kept build/cache/)"
|
|
else ifneq ($(BOARD_DIR),)
|
|
clean:
|
|
rm -rf build/boards/$(BOARD_DIR) build/$(call board_arch,$(TARGET))/kernel build/docs/$(BOARD_DIR) \
|
|
build/docs/LithosAnanke-$(BOARD_DIR).pdf
|
|
else
|
|
clean:
|
|
rm -rf build/*/$(TARGET)
|
|
endif
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# misc
|
|
# ------------------------------------------------------------------------------
|
|
GOAL ?= all
|
|
kernel:
|
|
@$(SUBMAKE) -f kernel/Makefile TARGET=kernel $(GOAL)
|
|
|
|
qemu qemu-esp qemu-gdb:
|
|
@$(SUBMAKE) -f kernel/Makefile $(if $(ARCH),ARCH=$(ARCH)) $@
|
|
|
|
boards:
|
|
@$(foreach b,$(BOARDS),printf ' %-9s %-8s %s\n' "$(b)" \
|
|
"$(if $(call board_ready,$(b)),ready,no port)" "$(call board_field,$(b),BOARD_DESC)";)
|
|
|
|
# write_stick -- the PC stick image (make boot_image TARGET=PC) onto a USB stick, safely:
|
|
# scripts/write_stick.sh refuses anything but a whole, removable, unmounted USB disk big enough,
|
|
# asks for the device to be typed back, writes, flushes, reads back and compares.
|
|
write_stick:
|
|
@[ -n "$(DEVICE)" ] || { echo "Usage: make write_stick DEVICE=/dev/sdX (lsblk lists the disks)"; exit 1; }
|
|
@bash scripts/write_stick.sh build/boards/pc/lithos-pc.img $(DEVICE)
|
|
|
|
# init -- the packages this tree needs, for one ISA or several (ARCH=amd64,aarch64,riscv64).
|
|
# Common: the build, the disk images (scripts/mkdiskimage.sh; sfdisk is in fdisk), git-lfs for
|
|
# the logs and DoE CSVs (.gitattributes), and the documents: the book (docs/book/Makefile's own
|
|
# list) and the formal volumes and API reference (docs/formal/Makefile: pdflatex, doxygen, dot).
|
|
# Per ISA: its QEMU, its UEFI firmware (kernel/Makefile's paths) and its compiler (kernel/Makefile
|
|
# TOOLCHAIN; aarch64's loader is clang + lld-link). Not installed: syft, which make sbom and
|
|
# boot_image need -- it is not packaged; see the message make sbom prints.
|
|
INIT_COMMON := build-essential make git git-lfs python3 xorriso mtools gdisk dosfstools fdisk \
|
|
pandoc latexmk texlive-xetex texlive-latex-base texlive-latex-recommended \
|
|
texlive-latex-extra texlive-pictures texlive-fonts-recommended lmodern \
|
|
fonts-dejavu fonts-dejavu-extra doxygen graphviz
|
|
INIT_amd64 := qemu-system-x86 ovmf
|
|
INIT_aarch64 := qemu-system-arm qemu-efi-aarch64 gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu clang lld
|
|
INIT_riscv64 := qemu-system-misc qemu-efi-riscv64 gcc-riscv64-linux-gnu binutils-riscv64-linux-gnu
|
|
comma := ,
|
|
INIT_ARCHES := $(subst $(comma),$(space),$(ARCH))
|
|
INIT_BAD := $(filter-out amd64 aarch64 riscv64,$(INIT_ARCHES))
|
|
SUDO := $(if $(filter 0,$(shell id -u)),,sudo)
|
|
|
|
init:
|
|
@[ -n "$(INIT_ARCHES)" ] && [ -z "$(INIT_BAD)" ] || { \
|
|
echo "Usage: make ARCH=<amd64|aarch64|riscv64>[,...] init (e.g. ARCH=amd64,aarch64,riscv64)"; exit 1; }
|
|
@command -v apt-get >/dev/null 2>&1 || { \
|
|
echo "init: needs apt-get (Debian/Ubuntu). The packages are listed in the Makefile, at init."; exit 1; }
|
|
$(SUDO) env DEBIAN_FRONTEND=noninteractive apt-get update -qq
|
|
$(SUDO) env DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
|
|
$(INIT_COMMON) $(foreach a,$(INIT_ARCHES),$(INIT_$(a)))
|
|
git lfs install --local
|
|
@echo ""
|
|
@echo "Ready for $(INIT_ARCHES): make ARCH=<arch> clean qemu, make -C v4 test, make docs"
|
|
|
|
help:
|
|
@sed -n '/^# TARGET is/,/^# =====/p' Makefile | sed '$$d; s/^# \{0,1\}//'
|
|
|
|
help-hosted:
|
|
@$(SUBMAKE) -f v3/Makefile help
|
|
|
|
# Everything else is the hosted v3 build, unchanged. (The empty Makefile
|
|
# rule stops the match-anything rule from trying to remake this file.)
|
|
Makefile: ;
|
|
%:
|
|
@$(SUBMAKE) -f v3/Makefile $@
|