# Backend build.
#
#   make log_write_speed       Compile, run, then delete the log write benchmark
#   make log_write_speed_prof  Same benchmark under perf, then print the report
#   make telemetry_speed       Compile, run, then delete the telemetry benchmark
#   make qdefs                 Generate C++ headers from quill defs into ./qdefs
#   make unit_tests            Build and run every tests/unit_tests/**/*.cpp file
#   make teensy_echo           Connect to a Teensy and check one echo reply
#
# Paths are anchored to this file, so the targets work from any working directory.

MAKEFILE_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST))))
# Source checkout: backend/ is four levels under the repo root. An installed copy lives
# under site-packages, so walk upward until dev_tools/quill is found.
FEATHEROS_ROOT := $(shell d="$(MAKEFILE_DIR)"; found=""; \
	while [ "$$d" != "/" ]; do \
		if [ -d "$$d/dev_tools/quill" ]; then found="$$d"; break; fi; \
		d=$$(dirname "$$d"); \
	done; \
	if [ -n "$$found" ]; then printf '%s\n' "$$found"; \
	else printf '%s\n' "$(abspath $(MAKEFILE_DIR)/../../../..)"; fi)
QUILL_DIR := $(FEATHEROS_ROOT)/dev_tools/quill
QUILL_SRC := $(QUILL_DIR)/src
QUILL_DEFS_DIR := $(abspath $(MAKEFILE_DIR)/../quill_defs/defs)
QDEFS_DIR := $(MAKEFILE_DIR)/qdefs
PYTHON ?= python3

CXX      := g++
CXXFLAGS := -std=c++17 -O3 -Wall -Wextra -Wshadow -I$(MAKEFILE_DIR) -I$(MAKEFILE_DIR)/src/utils -flto -march=native
PERF     := $(firstword $(wildcard /usr/lib/linux-tools/*/perf))

LOG_WRITE_SPEED_SRC := $(MAKEFILE_DIR)/tests/speed_tests/log_write_speed.cpp
TELEMETRY_SPEED_SRC := $(MAKEFILE_DIR)/tests/speed_tests/telemetry_speed.cpp
TELEMETRY_SRC := $(MAKEFILE_DIR)/src/robot/telemetry.cpp
LOG_SRC := $(MAKEFILE_DIR)/src/utils/logging.cpp
TEENSY_ECHO_SRC := $(MAKEFILE_DIR)/tests/teensy_echo.cpp
TEENSY_ECHO_SRCS := \
	$(TEENSY_ECHO_SRC) \
	$(MAKEFILE_DIR)/src/robot/comms/teensy_manager.cpp \
	$(MAKEFILE_DIR)/src/robot/comms/teensy_socket.cpp \
	$(MAKEFILE_DIR)/src/robot/comms/teensy/host/recompile.cpp \
	$(TELEMETRY_SRC) \
	$(MAKEFILE_DIR)/src/utils/logging.cpp \
	$(MAKEFILE_DIR)/src/utils/system.cpp \
	$(MAKEFILE_DIR)/src/utils/run.cpp

.PHONY: log_write_speed log_write_speed_prof telemetry_speed qdefs relax-perms unit_tests teensy_echo

UNIT_TEST_SRCS := $(shell find $(MAKEFILE_DIR)/tests/unit_tests -name '*.cpp')
# host/ is the standalone teensy recompile/flash tool (it has its own main).
BACKEND_SRCS := $(shell find $(MAKEFILE_DIR)/src -name '*.cpp' -not -path '*/comms/teensy/host/*')

# One binary per tests/unit_tests/**/*.cpp. Rebuild, run, then delete it.
# framework.hpp is a C harness; g++ warns on its designated initializers and truncation strncpy.
unit_tests:
	@fail=0; \
	for src in $(UNIT_TEST_SRCS); do \
		bin="/tmp/feather_$$(basename "$$src" .cpp)"; \
		echo "BUILD $$src"; \
		$(CXX) $(CXXFLAGS) -Wno-missing-field-initializers -Wno-stringop-truncation -o "$$bin" "$$src" $(BACKEND_SRCS) || { fail=1; continue; }; \
		"$$bin" || fail=1; \
		rm -f "$$bin"; \
	done; \
	exit $$fail

# `sudo make` creates a root-owned qdefs/. While root, make it writable and give it
# back to the invoking user so a later unsudoed make can delete it.
relax-perms:
	@dir="$(QDEFS_DIR)"; \
	if [ ! -e "$$dir" ]; then exit 0; fi; \
	if [ "$$(id -u)" -eq 0 ]; then \
		chmod -R a+rwX "$$dir"; \
		if [ -n "$${SUDO_USER:-}" ]; then \
			chown -R "$$SUDO_USER:$$(id -g "$$SUDO_USER")" "$$dir"; \
		fi; \
	fi

# Generate C++ headers from quill definition Python modules into ./qdefs/.
qdefs:
	@rm -rf "$(QDEFS_DIR)"
	@mkdir -p "$(QDEFS_DIR)"
	@echo "Building quill definitions: $(QUILL_DEFS_DIR) -> $(QDEFS_DIR)"
	PYTHONPATH="$(QUILL_SRC)" $(PYTHON) -m quill "$(QUILL_DEFS_DIR)" "$(QDEFS_DIR)" -l cpp
	@$(MAKE) --no-print-directory relax-perms

# No -flto: update_telemetry has to stay a real call. With LTO the rings are never
# read back, so the timed loop can be deleted.
telemetry_speed: $(TELEMETRY_SPEED_SRC) $(TELEMETRY_SRC) $(LOG_SRC)
	$(CXX) $(filter-out -flto,$(CXXFLAGS)) -o /tmp/telemetry_speed $(TELEMETRY_SPEED_SRC) $(TELEMETRY_SRC) $(LOG_SRC)
	/tmp/telemetry_speed; status=$$?; rm -f /tmp/telemetry_speed; exit $$status

# Starts the emulated Teensy, then checks one echo and one cansend. Not part of unit_tests.
teensy_echo: $(TEENSY_ECHO_SRCS)
	$(CXX) $(CXXFLAGS) -pthread -o /tmp/teensy_echo $(TEENSY_ECHO_SRCS)
	/tmp/teensy_echo; status=$$?; rm -f /tmp/teensy_echo; exit $$status

log_write_speed: $(LOG_WRITE_SPEED_SRC) $(LOG_SRC)
	$(CXX) $(CXXFLAGS) -o /tmp/log_write_speed $(LOG_WRITE_SPEED_SRC) $(LOG_SRC)
	/tmp/log_write_speed; status=$$?; rm -f /tmp/log_write_speed; exit $$status

log_write_speed_prof: $(LOG_WRITE_SPEED_SRC) $(LOG_SRC)
	$(CXX) $(CXXFLAGS) -g -fno-omit-frame-pointer -o /tmp/log_write_speed $(LOG_WRITE_SPEED_SRC) $(LOG_SRC)
	$(PERF) record -F 999 --call-graph fp -o /tmp/log_write_speed.perf -- /tmp/log_write_speed; \
		status=$$?; \
		$(PERF) report -i /tmp/log_write_speed.perf --stdio --inline --no-children; \
		rm -f /tmp/log_write_speed /tmp/log_write_speed.perf; \
		exit $$status
