# Radiance build file.

# Core standard library modules.
STD := -pkg std $(patsubst %,-mod %,$(shell cat std.lib))

# Full standard library with test modules.
STD_TEST := $(STD) $(patsubst %,-mod %,$(shell cat std.lib.test))

# Source files.
STD_LIB := $(shell find lib -name '*.rad')
BIN_DIR := bin
RAD_BIN := $(BIN_DIR)/radiance.rv64.dev

# Emulator command used to invoke the self-hosted compiler.
EMU       := $(or $(RAD_EMULATOR),emulator)
EMU_FLAGS := -memory-size=385024 \
			 -data-size=348160 \
			 -stack-size=1024 \
			 -count-instructions
RADIANCE  := $(EMU) $(EMU_FLAGS) -run $(RAD_BIN)

# Verify the emulator binary exists.
EMU_PATH := $(shell command -v $(EMU) 2>/dev/null)

default: emulator $(RAD_BIN)
test: emulator seed-test std-test bin-test kernel-test module-test package-test native-test shared-test sync-test kernel-boot-test trap-test page-test loader-test dispatch-test smp-test termination-test runtime-test bootstrap-test mmio-test cycles-test kernel-size-check scheduling-test

seed-test:
	@seed/test

module-test: emulator $(RAD_BIN)
	@RAD_EMULATOR="$(EMU)" sh test/modules/run

# Emulator command check

emulator:
ifeq ($(EMU_PATH),)
	$(error Emulator not found. Install it or set RAD_EMULATOR to its path)
endif

# Compiler build

SEED      := seed/radiance.rv64
COMPILER_SRC := compiler/radiance.rad compiler/radiance/codegen.rad
SEED_OPTS := $(STD) -pkg radiance $(patsubst %,-mod %,$(COMPILER_SRC)) -entry radiance

$(RAD_BIN): $(STD_LIB) $(COMPILER_SRC) | $(BIN_DIR)
	@echo "radiance $(SEED) => $@"
	@$(EMU) $(EMU_FLAGS) -run $(SEED) $(SEED_OPTS) -o $@

$(BIN_DIR):
	@mkdir -p $@

# Standard Library Tests

STD_LIB_TEST := lib/std.test.rv64

std-test: $(STD_LIB_TEST)
	@echo
	@$(EMU) $(EMU_FLAGS) -run $(STD_LIB_TEST)

$(STD_LIB_TEST): $(STD_LIB) $(RAD_BIN)
	@echo "radiance -test $(STD_TEST) -entry std -o $@"
	@$(RADIANCE) -test $(STD_TEST) -entry std -o $@

clean-std-test:
	@rm -f lib/std.test.rv64 \
		lib/std.test.rv64.debug \
		lib/std.test.rv64.s \
		lib/std.test.rv64.o

# Kernel modules and tests use a separate package and test entry point.
KERNEL_SRC := kernel/kernel.rad $(shell find kernel/kernel -name '*.rad' ! -name 'tests.rad' ! -path '*/tests/*' 2>/dev/null)
KERNEL_ASM := kernel/kernel/boot.ras kernel/kernel/sync.ras kernel/kernel/trap.ras kernel/kernel/pages.ras kernel/kernel/loader.ras kernel/kernel/instances.ras kernel/kernel/domains.ras kernel/kernel/dispatch.ras kernel/kernel/calls.ras kernel/kernel/plic.ras
KERNEL_TEST_SRC := kernel/kernel/tests.rad $(shell find kernel/kernel/tests -name '*.rad' 2>/dev/null)
KERNEL := -pkg kernel $(patsubst %,-mod %,$(sort $(KERNEL_SRC)))
KERNEL_TEST := $(BIN_DIR)/kernel.test.rv64

kernel-test: emulator $(KERNEL_TEST)
	@$(EMU) $(EMU_FLAGS) -run $(KERNEL_TEST)
	@sh test/slots/run "$(EMU)" $(STD_TEST) $(KERNEL) $(patsubst %,-mod %,$(KERNEL_TEST_SRC))

$(KERNEL_TEST): $(KERNEL_SRC) $(KERNEL_TEST_SRC) $(KERNEL_ASM) $(STD_LIB) $(RAD_BIN)
	@echo "radiance kernel tests => $@"
	@$(RADIANCE) -test $(STD_TEST) $(KERNEL) $(patsubst %,-mod %,$(KERNEL_TEST_SRC) $(KERNEL_ASM)) -entry kernel -o $@

# Native kernel image and machine boot checks.
kernel-boot-test: $(BIN_DIR)/kernel.rv64
	@RAD_EMULATOR="$(EMU)" sh test/boot/run

$(BIN_DIR)/kernel.ril $(BIN_DIR)/std.ril &: $(KERNEL_SRC) $(STD_LIB) $(RAD_BIN)
	@$(RADIANCE) $(STD) $(KERNEL) -entry kernel -ril $(BIN_DIR)

