RISCV_PREFIX ?= riscv64-unknown-elf-

CC := $(RISCV_PREFIX)gcc
OBJCOPY := $(RISCV_PREFIX)objcopy
OBJDUMP := $(RISCV_PREFIX)objdump
READELF := $(RISCV_PREFIX)readelf
NM := $(RISCV_PREFIX)nm
SIZE := $(RISCV_PREFIX)size
HOST_CC ?= cc

LOCAL_RV_ENV := $(abspath ../../../../../edu/rv32i-hazard3-student-env)
ifneq ($(wildcard /opt/rv-env/vendor/Hazard3/test/sim/common/init.S),)
  RV_ENV_ROOT ?= /opt/rv-env
else
  RV_ENV_ROOT ?= $(LOCAL_RV_ENV)
endif
H3_ROOT ?= $(RV_ENV_ROOT)/vendor/Hazard3
H3_COMMON ?= $(H3_ROOT)/test/sim/common
H3_INIT ?= $(H3_COMMON)/init.S
LOCAL_H3_TB := $(H3_ROOT)/test/sim/tb_verilator/tb
PREBUILT_H3_TB := $(abspath ../../freertos/lab-rv32i-freertos-c-scheduler/vendor/Hazard3/test/sim/tb_verilator/tb)
H3_TB ?= $(if $(wildcard $(LOCAL_H3_TB)),$(LOCAL_H3_TB),$(if $(wildcard $(PREBUILT_H3_TB)),$(PREBUILT_H3_TB),$(LOCAL_H3_TB)))
H3_TB_DIR ?= $(dir $(H3_TB))

PROFILE ?= observe
ifeq ($(PROFILE),observe)
  OPTFLAGS := -O0 -fno-omit-frame-pointer -fno-inline -fno-optimize-sibling-calls
  DBGFLAGS := -g3 -ggdb
else ifeq ($(PROFILE),debug)
  OPTFLAGS := -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls
  DBGFLAGS := -g3 -ggdb
else ifeq ($(PROFILE),release)
  OPTFLAGS := -Os
  DBGFLAGS :=
else
  $(error PROFILE must be observe, debug or release)
endif

ARCH ?= rv32i_zicsr_zifencei
ABI ?= ilp32
BUILD_ROOT ?= build
HOST_BUILD_ROOT ?= host-build
LINKER_SCRIPT := link/hazard3_c09.ld

TASK01 := task01_libc_without_heap
TASK02 := task02_malloc_requires_sbrk
TASK03 := task03_bounded_sbrk
TASK04 := task04_smalloc
TASK05 := task05_picolibc_malloc
SUCCESS_TASKS := $(TASK01) $(TASK03) $(TASK04) $(TASK05)

RV_CFLAGS := -std=c11 -march=$(ARCH) -mabi=$(ABI) -nostdlib -nostartfiles \
	-ffreestanding -fno-builtin -fno-stack-protector \
	-ffunction-sections -fdata-sections $(OPTFLAGS) $(DBGFLAGS) \
	-Wall -Wextra -Wpedantic -Werror -Isrc/runtime
RV_LDFLAGS := -Wl,--no-relax -Wl,--gc-sections -T$(LINKER_SCRIPT)
RV_LIBS := -Wl,--start-group -lc -lgcc -Wl,--end-group
HOST_CFLAGS := -std=c11 -DC09_HOST=1 -DHOST_PRINTF=1 -fno-builtin \
	$(OPTFLAGS) $(DBGFLAGS) -Wall -Wextra -Wpedantic -Werror -Isrc/runtime

.PHONY: all tasks host check full-check clean check-support \
	task1 task2 task3 task4 task5 t1 t2 t3 t4 t5 \
	host-test expected-fail archive-map check-elf testbench hazard3-test card card-check

all: tasks
tasks: $(SUCCESS_TASKS)
host: $(addprefix $(HOST_BUILD_ROOT)/,$(addsuffix /prog,$(SUCCESS_TASKS)))

task1 t1: $(TASK01)
task2 t2: expected-fail
task3 t3: $(TASK03)
task4 t4: $(TASK04)
task5 t5: $(TASK05)

check-support:
	@test -f "$(H3_INIT)" || { echo "Missing Hazard3 init.S: $(H3_INIT)" >&2; exit 1; }
	@test -f "$(LINKER_SCRIPT)" || { echo "Missing $(LINKER_SCRIPT)" >&2; exit 1; }

