CC = LC_ALL=C g++
CFLAGS += -std=c++17 -pipe -Wall -Wextra -Werror
CFLAGS += -Wconversion -Wsign-conversion -Wformat-security -Winline -Wno-unused-parameter -Wno-pointer-arith
CFLAGS += -march=native
CFLAGS += -include config.hpp
CFLAGS += -Ofast -flto -fwhole-program -ffast-math -fsingle-precision-constant -ffp-contract=fast
LFLAGS += -Ofast -flto -fwhole-program -ffast-math -fsingle-precision-constant -ffp-contract=fast
LFLAGS += -pthread -lX11 -lXext
LFLAGS += -pipe


NAME = maze-tracer
SOURCES = $(wildcard *.cpp)
HEADERS = $(wildcard *.hpp)
OBJECTS = $(SOURCES:.cpp=.o)


# default targets, build in 'release mode'
.PHONY: build
build: CFLAGS += -DNDEBUG -fno-stack-protector
build: LFLAGS += -no-pie -Wl,-z,relro,-z,now
build: $(NAME)

$(NAME): $(OBJECTS)
    $(CC) -o $(@) $(^) $(LFLAGS)
    @#which checksec >/dev/null 2>&1 && checksec --format=cli --file=$(@) || true
    @which toilet >/dev/null 2>&1 && toilet --font smblock --filter gay "# $(@) #" || true

%.o: %.cpp $(HEADERS) Makefile
    $(CC) $(CFLAGS) -o $(@) -c $(<)


# debug run targets
.PHONY: run
run: $(NAME)
    ./$(NAME)

.PHONY: run-vg
run-vg: CFLAGS += -Og -g
run-vg: LFLAGS += -Og
run-vg: $(NAME)
    valgrind --leak-check=full --show-reachable=yes --track-origins=yes --error-exitcode=42 ./$(NAME)

.PHONY: run-gdb
run-gdb: CFLAGS += -Og -g
run-gdb: LFLAGS += -Og
run-gdb: $(NAME)
    gdb -ex run --args ./$(NAME)


# benchmarking
.PHONY: benchmark
benchmark: CFLAGS += -DNDEBUG -DUSE_X=0 -DUSE_LOG_STATS=0
benchmark: $(NAME)
    ./$(NAME) -b -s 666

.PHONY: callgrind
callgrind: CFLAGS += -DNDEBUG -DUSE_X=0 -DUSE_LOG_STATS=0 -Og -g
callgrind: LFLAGS += -Og
callgrind: callgrind.txt callgrind.png
callgrind.out: $(NAME)
    valgrind --tool=callgrind --callgrind-out-file=callgrind.out ./$(<) -b -s 666

callgrind.txt: callgrind.out
    callgrind_annotate --inclusive=yes --show-percs=yes --tree=both $(<) > $(@)

callgrind.png: callgrind.out
    gprof2dot --format=callgrind --node-label=total-time-percentage --strip -n 0.1 -e 0.0 ${<} | dot -Tpng -o ${@}


# gcc profiling and profiled build for even more optimized release build
.PHONY: profile
profile: CFLAGS += -DNDEBUG -DUSE_X=0
profile: CFLAGS += -fprofile-generate -fprofile-arcs -ftest-coverage
profile: LFLAGS += -fprofile-generate -fprofile-arcs
profile: main.gcda main.cpp.gcov profile/index.html
main.gcda: clean.o $(NAME)
    @rm -f -- *.gcda
    ./$(NAME) -b -s 666

main.cpp.gcov: main.gcda
    gcov --branch-probabilities --function-summaries --use-colors --use-hotness-colors --demangled-names --relative-only *.gcda >/dev/null

main.info: main.gcda
    lcov --capture --no-external --rc lcov_branch_coverage=1 --directory . --output-file $(@)

profile/index.html: main.info
    genhtml --legend --branch-coverage --function-coverage --missed --demangle-cpp --output-directory $(dir $(@)) $(<)

.PHONY: profiled
profiled: CFLAGS += -Wno-coverage-mismatch -fprofile-use -fprofile-correction
profiled: LFLAGS += -fprofile-use -fprofile-correction
profiled: CFLAGS += -DNDEBUG -fno-stack-protector
profiled: LFLAGS += -no-pie -Wl,-z,relro,-z,now
profiled: clean.o $(NAME)
    @# XXX: -fprofile-partial-training - unrecognized command line option


# doxygen
.PHONY: docs
docs: docs/index.html

doxygen-awesome.css:
    @# https://github.com/jothepro/doxygen-awesome-css
    wget -nv -O $(@) https://raw.githubusercontent.com/jothepro/doxygen-awesome-css/main/doxygen-awesome.css

docs/index.html: $(SOURCES) $(HEADERS) Doxyfile doxygen-awesome.css
    @mkdir -p $(dir $(@))
    echo "@INCLUDE = Doxyfile\nPROJECT_NAME = $(NAME)\nINPUT = ./\nOUTPUT_DIRECTORY = $(dir $(@))\nHTML_EXTRA_STYLESHEET = doxygen-awesome.css" | doxygen -


# linting
.PHONY: check lint clean.o clean

check: $(SOURCES) $(HEADERS)
    clang-tidy --warnings-as-errors='*' $(^) -- $(CFLAGS)

lint: $(SOURCES) $(HEADERS)
    clang-format --dry-run -Werror -style=file -- $(^)


# clean
.PHONY: clean.o clean

clean.o:
    @rm -f -- $(NAME) $(wildcard *.o)

clean: clean.o
    @rm -f -- $(wildcard *.gcda) $(wildcard *.gcno) $(wildcard *.gcov) $(wildcard *.info) $(wildcard callgrind.*)
    @rm -rf -- ./profile/ ./docs/ *.css