#
# Makefile for phshim -- CCSO Ph/Qi protocol to SQL shim
#
# Usage:
#   make                  # PostgreSQL backend (default)
#   make BACKEND=mysql    # MySQL/MariaDB backend
#   make BACKEND=pgsql    # PostgreSQL (explicit)
#   make BACKEND=both     # Both compiled in; backend chosen at runtime
#                         # via "backend = pgsql|mysql" in phshim.conf
#   make clean
#   make install          # binary, man page, and sample config
#   make dist             # source distribution, phshim-VERSION.tar.bz2
#   make manual           # regenerate doc/phshim-manual.pdf (needs LaTeX)
#
# Sources are in src/; objects and the phshim binary are built in build/.
# Run "make clean" after changing BACKEND, so everything is rebuilt.
#
# You can also set CC, CFLAGS, LDFLAGS as usual.
#
# Copyright (c) 2026 Large Scale Systems Museum
# By Dave McGuire <mcguire@lssmuseum.org>
#

PROGNAME  = phshim
SRCDIR    = src
BUILDDIR  = build
TARGET    = $(BUILDDIR)/$(PROGNAME)
MANUAL    = $(PROGNAME)-manual
PDFLATEX ?= pdflatex
VERSION   = $(shell sed -n 's/.*VERSION[ \t]*"\(.*\)".*/\1/p' $(SRCDIR)/version.h)
PREFIX   ?= /usr/local

BINDIR    = $(PREFIX)/bin
MANDIR    = $(PREFIX)/share/man
CONFDIR   = /etc/phshim
BACKEND  ?= pgsql
CC       ?= cc
CFLAGS   += -std=c11 -Wall -Wextra -Wpedantic
CFLAGS   += -O2 -g
CFLAGS   += -Wno-declaration-after-statement

# -MMD -MP: GCC/Clang dependency generation; skip for Sun Studio
ifneq ($(shell $(CC) --version 2>/dev/null | grep -c 'Sun\|Studio'),1)
    CFLAGS += -MMD -MP
endif
# Feature-test macros are set per-platform in port.h, but we also pass
# _XOPEN_SOURCE=700 on the command line as a belt-and-suspenders measure.
# This ensures strdup, ssize_t, socklen_t, inet_pton etc. are declared
# even if a system header is somehow processed before port.h takes effect.
# _XOPEN_SOURCE=700 = SUSv4/POSIX.1-2008; safe on Linux, macOS, and BSD.
UNAME_S := $(shell uname -s 2>/dev/null)
ifneq ($(UNAME_S),SunOS)
    CFLAGS += -D_XOPEN_SOURCE=700
else
    # Solaris: _XOPEN_SOURCE alone suppresses extensions; need both.
    CFLAGS += -D_XOPEN_SOURCE=600 -D__EXTENSIONS__
endif

# On Solaris/Illumos, socket and name-service functions are in separate libs.
ifeq ($(UNAME_S),SunOS)
    LDFLAGS += -lsocket -lnsl
endif

DIST_SRCS  = commands.c db_dispatch.c db_pgsql.c initdb.c phshim.c session.c
DIST_SRCS += config.c db_mysql.c field.c parser.c response.c wildcard.c
DIST_HDRS  = commands.h config.h db_backend.h db.h field.h initdb.h parser.h
DIST_HDRS += port.h response.h session.h version.h wildcard.h
DIST_DOCS  = LICENSE.md README.md doc/$(PROGNAME).conf.sample
DIST_DOCS += doc/USAGE.md doc/$(PROGNAME).8 doc/$(MANUAL).tex doc/$(MANUAL).pdf
# Files in subdirectories, listed with their paths; dist preserves them
DIST_EXTRA = Linux/phshim.service Solaris/phshim.xml test/test_phshim.py
DIST_FILES = Makefile $(addprefix $(SRCDIR)/,$(DIST_SRCS) $(DIST_HDRS)) \
             $(DIST_DOCS) $(DIST_EXTRA)
DISTDIR    = $(PROGNAME)-$(VERSION)


# ------------------------------------------------------------------ #
# Source files common to all builds                                   #
# ------------------------------------------------------------------ #

COMMON_SRC = phshim.c config.c parser.c wildcard.c session.c \
             response.c commands.c field.c initdb.c db_dispatch.c

# ------------------------------------------------------------------ #
# PostgreSQL include/lib detection                                    #
# ------------------------------------------------------------------ #

define find_pg_inc
$(shell pg_config --includedir 2>/dev/null || \
  echo $(firstword $(wildcard \
    /usr/include/postgresql \
    /usr/local/include/postgresql \
    /usr/include/pgsql \
    /opt/homebrew/include/postgresql \
    /opt/local/include/postgresql \
    /opt/csw/include/postgresql \
    /opt/local/include \
    /usr/postgres/*/include \
    /opt/ooce/pgsql-*/include)))
endef

define find_pg_lib
$(shell pg_config --ldflags 2>/dev/null) \
$(shell pg_config --libs 2>/dev/null | grep -o '\-L[^ ]*' || true) \
$(shell test -d /opt/csw/lib && echo -L/opt/csw/lib) \
$(shell test -d /opt/ooce/pgsql-16/lib && echo -L/opt/ooce/pgsql-16/lib) \
-lpq
endef

# ------------------------------------------------------------------ #
# MySQL/MariaDB include/lib detection                                 #
# ------------------------------------------------------------------ #

