# Standalone GNU Make build for libdateconv.
#
# Supported environments:
#   - Linux/macOS with GNU Make
#   - Native Windows CMD with MinGW-w64/mingw32-make
#   - MSYS2/MinGW shells

LIBNAME := dateconv
SRC := date_converter.c
HEADER := date_converter.h

# GNU Make defines CC=cc internally. Native MinGW normally provides gcc.exe.
# Detect native Windows through either the standard OS variable or ComSpec.
HOST_WINDOWS := 0
ifneq ($(filter Windows_NT,$(OS)),)
HOST_WINDOWS := 1
else ifneq ($(strip $(ComSpec)$(COMSPEC)),)
HOST_WINDOWS := 1
endif

ifeq ($(HOST_WINDOWS),1)
ifeq ($(origin CC),default)
CC := gcc
endif
endif

# Read the version macros through the C preprocessor. This works with GCC and
# Clang in native Windows CMD as well as Unix-like shells.
VERSION_MAJOR := $(strip $(shell echo DATE_CONVERTER_VERSION_MAJOR | $(CC) -imacros "$(HEADER)" -E -P -x c -))
VERSION_MINOR := $(strip $(shell echo DATE_CONVERTER_VERSION_MINOR | $(CC) -imacros "$(HEADER)" -E -P -x c -))
VERSION_PATCH := $(strip $(shell echo DATE_CONVERTER_VERSION_PATCH | $(CC) -imacros "$(HEADER)" -E -P -x c -))

ifeq ($(strip $(VERSION_MAJOR)$(VERSION_MINOR)$(VERSION_PATCH)),)
$(error unable to read the version from $(HEADER))
endif

VERSION := $(VERSION_MAJOR).$(VERSION_MINOR).$(VERSION_PATCH)
SOVERSION := $(VERSION_MAJOR)
PIC_BUILD_DIR := .libs

BUILD_SHARED_INPUT_ORIGIN := $(origin BUILD_SHARED)
BUILD_STATIC_INPUT_ORIGIN := $(origin BUILD_STATIC)
BUILD_SHARED ?= 1
BUILD_STATIC ?= 1

CC_PREDEFINED_MACROS := $(shell echo | $(CC) -dM -E -x c -)

ifneq ($(findstring __CYGWIN__,$(CC_PREDEFINED_MACROS)),)
PLATFORM := cygwin
EXEEXT := .exe
else ifneq ($(findstring __MSYS__,$(CC_PREDEFINED_MACROS)),)
PLATFORM := msys
EXEEXT := .exe
else ifeq ($(HOST_WINDOWS),1)
PLATFORM := windows
EXEEXT := .exe
else
UNAME_S := $(shell uname -s 2>/dev/null)
ifeq ($(UNAME_S),Darwin)
PLATFORM := macos
else
PLATFORM := unix
endif
EXEEXT :=
endif

WINDOWS_ABI := $(if $(filter windows cygwin msys,$(PLATFORM)),1,0)

ifeq ($(WINDOWS_ABI),1)
RC := windres
RCFLAGS := --define GCC_WINDRES
RC_OBJ := libdateconv_wrc.o

# Native Windows CMD runs executables without ./; POSIX-like Windows shells do not.
ifeq ($(PLATFORM),windows)
ifneq ($(strip $(MSYSTEM)$(MSYSTEM_PREFIX)),)
WINDOWS_CMD := 0
else
WINDOWS_CMD := 1
endif
else
WINDOWS_CMD := 0
endif
endif

AR ?= ar
RANLIB ?= ranlib
INSTALL ?= install
RM ?= rm -f
MKDIR_P ?= mkdir -p

PREFIX ?=
ifeq ($(PLATFORM),windows)
DEFAULT_PREFIX := $(CURDIR)/dist
else
DEFAULT_PREFIX := /usr/local
endif
prefix ?= $(if $(PREFIX),$(PREFIX),$(DEFAULT_PREFIX))
exec_prefix ?= $(prefix)
bindir ?= $(exec_prefix)/bin
libdir ?= $(exec_prefix)/lib
includedir ?= $(prefix)/include
pkgconfigdir ?= $(libdir)/pkgconfig
DESTDIR ?=

