# SPDX-License-Identifier: AGPL-3.0-only
#
# Top-level convenience targets. `make help` lists everything.

NAME    := scradle
VERSION := $(shell sed -n 's/.*MODULE_VERSION("\(.*\)").*/\1/p' kernel/main.c)
KVER    ?= $(shell uname -r)
KDIR    ?= /lib/modules/$(KVER)/build

PREFIX      ?= /usr/local
DKMS_SRC    := /usr/src/$(NAME)-$(VERSION)
MODULE_DIR  := /lib/modules/$(KVER)/extra

.DEFAULT_GOAL := help

## build: compile the kernel module against $(KVER)
build:
	$(MAKE) -C kernel KVER=$(KVER) KDIR=$(KDIR)

## tools: build the scradlectl command-line tool
tools:
	$(MAKE) -C tools

## all: build the module and the tools
all: build tools

## test: boot a VM and run the suite against the running kernel
test:
	./tests/run_qemu.py

## test-matrix: run the suite against every locally available kernel
test-matrix:
	./tests/run_matrix.py

## check-kernels: compile against every kernel in tests/kernels.txt (in docker)
check-kernels:
	./sandbox.sh kernels

## test-kernels: boot the suite on every kernel in tests/kernels.txt
test-kernels:
	./sandbox.sh boot-kernels

## fetch-kernel: provision one kernel, e.g. make fetch-kernel K=6.12
fetch-kernel:
	./scripts/fetch-kernel.py fetch $(K)

## new-kernels: list mainline releases newer than the matrix covers
new-kernels:
	@./scripts/fetch-kernel.py list --newer-than \
	    $$(grep -E '^[0-9]+\.[0-9]+$$' tests/kernels.txt | tail -1)

## check-build: compile against every installed kernel (no boot, fast)
check-build:
	@fail=0; \
	for k in /lib/modules/*/build; do \
	    v=$$(basename $$(dirname $$k)); \
	    d=$$(mktemp -d); \
	    cp kernel/*.c kernel/*.h kernel/Makefile kernel/probe.sh $$d/; \
	    rm -f $$d/scradle_config.h; \
	    if $(MAKE) -C $$d KDIR=$$k >/dev/null 2>$$d/err; then \
	        echo "  OK    $$v"; \
	    else \
	        echo "  FAIL  $$v"; grep -E 'error:' $$d/err | head -3; fail=1; \
	    fi; \
	    rm -rf $$d; \
	done; \
	exit $$fail

## check-build-arm64: cross-compile for arm64 in a container (no boot)
#
# Separate from check-build rather than folded into it: it needs docker
# and, the first time, a kernel source download and a ~10 minute vmlinux
# build. check-build is the target people run before every commit and it
# has to stay fast.
#
# It matters because arm64 has a weak memory model. A missing barrier that
# x86's store ordering hides is a real bug there, and a driver that does
# not compile for the architecture cannot be tested on it at all.
check-build-arm64:
	./docker/arm64-build.sh build

## install: install via DKMS so the module survives kernel upgrades
install:
	./scripts/install.sh

## install-manual: install the module for the running kernel only
install-manual: build tools
	install -d $(MODULE_DIR)
	install -m 644 kernel/$(NAME).ko $(MODULE_DIR)/
	depmod -a $(KVER)
	install -d $(PREFIX)/sbin
	install -m 755 tools/scradlectl $(PREFIX)/sbin/
	@echo "installed; load with: modprobe $(NAME)"

## uninstall: remove the module and tools, however they were installed
uninstall:
	-modprobe -r $(NAME) 2>/dev/null || true
	-dkms remove -m $(NAME) -v $(VERSION) --all 2>/dev/null || true
	-rm -rf $(DKMS_SRC)
	-rm -f $(MODULE_DIR)/$(NAME).ko
	-rm -f $(PREFIX)/sbin/scradlectl
# The boot-time attach hooks. Removed here, but no initrd is regenerated:
# rebuilding an initrd is the one step in this file that could leave a
# machine unable to boot, and it is not something a `make uninstall` should
# do behind somebody's back. A hook left inside an existing initrd is
# harmless -- it finds no module, logs one line and lets the boot continue --
# and /etc/scradle/boot.conf is left alone because it is the operator's.
	-rm -rf /usr/lib/dracut/modules.d/90$(NAME)
	-rm -f /usr/lib/dracut/dracut.conf.d/90-$(NAME).conf
	-rm -f /usr/share/initramfs-tools/hooks/$(NAME)
	-rm -f /usr/share/initramfs-tools/scripts/local-top/$(NAME)
	-rm -rf /usr/lib/$(NAME)
	-depmod -a $(KVER)
	@echo "uninstalled. Existing initrds are untouched; regenerate them"
	@echo "yourself if this machine had boot-time tracking enabled."

## load: insert the module built in-tree (for development)
load: build
	insmod kernel/$(NAME).ko

## unload: remove the module
unload:
	rmmod $(NAME)

## clean: remove build artifacts
clean:
	$(MAKE) -C kernel clean
	$(MAKE) -C tools clean

## version: print the module version
version:
	@echo $(VERSION)

## help: show this message
help:
	@echo "$(NAME) $(VERSION) -- targets:"
	@echo
	@sed -n 's/^## \(.*\): \(.*\)/  \1|\2/p' $(MAKEFILE_LIST) | \
	    awk -F'|' '{printf "  %-16s %s\n", $$1, $$2}'
	@echo
	@echo "  variables: KVER=$(KVER) PREFIX=$(PREFIX)"

.PHONY: build tools all test test-matrix check-build check-build-arm64 \
	check-kernels test-kernels fetch-kernel new-kernels \
	install install-manual \
	uninstall load unload clean version help
