MAKEFLAGS += --no-builtin-rules
.ONESHELL:

# configurables

CC ?= cc
CC := $(CC)

CXX ?= cxx
CXX := $(CXX)

CFLAGS ?= -O2 -DNDEBUG
CXXFLAGS ?=
LDFLAGS ?=

CXXFLAGS := $(CFLAGS) $(CXXFLAGS)

# the following line could remove the need of setting ERLANGDIR externally
# however it hangs the whole castle when make is invoked through bear
ERLANGDIR ?= $(shell erl -eval 'io:format("~s", [code:root_dir()])' -eval 'halt()' -noshell)

LLVM_CONFIG ?= llvm-config
LLVM_BINDIR := $(shell command -v $(LLVM_CONFIG) > /dev/null && $(LLVM_CONFIG) --bindir)

ZSTDVER := 1.5.5
ZSTDDIR := priv/zstd
ZSTDTAR := vendor/zstd.tar.gz

# end configurables

COMMON_FLAGS := \
	-fno-unwind-tables -fno-asynchronous-unwind-tables \
	-fno-plt \
	-fPIC \
	-fvisibility=hidden

BASE_CXXFLAGS = \
	-std=c++17 \
	-isystem$(ERLANGDIR)/usr/include \
	-isystem$(ZSTDDIR) \
	-DZSTD_STATIC_LINKING_ONLY=1 \
	-Wall -Wextra -Wvla -Wpedantic -Wconversion -Winline \
	-MT $@ -MMD -MF priv/c_out/$*.d \
	$(COMMON_CFLAGS)

BASE_LDFLAGS := \
	-shared \
	-L$(ERLANGDIR)/usr/lib \
	-lei

DEBUG_CXXFLAGS := -O1 -g -fno-optimize-sibling-calls -fno-omit-frame-pointer
DEBUG_LDFLAGS := -g

SOURCES := $(wildcard c_src/*.cpp)
OBJECTS := $(SOURCES:%.cpp=priv/c_out/%.o)
DEPFILES := $(SOURCES:%.cpp=priv/c_out/%.d)

ALL_OBJECTS := $(OBJECTS) $(ZSTDDIR)/libzstd.a

all: priv/c_out/ex_zstd_nif.so

priv/c_out/ex_zstd_nif.so: $(ALL_OBJECTS)
	mkdir -p $(@D)
	$(CXX) $(ALL_OBJECTS) $(BASE_LDFLAGS) $(LDFLAGS) -o $@

priv/c_out/c_src/%.o: c_src/%.cpp $(ZSTDDIR)/zstd.h
	mkdir -p $(@D)
	$(CXX) $< $(BASE_CXXFLAGS) $(CXXFLAGS) -c -o $@

$(ZSTDDIR)/libzstd.a: $(ZSTDDIR)/zstd.h
	$(MAKE) \
		CC="$(CC)" \
		CFLAGS="-std=c11 $(COMMON_FLAGS) $(CFLAGS)" \
		ZSTD_STATIC_LINKING_ONLY=1 \
		ZSTD_LIB_COMPRESSION=1 \
		ZSTD_LIB_DECOMPRESSION=1 \
		ZSTD_LIB_DICTBUILDER=1 \
		ZSTD_LIB_DEPRECATED=0 \
		ZSTD_LIB_MINIFY=1 \
		-C $(@D) \
		libzstd.a-mt

# this has no explicit dependency on $(ZSTDTAR)
# this is intentional! The tar is part of the package, if it's missing human judgment is required.
$(ZSTDDIR)/zstd.h:
	mkdir -p $(@D)
	tar -zx -f $(ZSTDTAR) -C $(@D) --strip-components=2 zstd-$(ZSTDVER)/lib

-include $(DEPFILES)

.PHONY: format
format:
	$(LLVM_BINDIR)/clang-format --style=file -i $(SOURCES)

.PHONY: lint
lint:
	$(LLVM_BINDIR)/clang-tidy --fix $(SOURCES)

.PHONY: check
check: compile_commands.json
	$(LLVM_BINDIR)/clang-format --style=file --dry-run --Werror $(SOURCES)
	$(LLVM_BINDIR)/clang-tidy --warnings-as-errors='*' $(SOURCES)

.PHONY: clean
clean:
	-$(MAKE) -C $(ZSTDDIR) clean
	rm -rf priv

.PHONY: really-clean
really-clean:
	rm -rf priv $(ZSTDDIR)

# pass ERLANGDIR explicitly because bear hangs
compile_commands.json: $(ZSTDDIR)/zstd.h
	bear -- $(MAKE) ERLANGDIR=$(ERLANGDIR) --always-make $(OBJECTS)

$(ZSTDTAR):
	mkdir -p $(@D)
	curl -sSL -o $(ZSTDTAR) https://github.com/facebook/zstd/releases/download/v$(ZSTDVER)/zstd-$(ZSTDVER).tar.gz

$(ZSTDTAR).sig:
	mkdir -p $(@D)
	curl -sSL -o $(ZSTDTAR).sig https://github.com/facebook/zstd/releases/download/v$(ZSTDVER)/zstd-$(ZSTDVER).tar.gz.sig

$(ZSTDTAR).asc:
	curl -sSL -o $(ZSTDTAR).asc http://zstd.net/signing-key.asc

.PHONY: zstd-pull
zstd-pull: $(ZSTDTAR) $(ZSTDTAR).sig $(ZSTDTAR).asc

.PHONY: zstd-verify
zstd-verify: $(ZSTDTAR) $(ZSTDTAR).sig $(ZSTDTAR).asc
	gpg --import $(ZSTDTAR).asc
	gpg --verify $(ZSTDTAR).sig $(ZSTDTAR)