CPPFLAGS ?=
CFLAGS ?= -O2 -Wall
LDFLAGS ?=
LDLIBS ?=

ifeq ($(PLATFORM),unix)
LIBM ?= -lm
else
LIBM ?=
endif

STATIC_LIB := lib$(LIBNAME).a
OBJ := date_converter.o
SHARED_OBJ := date_converter_shared.o
PIC_OBJ := $(PIC_BUILD_DIR)/date_converter.o
TEST_EXE := test$(EXEEXT)

ifeq ($(WINDOWS_ABI),1)
SHARED_LIB := lib$(LIBNAME)-$(SOVERSION).dll
IMPORT_LIB := lib$(LIBNAME).dll.a
WINDOWS_DEF := libdateconv.def
SHARED_LDFLAGS := -shared -Wl,--out-implib,$(IMPORT_LIB)
TEST_RPATH :=
else ifeq ($(PLATFORM),macos)
SHARED_LIB := lib$(LIBNAME).$(VERSION).dylib
SHARED_SONAME := lib$(LIBNAME).$(SOVERSION).dylib
SHARED_LINK := lib$(LIBNAME).dylib
SHARED_LDFLAGS := -dynamiclib -Wl,-install_name,$(SHARED_SONAME) -Wl,-current_version,$(VERSION) -Wl,-compatibility_version,$(SOVERSION).0.0
SHARED_CFLAGS := -fvisibility=hidden
TEST_RPATH := -Wl,-rpath,@loader_path -Wl,-headerpad_max_install_names
else
SHARED_LIB := lib$(LIBNAME).so.$(VERSION)
SHARED_SONAME := lib$(LIBNAME).so.$(SOVERSION)
SHARED_LINK := lib$(LIBNAME).so
SHARED_LDFLAGS := -shared -Wl,-soname,$(SHARED_SONAME)
SHARED_CFLAGS := -fvisibility=hidden
TEST_RPATH := -Wl,-rpath,'$$ORIGIN'
endif

# A standalone `make shared` or `make static` selects the corresponding mode
# for targets requested in the same invocation. For later separate `make
# install`, `make test`, or `make check` invocations, the available library
# artifacts are detected without deleting the other library type. Plain `make`
# still builds both.
REQUESTED_LIBRARY_MODE :=
ifneq ($(filter shared,$(MAKECMDGOALS)),)
ifeq ($(filter static all,$(MAKECMDGOALS)),)
REQUESTED_LIBRARY_MODE := shared
endif
else ifneq ($(filter static,$(MAKECMDGOALS)),)
ifeq ($(filter shared all,$(MAKECMDGOALS)),)
REQUESTED_LIBRARY_MODE := static
endif
endif

BUILD_OPTIONS_EXPLICIT := $(filter-out undefined,$(BUILD_SHARED_INPUT_ORIGIN) $(BUILD_STATIC_INPUT_ORIGIN))
SHARED_BUILD_PRESENT := $(if $(wildcard $(SHARED_LIB)),1,0)
STATIC_BUILD_PRESENT := $(if $(wildcard $(STATIC_LIB)),1,0)

INSTALL_BUILD_SHARED := $(BUILD_SHARED)
INSTALL_BUILD_STATIC := $(BUILD_STATIC)
ifeq ($(strip $(BUILD_OPTIONS_EXPLICIT)),)
ifeq ($(REQUESTED_LIBRARY_MODE),shared)
INSTALL_BUILD_SHARED := 1
INSTALL_BUILD_STATIC := 0
else ifeq ($(REQUESTED_LIBRARY_MODE),static)
INSTALL_BUILD_SHARED := 0
INSTALL_BUILD_STATIC := 1
else ifeq ($(SHARED_BUILD_PRESENT)$(STATIC_BUILD_PRESENT),10)
INSTALL_BUILD_SHARED := 1
INSTALL_BUILD_STATIC := 0
else ifeq ($(SHARED_BUILD_PRESENT)$(STATIC_BUILD_PRESENT),01)
INSTALL_BUILD_SHARED := 0
INSTALL_BUILD_STATIC := 1
endif
endif

