# levdist makefile v1.0


# Variables =====================================

# Compiler options
CC=mpicc
CXX=g++
FLAGS=-Wall -Wextra -pthread -std=gnu11 -fopenmp
CFLAGS=$(FLAGS)
CXXFLAGS=$(FLAGS)
LIBS=

# Configure flags according to the target
debug: FLAGS += -g
release: FLAGS += -O3 -DNDEBUG

# Directories
BIN_DIR=bin
BUILD_DIR=build
DOC_DIR=doc
SRC_DIR=src
TEST_A_DIR=test
TEST_U_DIR=test_u
TEST_A_OUT_DIR=$(BUILD_DIR)/$(TEST_A_DIR)
TEST_U_OUT_DIR=$(BUILD_DIR)/$(TEST_U_DIR)
INST_DIR=$(HOME)/bin

# Project variables
APP_NAME=levdist
#EXECUTABLE=$(BIN_DIR)/$(shell basename `pwd`)
EXECUTABLE=$(BIN_DIR)/$(APP_NAME)
FILTER_MEMCHECK=| grep 'lost'

# Files and test cases
HEADERS=$(wildcard $(SRC_DIR)/*.h)
SOURCES=$(wildcard $(SRC_DIR)/*.c*)
OBJECTS=$(SOURCES:$(SRC_DIR)/%.c=$(BUILD_DIR)/%.o)

# Test cases ASCII and Unicode
TESTS_A=$(wildcard $(TEST_A_DIR)/*)
TESTS_A_OUT=$(TESTS_A:$(TEST_A_DIR)/%=$(TEST_A_OUT_DIR)/%)
TESTS_A_MEMCHECK=$(TESTS_A:$(TEST_A_DIR)/%=$(TEST_A_OUT_DIR)/%_memcheck)
TESTS_U=$(wildcard $(TEST_U_DIR)/*)
TESTS_U_OUT=$(TESTS_U:$(TEST_U_DIR)/%=$(TEST_U_OUT_DIR)/%)
TESTS_U_MEMCHECK=$(TESTS_U:$(TEST_U_DIR)/%=$(TEST_U_OUT_DIR)/%_memcheck)


# Building ======================================

# Default rule is debug target
debug: $(EXECUTABLE)
release: $(EXECUTABLE)

# All targets
all: debug doc test

# Linker call
$(EXECUTABLE): $(OBJECTS) | $(BIN_DIR)
	$(CXX) $(CFLAGS) -o $@ $^ $(LIBS)

# Compile C source into .o object file
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c $(HEADERS) | $(BUILD_DIR)
	$(CC) -c $(CFLAGS) $< -o $@

# Compile C++ source into .o object file
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp $(HEADERS) | $(BUILD_DIR)
	$(CXX) -c $(CXXFLAGS) $< -o $@

# Documentation
.PHONY: doc | $(DOC_DIR)
doc:
	doxygen Doxyfile


# Testing =======================================

# Function to test a test case directory
# $(call test_dir,in_dir,out_dir)
test_dir = @mkdir -p $2 ;\
	cd $(1) ;\
	../../$(EXECUTABLE) $(shell cat $(1)/args.txt) < input.txt > ../../$(2)/output.txt 2> ../../$(2)/error.txt || true ;\
	cd ../.. ;\
	$(HOME)/bin/icdiff $(1)/output.txt $(2)/output.txt || true ;\
	$(HOME)/bin/icdiff $(1)/error.txt $(2)/error.txt || true ;\
	rm -rf $(2) ;\
	$(eval testcnt=$(shell echo $$(($(testcnt)+1))))

# Testing in ASCII
.PHONY: test
test: debug $(TESTS_A_OUT) | $(TEST_A_OUT_DIR)
	@echo "$(testcnt) ascii cases tested, for details call 'make test -n'"

.PHONY: $(TEST_A_OUT_DIR)/%
$(TEST_A_OUT_DIR)/%: $(TEST_A_DIR)/% | icdiff
	$(call test_dir,$<,$@)

# Testing in Unicode
.PHONY: test_u
test_u: debug $(TESTS_U_OUT) | $(TEST_U_OUT_DIR)
	@echo "$(testcnt) unicode cases tested, for details call 'make test_u -n'"

.PHONY: $(TEST_U_OUT_DIR)/%
$(TEST_U_OUT_DIR)/%: $(TEST_U_DIR)/% | icdiff
	$(call test_dir,$<,$@)


# Profiling =====================================

# Function to profile a test case directory
# $(call memcheck_dir,in_dir)
memcheck_dir = 	@cd $(1) ;\
	echo "cd $(1) && valgrind ../../$(EXECUTABLE) -Q $(shell cat $(1)/args.txt) < input.txt" ;\
	valgrind --tool=memcheck ../../$(EXECUTABLE) -Q $(shell cat $(1)/args.txt) < input.txt 2>&1 > /dev/null $(FILTER_MEMCHECK) || true ;\
	cd ../..

# Memcheck with ASCII test cases
.PHONY: memcheck
memcheck: debug $(TESTS_A_MEMCHECK)

.PHONY: $(TEST_A_OUT_DIR)/%_memcheck
$(TEST_A_OUT_DIR)/%_memcheck: $(TEST_A_DIR)/%
	$(call memcheck_dir,$<)

# Memcheck with Unicode test cases
.PHONY: memcheck_u
memcheck_u: debug $(TESTS_U_MEMCHECK)

.PHONY: $(TEST_U_OUT_DIR)/%_memcheck
$(TEST_U_OUT_DIR)/%_memcheck: $(TEST_U_DIR)/%
	$(call memcheck_dir,$<)


# Misc ==========================================

# Remove output directories
.PHONY: clean
clean:
	rm -rf $(BIN_DIR) $(BUILD_DIR) $(DOC_DIR)

# Install a copy of the executable in ~/bin
install: release | $(INST_DIR)
	cp -p $(EXECUTABLE) $(INST_DIR)

# Uninstall the copy of the executable in ~/bin
.PHONY: uninstall
uninstall:
	rm -f $(INST_DIR)/$(APP_NAME)


# Directories ===================================
# Rules for creating output directories

$(BIN_DIR):
	mkdir -p $@

$(BUILD_DIR):
	mkdir -p $@

$(TEST_A_OUT_DIR):
	mkdir -p $@

$(TEST_U_OUT_DIR):
	mkdir -p $@

$(INST_DIR):
	mkdir -p $@


# 3rd party =====================================

icdiff: $(HOME)/bin/icdiff

$(HOME)/bin/icdiff:
	mkdir -p $(HOME)/bin
	wget -q https://raw.githubusercontent.com/jeffkaufman/icdiff/release-1.9.2/icdiff -O $@
	chmod +x $@