.PHONY: help full prng testing uuid core primitives all clean builddir build-test run-test codegen install build-lib ccodegen

# External variables
CC             = clang
CXX            = clang++
AR             = ar
BUILD_TYPE     = Debug
BUILD_DIR      = libwapp-build/$(PLATFORM)-$(BUILD_TYPE)
INSTALL_PREFIX = dist
CODEGEN_INPUT  = ""
RUNTIME_ASSERT = true

# Internal variables
override CFLAGS              = -Wall -Wextra -Werror -pedantic -Isrc
override LIBFLAGS            = -fPIC
override CSTD               := -std=gnu11
override CXXSTD             := -std=gnu++11
override KERNEL             := $(shell uname -s)
override MACHINE            := $(shell uname -m)
override PLATFORM           := $(KERNEL)_$(MACHINE)
override TEST_INCLUDE       := -Isrc $(shell find tests -type d | xargs -I{} echo -n "-I{} ")
override TEST_SRC           := $(shell find tests -type f -name "*.c" | xargs -I{} echo -n "{} ")
override TEST_C_SRC         := src/wapp.c $(TEST_SRC)
override TEST_CXX_SRC       := $(shell find tests -type f -name "*.cc" | xargs -I{} echo -n "{} ")
override LIB_NAME           := wapp
override OBJ_OUT            := $(BUILD_DIR)/$(LIB_NAME).o
override LIB_OUT            := $(BUILD_DIR)/lib$(LIB_NAME).a
override TEST_C_OUT         := $(BUILD_DIR)/wapptest
override TEST_CXX_OUT       := $(BUILD_DIR)/wapptestcc
override ABS_INSTALL_PREFIX := $(shell mkdir -p $(INSTALL_PREFIX) && realpath $(INSTALL_PREFIX))
override INCLUDE_INSTALL    := $(ABS_INSTALL_PREFIX)/include/$(LIB_NAME)
override LIB_INSTALL        := $(ABS_INSTALL_PREFIX)/lib
override HEADER_INSTALL_CMD := scripts/header_install.sh

ifeq ($(origin BUILD_FLAGS), undefined)
ifeq ($(BUILD_TYPE),Debug)
	BUILD_FLAGS += -g -fsanitize=address,undefined -DWAPP_DEBUG_ASSERT
else ifeq ($(BUILD_TYPE),RelWithDebInfo)
	BUILD_FLAGS += -g -O2 -fsanitize=address,undefined -DWAPP_DEBUG_ASSERT
else ifeq ($(BUILD_TYPE),Release)
	BUILD_FLAGS += -O3
else
	$(error Invalid BUILD type '$(BUILD_TYPE)'. Use 'Debug', 'RelWithDebInfo' or 'Release')
endif
endif

# Disable runtime asserts
ifeq ($(RUNTIME_ASSERT), false)
	override BUILD_FLAGS += WAPP_NO_RUNTIME_ASSERT
endif

ifeq ($(CC),gcc)
  # Used to disable the "ASan runtime does not come first in initial library list" error when compiling with gcc
  export ASAN_OPTIONS=verify_asan_link_order=0
endif