define rv_task_rules
$(1): $(BUILD_ROOT)/$(1)/prog.bin $(BUILD_ROOT)/$(1)/prog.lst

$(BUILD_ROOT)/$(1)/prog.elf: crt0.S $(H3_INIT) $(LINKER_SCRIPT) $(2) | check-support
	mkdir -p $$(@D)
	$(CC) $(RV_CFLAGS) $(RV_LDFLAGS) -Wl,-Map,$(BUILD_ROOT)/$(1)/prog.map \
		-o $$@ $(H3_INIT) crt0.S $(2) $(RV_LIBS)
	$(SIZE) -A -x $$@

$(BUILD_ROOT)/$(1)/prog.bin: $(BUILD_ROOT)/$(1)/prog.elf
	$(OBJCOPY) -O binary $$< $$@

$(BUILD_ROOT)/$(1)/prog.lst: $(BUILD_ROOT)/$(1)/prog.elf
	$(OBJDUMP) -d -S -M no-aliases,numeric $$< > $$@

$(HOST_BUILD_ROOT)/$(1)/prog: $(2)
	mkdir -p $$(@D)
	$(HOST_CC) $(HOST_CFLAGS) -o $$@ $(2)
endef

$(eval $(call rv_task_rules,$(TASK01),src/tasks/$(TASK01).c))
$(eval $(call rv_task_rules,$(TASK03),src/tasks/$(TASK03).c src/runtime/c09_bounded_sbrk.c))
$(eval $(call rv_task_rules,$(TASK04),src/tasks/$(TASK04).c src/runtime/c09_bounded_sbrk.c src/runtime/c09_smalloc.c))
$(eval $(call rv_task_rules,$(TASK05),src/tasks/$(TASK05).c))

$(BUILD_ROOT)/$(TASK02)/task.o: src/tasks/$(TASK02).c
	mkdir -p $(@D)
	$(CC) $(RV_CFLAGS) -c -o $@ $<

expected-fail: $(BUILD_ROOT)/$(TASK02)/task.o | check-support
	@set -eu; \
	log="$(BUILD_ROOT)/$(TASK02)/expected-link-failure.log"; \
	elf="$(BUILD_ROOT)/$(TASK02)/must-not-exist.elf"; \
	rm -f "$$elf" "$$log"; \
	set +e; \
	$(CC) $(RV_CFLAGS) $(RV_LDFLAGS) -Wl,--wrap=sbrk \
		-Wl,-Map,$(BUILD_ROOT)/$(TASK02)/expected-link-failure.map \
		-o "$$elf" $(H3_INIT) crt0.S $(BUILD_ROOT)/$(TASK02)/task.o \
		$(RV_LIBS) >"$$log" 2>&1; \
	status=$$?; \
	set -e; \
	if [ "$$status" -eq 0 ]; then \
		echo "FAIL: Task02 unexpectedly linked" >&2; rm -f "$$elf"; exit 1; \
	fi; \
	test ! -e "$$elf"; \
	grep -q "undefined reference to.*__wrap_sbrk" "$$log"; \
	grep -q "libc_stdlib_malloc.c.o" "$$log"; \
	echo "PASS Task02: controlled link failure (__wrap_sbrk missing)"

host-test: host
	./tests/test_host.sh --already-built

archive-map: $(BUILD_ROOT)/$(TASK01)/prog.elf $(BUILD_ROOT)/$(TASK05)/prog.elf
	./tests/check_archive_maps.sh

check-elf: tasks
	./tests/check_elf.sh

testbench:
	@if [ ! -x "$(H3_TB)" ]; then $(MAKE) -C "$(H3_TB_DIR)"; fi
	@test -x "$(H3_TB)"

hazard3-test: tasks testbench
	@set -eu; \
	for task in $(SUCCESS_TASKS); do \
		echo "RUN Hazard3 $$task"; \
		"$(H3_TB)" --bin "$(abspath $(BUILD_ROOT))/$$task/prog.bin" \
			--cycles 1000000 --cpuret; \
	done

card:
	./scripts/build_new_card.sh
	GITEA_SHA=$${GITEA_SHA:-0000000000000000000000000000000000000000} \
		./scripts/render_new_pdf.sh

card-check: card
	./tests/check_card.sh

check: host-test expected-fail archive-map check-elf
full-check: check hazard3-test card-check

clean:
	rm -rf $(BUILD_ROOT) $(HOST_BUILD_ROOT)
