# Makefile for building the odbcserver port program.
# Called from rebar3 hooks.

CURDIR := $(shell pwd)
BASEDIR := $(abspath $(CURDIR)/..)
PRIVDIR := $(BASEDIR)/priv

# Erlang paths
ERL ?= erl
ERTS_INCLUDE_DIR ?= $(shell $(ERL) -noshell -eval "io:format(\"~ts/erts-~ts/include/\", [code:root_dir(), erlang:system_info(version)])." -s init stop)
EI_INCLUDE_DIR ?= $(shell $(ERL) -noshell -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, include)])." -s init stop)
EI_LIB_DIR ?= $(shell $(ERL) -noshell -eval "io:format(\"~ts\", [code:lib_dir(erl_interface, lib)])." -s init stop)

# Platform detection
UNAME_S := $(shell uname -s)

CC ?= cc
CFLAGS ?= -O2 -Wall
LDFLAGS =

# Platform-specific flags
ifeq ($(UNAME_S),Darwin)
    TARGET_FLAGS = -DUNIX -DHAVE_STRUCT_SOCKADDR_IN6_SIN6_ADDR
    # Try unixODBC first (Homebrew), fall back to iodbc
    ODBC_LIB := $(shell pkg-config --libs odbc 2>/dev/null || echo "-lodbc")
    ODBC_CFLAGS := $(shell pkg-config --cflags odbc 2>/dev/null)
    ifeq ($(ODBC_LIB),-lodbc)
        # Check Homebrew paths
        HOMEBREW_PREFIX := $(shell brew --prefix 2>/dev/null || echo "/opt/homebrew")
        ifneq ($(wildcard $(HOMEBREW_PREFIX)/opt/unixodbc/lib/libodbc.dylib),)
            ODBC_LIB = -L$(HOMEBREW_PREFIX)/opt/unixodbc/lib -lodbc
            ODBC_CFLAGS = -I$(HOMEBREW_PREFIX)/opt/unixodbc/include
        else
            ODBC_LIB = -lodbc
        endif
    endif
else ifeq ($(UNAME_S),Linux)
    TARGET_FLAGS = -DUNIX -DHAVE_STRUCT_SOCKADDR_IN6_SIN6_ADDR
    ODBC_LIB := $(shell pkg-config --libs odbc 2>/dev/null || echo "-lodbc")
    ODBC_CFLAGS := $(shell pkg-config --cflags odbc 2>/dev/null)
else
    # Windows / other
    TARGET_FLAGS = -DWIN32
    ODBC_LIB = -lodbc32
    ODBC_CFLAGS =
endif

# Threading
THR_LIBS = -lpthread
THR_DEFS = -D_THREAD_SAFE -D_REENTRANT

INCLUDES = -I. -I$(EI_INCLUDE_DIR) -I$(ERTS_INCLUDE_DIR) $(ODBC_CFLAGS)
ALL_CFLAGS = $(CFLAGS) $(TARGET_FLAGS) $(THR_DEFS) $(INCLUDES)
ALL_LDFLAGS = -L$(EI_LIB_DIR) -lei $(ODBC_LIB) $(THR_LIBS) $(LDFLAGS)

TARGET = $(PRIVDIR)/bin/odbcserver

all: $(TARGET)

$(TARGET): odbcserver.o
	@mkdir -p $(PRIVDIR)/bin
	$(CC) $< $(ALL_LDFLAGS) -o $@

odbcserver.o: odbcserver.c odbcserver.h
	$(CC) $(ALL_CFLAGS) -c -o $@ $<

clean:
	rm -f odbcserver.o $(TARGET)
	rm -f test_decode_params $(BASEDIR)/test_decode_params

# ---- Test targets ----

TEST_CFLAGS = $(ALL_CFLAGS) -DTEST
TEST_LDFLAGS = -L$(EI_LIB_DIR) -lei $(ODBC_LIB) $(THR_LIBS) $(LDFLAGS)

# Build and run C unit tests (no database needed)
test: test_decode_params
	./test_decode_params

test_decode_params: test_decode_params.c odbcserver.c odbcserver.h test_harness.h
	$(CC) $(TEST_CFLAGS) -o $@ test_decode_params.c $(TEST_LDFLAGS)

# Build with AddressSanitizer + UndefinedBehaviorSanitizer
sanitize: CFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -g
sanitize: LDFLAGS += -fsanitize=address,undefined
sanitize: clean $(TARGET)

# Run C unit tests under ASan/UBSan
test-sanitize: CFLAGS += -fsanitize=address,undefined -fno-omit-frame-pointer -g
test-sanitize: LDFLAGS += -fsanitize=address,undefined
test-sanitize: test

# Run C unit tests under Valgrind (Linux only)
test-valgrind: CFLAGS += -g -O0
test-valgrind: test_decode_params
	valgrind --leak-check=full --show-reachable=yes --track-origins=yes \
		--error-exitcode=42 --errors-for-leak-kinds=all \
		$(if $(wildcard $(BASEDIR)/scripts/valgrind.supp),--suppressions=$(BASEDIR)/scripts/valgrind.supp) \
		./test_decode_params

.PHONY: all clean test sanitize test-sanitize test-valgrind