ALL_TARGETS :=
ifeq ($(BUILD_SHARED),1)
ALL_TARGETS += shared
endif
ifeq ($(BUILD_STATIC),1)
ALL_TARGETS += static
endif

.PHONY: all shared static test check install clean check-options

ifeq ($(INSTALL_BUILD_SHARED)$(INSTALL_BUILD_STATIC),11)
INSTALL_TARGETS := all
else ifeq ($(INSTALL_BUILD_SHARED),1)
INSTALL_TARGETS := shared
else ifeq ($(INSTALL_BUILD_STATIC),1)
INSTALL_TARGETS := static
else
INSTALL_TARGETS :=
endif

all: check-options $(ALL_TARGETS)

# This validation is performed by GNU Make itself, not by cmd.exe or /bin/sh.
check-options:
ifeq ($(filter 1,$(BUILD_SHARED) $(BUILD_STATIC)),)
	$(error enable BUILD_SHARED=1 and/or BUILD_STATIC=1)
endif

$(OBJ): $(SRC) $(HEADER)
	$(CC) $(CPPFLAGS) $(CFLAGS) -I. -c -o $@ $(SRC)

$(STATIC_LIB): $(OBJ)
	$(AR) rcs $@ $<
ifeq ($(PLATFORM),windows)
	$(RANLIB) $@
else
	chmod 644 $@
endif

static: $(STATIC_LIB)

ifeq ($(WINDOWS_ABI),1)
$(SHARED_OBJ): $(SRC) $(HEADER)
	$(CC) $(CPPFLAGS) $(CFLAGS) -DDATE_CONVERTER_BUILD_SHARED=1 -I. -c -o $@ $(SRC)

$(SHARED_LIB): $(SHARED_OBJ) $(HEADER) libdateconv_w.rc $(WINDOWS_DEF)
	$(RC) $(RCFLAGS) -o $(RC_OBJ) libdateconv_w.rc
	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $(SHARED_LDFLAGS) -o $@ $(SHARED_OBJ) $(RC_OBJ) $(WINDOWS_DEF) $(LIBM) $(LDLIBS)
else
$(PIC_BUILD_DIR):
	mkdir -p $@

$(PIC_OBJ): $(SRC) $(HEADER) | $(PIC_BUILD_DIR)
	$(CC) $(CPPFLAGS) $(CFLAGS) $(SHARED_CFLAGS) -I. -fPIC -c -o $@ $(SRC)

$(SHARED_LIB): $(PIC_OBJ)
	$(CC) $(CPPFLAGS) $(CFLAGS) -fPIC $(LDFLAGS) $(SHARED_LDFLAGS) -o $@ $< -lc $(LIBM) $(LDLIBS)
	ln -sf $(SHARED_LIB) $(SHARED_SONAME)
	ln -sf $(SHARED_SONAME) $(SHARED_LINK)
	chmod 755 $(SHARED_LIB)
endif

shared: $(SHARED_LIB)

# Build the test with the only available library type. If both Shared and
# Static are available, Shared remains the default preference.
TEST_LIB_DEPS := $(if $(filter 1,$(INSTALL_BUILD_SHARED)),$(SHARED_LIB),$(if $(filter 1,$(INSTALL_BUILD_STATIC)),$(STATIC_LIB)))
TEST_SHARED_CPPFLAGS := $(if $(and $(filter 1,$(WINDOWS_ABI)),$(filter 1,$(INSTALL_BUILD_SHARED))),-DDATE_CONVERTER_USE_SHARED=1,)
test: check-options $(TEST_LIB_DEPS)
	$(CC) -I. $(CPPFLAGS) $(TEST_SHARED_CPPFLAGS) $(CFLAGS) tests/test.c -L. -l$(LIBNAME) $(LIBM) $(TEST_RPATH) $(LDFLAGS) $(LDLIBS) -o $(TEST_EXE)