define find_my_inc
$(shell mysql_config --cflags 2>/dev/null || \
        mariadb_config --cflags 2>/dev/null || \
  echo -I$(firstword $(wildcard \
    /usr/include/mysql \
    /usr/local/include/mysql \
    /usr/include/mariadb \
    /opt/homebrew/include/mysql \
    /opt/csw/include/mysql \
    /opt/csw/include/mariadb \
    /opt/local/include/mysql \
    /opt/ooce/mariadb-*/include/mysql)))
endef

define find_my_lib
$(shell mysql_config --libs 2>/dev/null || \
        mariadb_config --libs 2>/dev/null || \
        echo -lmysqlclient) \
$(shell test -d /opt/csw/lib && echo -L/opt/csw/lib) \
$(shell test -d /opt/ooce/mariadb-106/lib && echo -L/opt/ooce/mariadb-106/lib)
endef

# ------------------------------------------------------------------ #
# Backend-specific configuration                                      #
# ------------------------------------------------------------------ #

ifeq ($(BACKEND),pgsql)
    CFLAGS      += -DUSE_PGSQL -I$(call find_pg_inc)
    LDFLAGS     += $(call find_pg_lib)
    BACKEND_SRC  = db_pgsql.c
endif

ifeq ($(BACKEND),mysql)
    CFLAGS      += -DUSE_MYSQL $(call find_my_inc)
    LDFLAGS     += $(call find_my_lib)
    BACKEND_SRC  = db_mysql.c
endif

ifeq ($(BACKEND),both)
    CFLAGS      += -DUSE_PGSQL -DUSE_MYSQL
    CFLAGS      += -I$(call find_pg_inc) $(call find_my_inc)
    LDFLAGS     += $(call find_pg_lib) $(call find_my_lib)
    BACKEND_SRC  = db_pgsql.c db_mysql.c
endif

ifeq ($(filter $(BACKEND),pgsql mysql both),)
    $(error Unknown BACKEND '$(BACKEND)'.  Use: pgsql, mysql, or both)
endif

# Source names are relative to $(SRCDIR); objects go in $(BUILDDIR)
ALL_SRC = $(COMMON_SRC) $(BACKEND_SRC)
ALL_OBJ = $(addprefix $(BUILDDIR)/,$(ALL_SRC:.c=.o))


.PHONY: all clean dist install clangd manual

all: $(TARGET)

$(TARGET): $(ALL_OBJ)
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
	@echo "Built $@"

# The build directory is an order-only prerequisite: it must exist, but
# its timestamp changing must not cause objects to be rebuilt.
$(BUILDDIR)/%.o: $(SRCDIR)/%.c | $(BUILDDIR)
	$(CC) $(CFLAGS) -c -o $@ $<

$(BUILDDIR):
	mkdir -p $@

# The PDF is kept under version control and distributed, so that readers
# need no LaTeX; regenerate it with "make manual" after editing the .tex.
# pdflatex runs in $(BUILDDIR) until the cross-references settle (at most
# three passes), and only the finished PDF is moved into doc/, so a failed
# run leaves the existing doc/ copy untouched.
manual: doc/$(MANUAL).pdf

doc/$(MANUAL).pdf: doc/$(MANUAL).tex | $(BUILDDIR)
	@for pass in 1 2 3; do \
	    $(PDFLATEX) -interaction=nonstopmode -halt-on-error \
	        -output-directory=$(BUILDDIR) $< >/dev/null || { \
	        rm -f $(BUILDDIR)/$(MANUAL).pdf; \
	        echo "$(PDFLATEX) failed; see $(BUILDDIR)/$(MANUAL).log"; \
	        exit 1; }; \
	    grep -q 'Rerun to get' $(BUILDDIR)/$(MANUAL).log || break; \
	done
	@mv $(BUILDDIR)/$(MANUAL).pdf $@
	@echo "Built $@"

clangd:
	bear -- make

dist:
	@rm -rf $(DISTDIR)
	@rm -f $(DISTDIR).tar.bz2
	@for f in $(DIST_FILES); do \
	    mkdir -p $(DISTDIR)/`dirname $$f` && \
	    cp -p $$f $(DISTDIR)/$$f || exit 1; \
	done
	@tar -cf - $(DISTDIR) | bzip2 -9 > $(DISTDIR).tar.bz2
	@rm -rf $(DISTDIR)
	@echo Distribution file $(DISTDIR).tar.bz2 created.

install: $(TARGET)
	install -d $(DESTDIR)$(BINDIR)
	install -d $(DESTDIR)$(CONFDIR)
	install -m 755 $(TARGET) $(DESTDIR)$(BINDIR)/phshim
	install -d $(DESTDIR)$(MANDIR)/man8
	install -m 644 doc/phshim.8 $(DESTDIR)$(MANDIR)/man8/phshim.8
	@if [ ! -f $(DESTDIR)$(CONFDIR)/phshim.conf ]; then \
	    install -m 640 doc/phshim.conf.sample $(DESTDIR)$(CONFDIR)/phshim.conf; \
	    echo "Installed sample config to $(DESTDIR)$(CONFDIR)/phshim.conf"; \
	else \
	    echo "Config already exists; not overwriting."; \
	fi
	@echo "Installed $(PROGNAME) as $(DESTDIR)$(BINDIR)/phshim"

clean:
	@echo Cleaning
	rm -rf $(BUILDDIR)
	rm -f $(PROGNAME)-*.tar.bz2 *~ $(SRCDIR)/*~

-include $(ALL_OBJ:.o=.d)
