SHELL := /bin/sh

-include config.mk

CXX ?= c++
AR ?= ar
RANLIB ?= ranlib
INSTALL ?= install
MKDIR_P ?= mkdir -p

BUILD_DIR ?= build/make
TEST_BUILD_DIR := $(BUILD_DIR)/tests

PREFIX ?= /usr/local
prefix ?= $(PREFIX)
exec_prefix ?= $(prefix)
bindir ?= $(exec_prefix)/bin
libdir ?= $(exec_prefix)/lib
includedir ?= $(prefix)/include
mandir ?= $(prefix)/share/man
man1dir ?= $(mandir)/man1
DESTDIR ?=

BSORT_USE_STD_SORT_CLEANUP ?= 1
BSORT_STD_SORT_CLEANUP_MIN ?= 96

CPPFLAGS += -Iinclude
CXXFLAGS ?= -O3 -DNDEBUG
STANDARD_FLAGS := -std=c++23
WARNING_FLAGS ?= -Wall -Wextra -Wpedantic
DEPENDENCY_FLAGS := -MMD -MP
BSORT_DEFINES := \
	-DBSORT_USE_STD_SORT_CLEANUP=$(BSORT_USE_STD_SORT_CLEANUP) \
	-DBSORT_STD_SORT_CLEANUP_MIN=$(BSORT_STD_SORT_CLEANUP_MIN)
CONFIG_MK := $(wildcard config.mk)

PROGRAM := $(BUILD_DIR)/bsort
LIBRARY := $(BUILD_DIR)/libbsort.a
PROGRAM_OBJECT := $(BUILD_DIR)/bsort-program.o
LIBRARY_OBJECT := $(BUILD_DIR)/bsort-library.o
MANPAGE := man/bsort.1

TEST_NAMES := \
	bsort_library_smoke \
	bsort_library_golden \
	bsort_variable_lengths
TEST_OBJECTS := $(addprefix $(TEST_BUILD_DIR)/,$(addsuffix .o,$(TEST_NAMES)))
TEST_PROGRAMS := $(addprefix $(TEST_BUILD_DIR)/,$(TEST_NAMES))

DEPENDENCIES := \
	$(PROGRAM_OBJECT:.o=.d) \
	$(LIBRARY_OBJECT:.o=.d) \
	$(TEST_OBJECTS:.o=.d)

.PHONY: all bsort libbsort.a test check install uninstall clean distclean

.SECONDARY: $(TEST_OBJECTS)

all: $(PROGRAM) $(LIBRARY)

bsort: $(PROGRAM)

libbsort.a: $(LIBRARY)

$(BUILD_DIR) $(TEST_BUILD_DIR):
	$(MKDIR_P) "$@"

$(PROGRAM_OBJECT): src/bsort.cpp include/bsort/bsort.hpp $(CONFIG_MK) | $(BUILD_DIR)
	$(CXX) $(CPPFLAGS) $(BSORT_DEFINES) $(CXXFLAGS) \
		$(STANDARD_FLAGS) $(WARNING_FLAGS) $(DEPENDENCY_FLAGS) -c "$<" -o "$@"

$(LIBRARY_OBJECT): src/bsort.cpp include/bsort/bsort.hpp $(CONFIG_MK) | $(BUILD_DIR)
	$(CXX) $(CPPFLAGS) $(BSORT_DEFINES) -DBSORT_LIBRARY_ONLY=1 \
		$(CXXFLAGS) $(STANDARD_FLAGS) $(WARNING_FLAGS) \
		$(DEPENDENCY_FLAGS) -c "$<" -o "$@"

$(PROGRAM): $(PROGRAM_OBJECT)
	$(CXX) $(LDFLAGS) "$<" $(LDLIBS) -o "$@"

$(LIBRARY): $(LIBRARY_OBJECT)
	$(AR) rcs "$@" "$<"
	$(RANLIB) "$@"

$(TEST_BUILD_DIR)/%.o: tests/%.cpp include/bsort/bsort.hpp $(CONFIG_MK) | $(TEST_BUILD_DIR)
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(STANDARD_FLAGS) $(WARNING_FLAGS) \
		$(DEPENDENCY_FLAGS) -c "$<" -o "$@"

$(TEST_BUILD_DIR)/%: $(TEST_BUILD_DIR)/%.o $(LIBRARY)
	$(CXX) $(LDFLAGS) "$<" "$(LIBRARY)" $(LDLIBS) -o "$@"

test: all $(TEST_PROGRAMS)
	sh tests/bsort_smoke.sh "$(abspath $(PROGRAM))"
	"$(TEST_BUILD_DIR)/bsort_library_smoke"
	"$(TEST_BUILD_DIR)/bsort_library_golden" tests/data/input tests/data/output
	"$(TEST_BUILD_DIR)/bsort_variable_lengths"

check: test

install: all $(MANPAGE)
	$(INSTALL) -d "$(DESTDIR)$(bindir)"
	$(INSTALL) -m 755 "$(PROGRAM)" "$(DESTDIR)$(bindir)/bsort"
	$(INSTALL) -d "$(DESTDIR)$(libdir)"
	$(INSTALL) -m 644 "$(LIBRARY)" "$(DESTDIR)$(libdir)/libbsort.a"
	$(INSTALL) -d "$(DESTDIR)$(includedir)/bsort"
	$(INSTALL) -m 644 include/bsort/bsort.hpp \
		"$(DESTDIR)$(includedir)/bsort/bsort.hpp"
	$(INSTALL) -d "$(DESTDIR)$(man1dir)"
	$(INSTALL) -m 644 "$(MANPAGE)" "$(DESTDIR)$(man1dir)/bsort.1"

uninstall:
	$(RM) "$(DESTDIR)$(bindir)/bsort"
	$(RM) "$(DESTDIR)$(libdir)/libbsort.a"
	$(RM) "$(DESTDIR)$(includedir)/bsort/bsort.hpp"
	$(RM) "$(DESTDIR)$(man1dir)/bsort.1"
	-rmdir "$(DESTDIR)$(includedir)/bsort"

clean:
	$(RM) $(PROGRAM) $(LIBRARY) $(PROGRAM_OBJECT) $(LIBRARY_OBJECT)
	$(RM) $(TEST_PROGRAMS) $(TEST_OBJECTS) $(DEPENDENCIES)
	-rmdir "$(TEST_BUILD_DIR)"
	-rmdir "$(BUILD_DIR)"

distclean: clean
	$(RM) config.mk config.log config.status

-include $(DEPENDENCIES)