ifeq ($(PLATFORM),macos)
ifeq ($(INSTALL_BUILD_SHARED),1)
	@current_dep="$$(otool -L "$(TEST_EXE)" | awk 'NR > 1 && index($$1, "$(SHARED_SONAME)") { print $$1; exit }')"; \
	if [ -z "$$current_dep" ]; then \
		echo "unable to find $(SHARED_SONAME) in $(TEST_EXE)" >&2; \
		exit 1; \
	fi; \
	if [ "$$current_dep" != "@rpath/$(SHARED_SONAME)" ]; then \
		install_name_tool -change "$$current_dep" "@rpath/$(SHARED_SONAME)" "$(TEST_EXE)"; \
	fi; \
	otool -L "$(TEST_EXE)" | awk 'NR > 1 { print $$1 }' | grep -Fqx "@rpath/$(SHARED_SONAME)"
endif
endif
ifneq ($(PLATFORM),windows)
	chmod 755 $(TEST_EXE)
endif

check: test
ifeq ($(WINDOWS_ABI),1)
ifeq ($(WINDOWS_CMD),1)
	$(TEST_EXE)
else
	./$(TEST_EXE)
endif
else
	./$(TEST_EXE)
endif

# Convert forward slashes to native Windows separators for cmd.exe built-ins.
winpath = $(subst /,\,$(1))

ifeq ($(WINDOWS_ABI),1)
ifeq ($(WINDOWS_CMD),1)
install: check-options $(INSTALL_TARGETS)
	@if not exist "$(call winpath,$(DESTDIR)$(includedir))" mkdir "$(call winpath,$(DESTDIR)$(includedir))"
	@if not exist "$(call winpath,$(DESTDIR)$(libdir))" mkdir "$(call winpath,$(DESTDIR)$(libdir))"
	@if not exist "$(call winpath,$(DESTDIR)$(pkgconfigdir))" mkdir "$(call winpath,$(DESTDIR)$(pkgconfigdir))"
	@copy /Y "$(HEADER)" "$(call winpath,$(DESTDIR)$(includedir)/$(HEADER))" >NUL
ifeq ($(INSTALL_BUILD_STATIC),1)
	@copy /Y "$(STATIC_LIB)" "$(call winpath,$(DESTDIR)$(libdir)/$(STATIC_LIB))" >NUL
endif
ifeq ($(INSTALL_BUILD_SHARED),1)
	@if not exist "$(call winpath,$(DESTDIR)$(bindir))" mkdir "$(call winpath,$(DESTDIR)$(bindir))"
	@copy /Y "$(SHARED_LIB)" "$(call winpath,$(DESTDIR)$(bindir)/$(SHARED_LIB))" >NUL
	@copy /Y "$(IMPORT_LIB)" "$(call winpath,$(DESTDIR)$(libdir)/$(IMPORT_LIB))" >NUL
endif
	@> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo prefix=$(prefix)
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo exec_prefix=$${prefix}
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo libdir=$${exec_prefix}/$(notdir $(libdir))
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo includedir=$${prefix}/$(notdir $(includedir))
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo.
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo Name: Date Converter Library
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo Description: A lightweight C library for converting between multiple calendar systems, including Persian, Gregorian, Islamic, Hebrew, and Julian calendars
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo URL: https://www.rosybit.com
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo Version: $(VERSION)
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo Libs: -L$${libdir} -ldateconv
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo Libs.private: $(LIBM)
	@>> "$(call winpath,$(DESTDIR)$(pkgconfigdir)/libdateconv.pc)" echo Cflags: -I$${includedir}
else
install: check-options $(INSTALL_TARGETS)
	$(MKDIR_P) "$(DESTDIR)$(includedir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"
	$(INSTALL) -m 644 $(HEADER) "$(DESTDIR)$(includedir)/$(HEADER)"
ifeq ($(INSTALL_BUILD_STATIC),1)
	$(INSTALL) -m 644 $(STATIC_LIB) "$(DESTDIR)$(libdir)/$(STATIC_LIB)"