$(BIN_DIR)/kernel.build.rv64: kernel/tools/build.rad $(STD_LIB) $(RAD_BIN)
	@$(RADIANCE) $(STD) -pkg build -mod $< -entry build -o $@

# User packages share the ABI source through a staged package module tree.
$(BIN_DIR)/root.ril $(BIN_DIR)/scheduler.ril &: kernel/root.rad kernel/scheduler.rad kernel/mmio.rad kernel/kernel/abi.rad kernel/kernel/sys.rad $(RAD_BIN)
	@mkdir -p $(BIN_DIR)/user/root $(BIN_DIR)/user/scheduler
	@cp kernel/root.rad kernel/scheduler.rad $(BIN_DIR)/user/
	@cp kernel/kernel/abi.rad kernel/kernel/sys.rad $(BIN_DIR)/user/root/
	@cp kernel/kernel/abi.rad kernel/kernel/sys.rad kernel/mmio.rad $(BIN_DIR)/user/scheduler/
	@$(RADIANCE) -pkg root -mod $(BIN_DIR)/user/root.rad -mod $(BIN_DIR)/user/root/abi.rad -mod $(BIN_DIR)/user/root/sys.rad -entry root -ril $(BIN_DIR)
	@$(RADIANCE) -pkg scheduler -mod $(BIN_DIR)/user/scheduler.rad -mod $(BIN_DIR)/user/scheduler/abi.rad -mod $(BIN_DIR)/user/scheduler/sys.rad -mod $(BIN_DIR)/user/scheduler/mmio.rad -entry scheduler -ril $(BIN_DIR)

$(BIN_DIR)/kernel.rv64: $(BIN_DIR)/root.ril $(BIN_DIR)/scheduler.ril $(BIN_DIR)/std.ril $(BIN_DIR)/kernel.ril $(BIN_DIR)/kernel.build.rv64 $(KERNEL_ASM)
	@cat $(KERNEL_ASM) > $(BIN_DIR)/kernel.ras
	@$(EMU) $(EMU_FLAGS) -run $(BIN_DIR)/kernel.build.rv64 -- $(BIN_DIR)/std.ril $(BIN_DIR)/kernel.ril $(BIN_DIR)/kernel.ras $@ $(BIN_DIR)/root.ril $(BIN_DIR)/scheduler.ril

# Separate binary package emission and decode checks.
package-test: emulator $(BIN_DIR)/packages.check.rv64
	@sh test/packages/run

$(BIN_DIR)/packages.check.rv64: test/packages/check.rad $(STD_LIB) $(RAD_BIN)
	@$(RADIANCE) $(STD) -pkg check -mod $< -entry check -o $@

# Native image execution through the configured machine-capable emulator.
native-test: emulator $(BIN_DIR)/native.fixture.rv64
	@$(EMU) -machine -run $(BIN_DIR)/native.fixture.rv64

$(BIN_DIR)/native.build.rv64: test/native/build.rad $(STD_LIB) $(RAD_BIN)
	@$(RADIANCE) $(STD) -pkg build -mod $< -entry build -o $@

$(BIN_DIR)/native.fixture.rv64: $(BIN_DIR)/native.build.rv64
	@$(EMU) -run $< -- $@

# Shared package execution

shared-test: $(BIN_DIR)/shared.build.rv64
	@RAD_EMULATOR="$(EMU)" test/shared/run

$(BIN_DIR)/shared.build.rv64: test/shared/build.rad $(STD_LIB) $(RAD_BIN)
	@$(RADIANCE) $(STD) -pkg build -mod $< -entry build -o $@

# Kernel synchronization machine tests

sync-test: $(BIN_DIR)/sync.build.rv64
	@RAD_EMULATOR="$(EMU)" test/sync/run

$(BIN_DIR)/sync.build.rv64: test/sync/build.rad $(STD_LIB) $(RAD_BIN)
	@$(RADIANCE) $(STD) -pkg build -mod $< -entry build -o $@

# Binary Tests

BIN_TEST_DIR := test/tests
# Only tests with `//! returns:` are compiled to binaries and executed.
BIN_TEST_EXE_SRC := $(shell grep -rl '^//! returns:' $(BIN_TEST_DIR))
BIN_TEST_RAD_EXE_SRC := $(filter %.rad,$(BIN_TEST_EXE_SRC))
BIN_TEST_RAS_EXE_SRC := $(filter %.ras,$(BIN_TEST_EXE_SRC))
BIN_TEST_RAD_ASM_SRC := $(wildcard $(BIN_TEST_RAD_EXE_SRC:.rad=.ras))
BIN_TEST_RAD_START_SRC := $(wildcard $(BIN_TEST_RAD_EXE_SRC:.rad=.start.ras))
BIN_TEST_EXE_BIN := $(patsubst %.rad,%.rv64,$(BIN_TEST_RAD_EXE_SRC)) \
	$(patsubst %.ras,%.rv64,$(BIN_TEST_RAS_EXE_SRC))
