# tk.mk — the kernel build entry point. # # A project Makefile sets PROJECT + SRCS, then includes this file: # # PROJECT := my_app # SRCS := main.c # include ../../make/tk.mk # in-tree example (path relative to the project) # # Out-of-tree (a project copied outside the kernel tree)? Point TK_DIR at the tree and include from # there — tk.mk self-locates its root from its OWN path (TK_ROOT below), so nothing downstream needs # to change: # # TK_DIR ?= /opt/tk-0.1.2 # absolute or relative; override from the environment / CLI # include $(TK_DIR)/make/tk.mk # # make BOARD=host # build + `make run` on the desktop (arch/pthread) # make boards # list every board BOARD= accepts, with a one-line description # make BOARD=host PROFILE=/profiles/tk_profile_nano.h # make BOARD=host print-config # show the resolved board/arch/flags # # Resolution: BOARD -> make/board/.mk (sets TK_ARCH + toolchain + flags + run/flash) # -> make/arch/.mk (sets the arch source list + arch flags/includes). # Add a board or arch = drop in one .mk — nothing here changes. The kernel core is constant. # # Single-pass compile (kernel + arch + board + app in one $(CC) call) — simple and fine for a # small kernel; a prebuilt libtk.a variant is a later refinement. TK_ROOT := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))/..) # Windows: Cygwin make emits /cygdrive//... paths that a NATIVE (non-Cygwin) cross-compiler — # e.g. the xPack riscv-none-elf-gcc used by the qemu_riscv32_virt board — cannot open. Rewrite # TK_ROOT to :/... with pure-make string ops (NOT cygpath: the ambient cygpath is often # MSYS/Git's, which mis-resolves /cygdrive). Cygwin make's include and drive-colon prerequisites # both accept c:/... form. No-op off Windows — a Mac/Linux abspath contains no '/cygdrive/'. ifneq (,$(findstring /cygdrive/,$(TK_ROOT))) tk_drive := $(word 2,$(subst /, ,$(TK_ROOT))) TK_ROOT := $(tk_drive):/$(patsubst /cygdrive/$(tk_drive)/%,%,$(TK_ROOT)) endif BOARD ?= host PROJECT ?= app include $(TK_ROOT)/make/board/$(BOARD).mk include $(TK_ROOT)/make/arch/$(TK_ARCH).mk # The portable kernel core — constant across every board and arch. TK_KERNEL_SRCS := $(TK_ROOT)/kernel/tk_kernel.c $(TK_ROOT)/kernel/tk_sync.c \ $(TK_ROOT)/kernel/tk_trace_ring.c $(TK_ROOT)/kernel/tk_stack.c TK_SRCS := $(TK_KERNEL_SRCS) $(TK_ARCH_SRCS) $(TK_BOARD_SRCS) TK_INC := -I$(TK_ROOT)/kernel -I$(TK_ROOT)/arch/$(TK_ARCH) $(TK_ARCH_INC) $(TK_BOARD_INC) PROFILE_CFLAGS := $(if $(PROFILE),-include $(PROFILE),) # Language standard, pinned PROJECT-WIDE for build reproducibility. Left unset, GCC's default -std # drifts across releases (gnu11 -> gnu17 -> gnu23), silently changing which conformance/UB rules # apply when the toolchain is upgraded or swapped. C99 is the floor the whole tree is verified clean # against under -pedantic-errors. `?=` lets a board that needs GNU/POSIX extensions override it # *before* this point — the host board (pthreads) sets TK_STD := -std=gnu11. Placed first in # ALL_CFLAGS so any residual board-local -std (last one wins in GCC) still takes precedence. TK_STD ?= -std=c99 # Strictness, applied PROJECT-WIDE — no exceptions, vendored SEGGER included (each board's own # -Wall -Wextra stay in TK_CFLAGS). `?=` keeps it overridable for a one-off (e.g. `make TK_WARNINGS=`). TK_WARNINGS ?= -Werror -pedantic-errors ALL_CFLAGS := $(TK_STD) $(TK_CFLAGS) $(TK_ARCH_CFLAGS) $(TK_BOARD_CFLAGS) $(TK_WARNINGS) $(TK_INC) $(PROFILE_CFLAGS) $(CFLAGS) BIN := $(PROJECT)$(TK_BIN_EXT) TK_SIZE ?= $(CROSS)size # footprint tool; boards set CROSS (host -> plain 'size') .PHONY: all run flash size clean print-config boards all: $(BIN) $(BIN): $(SRCS) $(TK_SRCS) $(TK_CC) $(ALL_CFLAGS) $(SRCS) $(TK_SRCS) $(TK_LDFLAGS) -o $@ run: all $(TK_RUN) flash: all $(TK_FLASH) # Footprint: text (code, in flash) / data / bss (RAM). Add PROFILE=$(TK_ROOT)/profiles/tk_profile_nano.h # for the minimal-kernel figure on this arch. size: all $(TK_SIZE) $(BIN) clean: rm -f $(BIN) $(PROJECT) $(PROJECT).exe $(PROJECT).elf $(PROJECT).bin $(PROJECT).hex $(PROJECT).map print-config: @echo "BOARD = $(BOARD) ARCH = $(TK_ARCH)" @echo "CC = $(TK_CC)" @echo "CFLAGS = $(ALL_CFLAGS)" @echo "LDFLAGS = $(TK_LDFLAGS)" @echo "APP = $(SRCS)" @echo "KERNEL = $(TK_KERNEL_SRCS)" @echo "ARCH = $(TK_ARCH_SRCS)" @echo "BOARD_SRCS = $(TK_BOARD_SRCS)" # List every board BOARD= accepts, with a one-line description scraped from each fragment's header # comment (no build; works from any project dir). Add a board = drop in make/board/.mk and it # shows up here automatically — nothing to register. boards: @echo 'Supported boards (build with BOARD=):' @for f in $(sort $(wildcard $(TK_ROOT)/make/board/*.mk)); do \ b=$$(basename $$f .mk); \ d=$$(sed -n '1s/.*\.mk[^A-Za-z0-9]*//p' $$f); \ printf ' %-24s %s\n' "$$b" "$${d:-(no description)}"; \ done