endif
ifeq ($(INSTALL_BUILD_SHARED),1)
	$(MKDIR_P) "$(DESTDIR)$(bindir)"
	$(INSTALL) -m 755 $(SHARED_LIB) "$(DESTDIR)$(bindir)/$(SHARED_LIB)"
	$(INSTALL) -m 644 $(IMPORT_LIB) "$(DESTDIR)$(libdir)/$(IMPORT_LIB)"
endif
	sed \
		-e 's|@prefix@|$(prefix)|g' \
		-e 's|@exec_prefix@|$${prefix}|g' \
		-e 's|@libdir@|$${exec_prefix}/$(notdir $(libdir))|g' \
		-e 's|@includedir@|$${prefix}/$(notdir $(includedir))|g' \
		-e 's|@PACKAGE_VERSION@|$(VERSION)|g' \
		-e 's|@LIBM@|$(LIBM)|g' \
		libdateconv.pc.in > "$(DESTDIR)$(pkgconfigdir)/libdateconv.pc"
	chmod 644 "$(DESTDIR)$(pkgconfigdir)/libdateconv.pc"
endif
else
install: check-options $(INSTALL_TARGETS)
	$(MKDIR_P) "$(DESTDIR)$(includedir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(pkgconfigdir)"
	$(INSTALL) -m 644 $(HEADER) "$(DESTDIR)$(includedir)/$(HEADER)"
ifeq ($(INSTALL_BUILD_STATIC),1)
	$(INSTALL) -m 644 $(STATIC_LIB) "$(DESTDIR)$(libdir)/$(STATIC_LIB)"
endif
ifeq ($(INSTALL_BUILD_SHARED),1)
	$(INSTALL) -m 755 $(SHARED_LIB) "$(DESTDIR)$(libdir)/$(SHARED_LIB)"
	ln -sf $(SHARED_LIB) "$(DESTDIR)$(libdir)/$(SHARED_SONAME)"
	ln -sf $(SHARED_SONAME) "$(DESTDIR)$(libdir)/$(SHARED_LINK)"
endif
	sed \
		-e 's|@prefix@|$(prefix)|g' \
		-e 's|@exec_prefix@|$${prefix}|g' \
		-e 's|@libdir@|$${exec_prefix}/$(notdir $(libdir))|g' \
		-e 's|@includedir@|$${prefix}/$(notdir $(includedir))|g' \
		-e 's|@PACKAGE_VERSION@|$(VERSION)|g' \
		-e 's|@LIBM@|$(LIBM)|g' \
		libdateconv.pc.in > "$(DESTDIR)$(pkgconfigdir)/libdateconv.pc"
	chmod 644 "$(DESTDIR)$(pkgconfigdir)/libdateconv.pc"
endif

ifeq ($(WINDOWS_ABI),1)
ifeq ($(WINDOWS_CMD),1)
clean:
	@if exist "$(call winpath,$(PIC_BUILD_DIR))" rmdir /S /Q "$(call winpath,$(PIC_BUILD_DIR))"
	@if exist "$(OBJ)" del /Q "$(OBJ)"
	@if exist "$(SHARED_OBJ)" del /Q "$(SHARED_OBJ)"
	@if exist "$(RC_OBJ)" del /Q "$(RC_OBJ)"
	@if exist "$(STATIC_LIB)" del /Q "$(STATIC_LIB)"
	@if exist "$(SHARED_LIB)" del /Q "$(SHARED_LIB)"
	@if exist "$(IMPORT_LIB)" del /Q "$(IMPORT_LIB)"
	@if exist "$(TEST_EXE)" del /Q "$(TEST_EXE)"
else
clean:
	$(RM) -r $(PIC_BUILD_DIR) $(OBJ) $(SHARED_OBJ) $(RC_OBJ) $(STATIC_LIB) $(SHARED_LIB) $(IMPORT_LIB) $(TEST_EXE)
endif
else
clean:
	$(RM) -r $(PIC_BUILD_DIR) $(OBJ) $(STATIC_LIB) $(SHARED_LIB) $(SHARED_SONAME) $(SHARED_LINK) $(TEST_EXE)
endif