BIN_RUNNER   := test/runner.rv64
BIN_TEST_RUN := test/run

bin-test: $(BIN_RUNNER) $(BIN_TEST_EXE_BIN)
	@echo
	@$(BIN_TEST_RUN)

# Runner binary: the lowering IL checker.
$(BIN_RUNNER): test/runner.rad $(STD_LIB) $(RAD_BIN)
	@echo "radiance test/runner.rad => $@"
	@$(RADIANCE) $(STD) -pkg runner -mod test/runner.rad -entry runner -o $@

# A `.rad` executable test can have a same-basename `.ras` module.
$(patsubst %.ras,%.rv64,$(BIN_TEST_RAD_ASM_SRC)): %.rv64: %.ras
$(patsubst %.start.ras,%.rv64,$(BIN_TEST_RAD_START_SRC)): %.rv64: %.start.ras

# Compile each executable test to a binary.
$(BIN_TEST_DIR)/%.rv64: $(BIN_TEST_DIR)/%.rad $(RAD_BIN)
	@echo "radiance $< => $@"
	@$(RADIANCE) -pkg test -mod $< $(patsubst %,-mod %,$(wildcard $(@:.rv64=)/*.rad)) $(patsubst %,-start %,$(wildcard $(@:.rv64=.start.ras))) $(patsubst %,-mod %,$(wildcard $(@:.rv64=.ras))) -o $@

$(BIN_TEST_DIR)/%.rv64: $(BIN_TEST_DIR)/%.ras $(BIN_RUNNER)
	@echo "asm $< => $@"
	@$(EMU) $(EMU_FLAGS) -run $(BIN_RUNNER) -- assemble $< $@

clean-bin-test:
	@rm -f $(BIN_RUNNER) \
		$(BIN_RUNNER:.rv64=.rv64.debug) \
		$(BIN_RUNNER:.rv64=.rv64.s) \
		$(BIN_RUNNER:.rv64=.rv64.o) \
		$(BIN_TEST_EXE_BIN) \
		$(wildcard $(BIN_TEST_DIR)/*.rv64.debug) \
		$(wildcard $(BIN_TEST_DIR)/*.rv64.s)

seed:
	seed/update

clean-rad:
	rm -rf bin/*
	rm -f seed/radiance.rv64.s[0-9]*

clean: clean-std-test clean-bin-test clean-rad

t: test
c: clean

.PHONY: test clean default seed-test std-test bin-test kernel-test package-test native-test shared-test sync-test seed \
	clean-std-test clean-bin-test clean-rad emulator
.SUFFIXES:
.DELETE_ON_ERROR:
.SILENT:

# Integer trap contexts and nested machine interrupts.
trap-test: $(BIN_DIR)/sync.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/trap/run

# Physical frame zeroing at native addresses.
page-test: $(BIN_DIR)/sync.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/pages/run

# Runtime package compilation and execution through the production kernel.
.PHONY: loader-test
loader-test: $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/loader/run

# Native preemption across user code, idle, and retained machine work.
.PHONY: dispatch-test
dispatch-test: $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/dispatch/run

# Concurrent user contexts and metadata transactions on every online hart.
.PHONY: smp-test
smp-test: $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/dispatch/run smp "1 2 8" 7

# User exit, abort, and CPU faults preserve the surviving parent's execution.
termination-test: emulator $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/dispatch/run termination

.PHONY: termination-test

# Scheduled compilation, cancellation, retry, and shared executable publication.
runtime-test: emulator $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/runtime/run

.PHONY: runtime-test

# Root lifetime and resource authority across the boot-package handoff.
bootstrap-test: emulator $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/bootstrap/run

.PHONY: bootstrap-test

# Production root and scheduler packages execute all authority changes in user mode.
.PHONY: scheduling-test
scheduling-test: $(BIN_DIR)/kernel.rv64
	@RAD_EMULATOR="$(EMU)" sh test/scheduling/run

# Checked user-mode register access, including rejection before device effects.
.PHONY: mmio-test
mmio-test: $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/dispatch/run mmio

# Physical source-line limits and fixed native image memory.
.PHONY: kernel-size-check
kernel-size-check: $(BIN_DIR)/kernel.rv64
	@python3 test/acceptance/sizes

# Full sibling-emulator and kernel validation with retained acceptance reports.
.PHONY: kernel-acceptance
kernel-acceptance:
	@sh test/acceptance/run

.PHONY: cycles-test
cycles-test: $(RAD_BIN) $(BIN_DIR)/kernel.build.rv64
	@RAD_EMULATOR="$(EMU)" sh test/cycles/run