# Escape sequences
BOLD              = \033[1m
BLACK             = \033[30m
BG_BLACK          = \033[40m
RED               = \033[31m
BG_RED            = \033[41m
GREEN             = \033[32m
BG_GREEN          = \033[42m
YELLOW            = \033[33m
BG_YELLOW         = \033[43m
BLUE              = \033[34m
BG_BLUE           = \033[44m
MAGENTA           = \033[35m
BG_MAGENTA        = \033[45m
CYAN              = \033[36m
BG_CYAN           = \033[46m
WHITE             = \033[37m
BG_WHITE          = \033[47m
GRAY              = \033[90m
BG_GRAY           = \033[100m
BRIGHT_RED        = \033[91m
BG_BRIGHT_RED     = \033[101m
BRIGHT_GREEN      = \033[92m
BG_BRIGHT_GREEN   = \033[102m
BRIGHT_YELLOW     = \033[93m
BG_BRIGHT_YELLOW  = \033[103m
BRIGHT_BLUE       = \033[94m
BG_BRIGHT_BLUE    = \033[104m
BRIGHT_MAGENTA    = \033[95m
BG_BRIGHT_MAGENTA = \033[105m
BRIGHT_CYAN       = \033[96m
BG_BRIGHT_CYAN    = \033[106m
BRIGHT_WHITE      = \033[97m
BG_BRIGHT_WHITE   = \033[107m
RESET             = \033[0m

ECHO_E = echo -e
ifeq ($(KERNEL), Darwin)
	ECHO_E = echo
endif

all: clean builddir codegen run-c-test full run-cc-test

help:
	@$(ECHO_E) "$(BOLD)$(BLUE)Available build variables:$(RESET)"
	@$(ECHO_E) "  $(GREEN)CC$(RESET)                C compiler to use $(YELLOW)(Default: clang)$(RESET)."
	@$(ECHO_E) "  $(GREEN)CXX$(RESET)               C++ compiler to use $(YELLOW)(Default: clang++)$(RESET)."
	@$(ECHO_E) "  $(GREEN)AR$(RESET)                Archiving utility to use for building static libraries $(YELLOW)(Default: ar)$(RESET)."
	@$(ECHO_E) "  $(GREEN)BUILD_TYPE$(RESET)        Build type $(MAGENTA)[Debug | RelWithDebInfo | Release] $(YELLOW)(Default: Debug)$(RESET)."
	@$(ECHO_E) "  $(GREEN)BUILD_DIR$(RESET)         Directory where build files will be written."
	@$(ECHO_E) "  $(GREEN)INSTALL_PREFIX$(RESET)    Prefix where library and include files will be installed."
	@$(ECHO_E) "  $(GREEN)CODEGEN_INPUT$(RESET)     Input file for code generation (See $(CYAN)codegen_custom_data_example.json$(RESET) for an example)."
	@$(ECHO_E) "  $(GREEN)RUNTIME_ASSERT$(RESET)    Whether runtime asserts are enabled $(MAGENTA)[true | false] $(YELLOW)(Default: true)$(RESET)."
	@$(ECHO_E) "  $(GREEN)$(RESET)                  $(BOLD)$(BG_RED)DISCLAIMER:$(RESET) Using this flag is not recommended as it disables safety checks"
	@$(ECHO_E) "  $(GREEN)$(RESET)                  potentially leading to Undefined Behaviour."
	@$(ECHO_E)
	@$(ECHO_E) "$(BOLD)$(BLUE)Available targets:$(RESET)"
	@$(ECHO_E) "  $(GREEN)make$(RESET)              Build, install and test the full wapp library."
	@$(ECHO_E) "  $(GREEN)make full$(RESET)         Build and install the full wapp library."
	@$(ECHO_E) "  $(GREEN)make core$(RESET)         Build and install only the $(CYAN)core$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make prng$(RESET)         Build and install only the $(CYAN)prng$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make uuid$(RESET)         Build and install only the $(CYAN)uuid$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make testing$(RESET)      Build and install only the $(CYAN)testing$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make primitives$(RESET)   Build and install only the $(CYAN)primitives$(RESET) component of the wapp library with all its dependencies."
	@$(ECHO_E) "  $(GREEN)make clean$(RESET)        Clean the build directory."
	@$(ECHO_E) "  $(GREEN)make help$(RESET)         Print this help message and exit."

full: LIB_SRC = src/wapp.c
full: INCLUDES = common core primitives prng testing uuid
full: install

prng: LIB_SRC = src/prng/wapp_prng.c
prng: INCLUDES = common prng
prng: install

testing: LIB_SRC = src/testing/wapp_testing.c
testing: INCLUDES = common core testing
testing: install

uuid: LIB_SRC = src/uuid/wapp_uuid.c
uuid: INCLUDES = common primitives prng
uuid: install

core: LIB_SRC = src/core/wapp_core.c
core: INCLUDES = common core primitives
core: install

primitives: LIB_SRC = src/primitives/wapp_primitives.c
primitives: INCLUDES = common primitives
primitives: install

clean:
	@rm -rf $(BUILD_DIR)

builddir:
	@mkdir -p $(BUILD_DIR)

build-c-test:
	$(CC) $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(TEST_INCLUDE) $(TEST_C_SRC) -o $(TEST_C_OUT)

run-c-test: build-c-test
	@echo -e "\n\033[34;1mRUNNING C TESTS\033[0m"
	@$(TEST_C_OUT)
	@rm $(TEST_C_OUT)

build-cc-test:
	$(CXX) $(CXXSTD) $(CFLAGS) $(BUILD_FLAGS) $(TEST_INCLUDE) $(TEST_CXX_SRC) $(LIB_OUT) -o $(TEST_CXX_OUT)

run-cc-test: build-cc-test
	@echo -e "\n\033[34;1mRUNNING C++ TESTS\033[0m"
	@export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:$(BUILD_DIR) && $(TEST_CXX_OUT)
	@rm $(TEST_CXX_OUT)

codegen:
	python3 -m codegen -f $(CODEGEN_INPUT)

install: codegen build-lib
	@mkdir -p $(LIB_INSTALL)
	@cp -v $(LIB_OUT) $(LIB_INSTALL)
	@mkdir -p $(INCLUDE_INSTALL)
	@bash $(HEADER_INSTALL_CMD) $(LIB_SRC) $(INCLUDE_INSTALL) $(INCLUDES)

build-lib: builddir
	$(CC) -c $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(LIBFLAGS) $(LIB_SRC) -o $(OBJ_OUT)
	$(AR) r $(LIB_OUT) $(OBJ_OUT)
	@rm $(OBJ_OUT)

ccodegen:
	$(CC) $(CSTD) $(CFLAGS) $(BUILD_FLAGS) $(LIBFLAGS) -Isrc/core src/core/wapp_core.c ccodegen/*.c -o ccgen
