EXTENSION = provsql
EXTVERSION = $(shell grep default_version $(EXTENSION).common.control | \
           sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/")

MODULE_big = provsql
# The KCMCP wire codec (kcmcp_protocol) is shared: the in-extension client
# (kcmcp_client) and the standalone tdkc server both use it, so it stays in the
# extension OBJS.  What is tdkc-only -- the server (kcmcp_server), the DIMACS
# reader (dimacs_cnf), and the tdkc main (TreeDecompositionKnowledgeCompiler,
# which calls kcmcp_serve()) -- is filtered out, like the provsql_migrate_mmap
# tool; leaving the tdkc main in would make provsql.so reference kcmcp_serve.
OBJS = $(patsubst %.c,%.o,$(wildcard src/*.c)) $(filter-out src/provsql_migrate_mmap.o src/TreeDecompositionKnowledgeCompiler.o src/kcmcp_server.o src/dimacs_cnf.o,$(patsubst %.cpp,%.o,$(wildcard src/*.cpp src/semiring/*.cpp src/distributions/*.cpp)))

DOCS = $(wildcard doc/*.md)
UPGRADE_SCRIPTS = $(wildcard sql/upgrades/$(EXTENSION)--*--*.sql)
# sql/provsql--1.0.0.sql is a frozen install-script fixture for the
# oldest supported version, used by test/sql/extension_upgrade.sql to
# exercise the ALTER EXTENSION provsql UPDATE chain.  It is generated
# at build time from version-sensitive fixtures under sql/fixtures/.
BASE_INSTALL = sql/$(EXTENSION)--1.0.0.sql

# If EXTVERSION is a development version (e.g. 1.3.0-dev), auto-generate
# an empty upgrade script from the latest released tag to this dev
# version.  This lets `ALTER EXTENSION provsql UPDATE` reach the
# current default_version in the upgrade test suite without requiring
# a committed dev-cycle upgrade script.  Actual SQL changes made during
# the dev cycle are captured by the real upgrade script that
# release.sh creates at release time; this auto-generated file is only
# a placeholder to make the path traversable during dev.  The generated
# file is matched by the existing sql/provsql--*.sql gitignore pattern
# and is therefore not committed.
#
# LATEST_RELEASE is read primarily from the committed release upgrade
# script's file name (sql/upgrades/provsql--<prev>--<bare version>.sql):
# unlike `git describe`, that works in a source tarball, a Docker COPY
# without .git, and -- critically -- under `sudo make install`, where
# git's dubious-ownership protection makes describe fail silently and
# the dev-cycle upgrade script would be dropped from DATA, leaving a
# stale copy in the extension directory to hijack the upgrade path.
# `git describe` remains the fallback for the early dev cycle, before
# release.sh has created the upgrade script.  LATEST_RELEASE can be
# overridden on the make command line to force a specific target:
#   make LATEST_RELEASE=1.2.1
ifneq ($(findstring -dev,$(EXTVERSION)),)
BARE_EXTVERSION = $(subst -dev,,$(EXTVERSION))
LATEST_RELEASE = $(patsubst sql/upgrades/$(EXTENSION)--%--$(BARE_EXTVERSION).sql,%,$(wildcard sql/upgrades/$(EXTENSION)--*--$(BARE_EXTVERSION).sql))
ifeq ($(LATEST_RELEASE),)
LATEST_RELEASE = $(shell git describe --tags --abbrev=0 --match 'v[0-9]*' 2>/dev/null | sed 's/^v//')
endif
ifneq ($(LATEST_RELEASE),)
DEV_UPGRADE = sql/$(EXTENSION)--$(LATEST_RELEASE)--$(EXTVERSION).sql
else
SKIP_UPGRADE_TEST = yes
endif
endif

DATA = sql/$(EXTENSION)--$(EXTVERSION).sql $(BASE_INSTALL) $(UPGRADE_SCRIPTS) $(DEV_UPGRADE)
EXTRA_CLEAN = sql/$(EXTENSION)--*.sql sql/$(EXTENSION).sql $(BASE_INSTALL) $(DEV_UPGRADE) tdkc provsql_migrate_mmap test/schedule $(EXTENSION).control doc/doxygen-*/ doc/source/_build doc/source/c/files.rst doc/source/c/files src/*.gcno src/*.gcda src/semiring/*.gcno src/semiring/*.gcda coverage

# We want REGRESS to be empty, since we are going to provide a schedule
# of tests. But we want REGRESS to be defined, otherwise installcheck
# does nothing. So we use the following Makefile trick to have a defined
# variable with empty value
EMPTY =
REGRESS = $(EMPTY)
REGRESS_OPTS = --load-extension=plpgsql --inputdir=test --outputdir=$(shell mktemp -d /tmp/tmp.provsqlXXXX) --schedule test/schedule

OS := $(shell uname)
ARCH := $(shell uname -m)


# Additional options for pg_regress can be specified below, e.g., a port
# number with EXTRA_REGRESS_OPTS = --port=5434
EXTRA_REGRESS_OPTS =

ifdef DEBUG
PG_CPPFLAGS += -O0 -g
endif

# Coverage build (driven by `make coverage`). gcov line/branch counters with
# LTO disabled (LTO mangles gcov's line mapping) and -O0 (faithful source
# mapping). PG_CPPFLAGS lands after CFLAGS in both the C and C++ recipes, so
# -fno-lto / -O0 override the PGXS defaults. Profile updates are atomic because
# many backends write the same .gcda concurrently. --coverage also goes to the
# link so libgcov is pulled in.
ifdef COVERAGE
PG_CPPFLAGS  += --coverage -fno-lto -O0 -fprofile-update=atomic
LINKER_FLAGS += --coverage -fno-lto
endif

# Disable JIT code generation, due to various bugs, see in particular
# https://bugs.llvm.org/show_bug.cgi?id=41863
# https://github.com/PierreSenellart/provsql/issues/9
with_llvm = no

# A different pg_config can be specified below, e.g., when different
# PostgreSQL are installed on the same machine
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)

all: $(DATA) $(MODULE_big)$(DLSUFFIX) test/schedule

$(OBJS): $(wildcard src/*.h src/semiring/*.h src/distributions/*.h src/*.hpp)

sql/provsql.sql: sql/provsql.*.sql
	cat sql/provsql.common.sql > sql/provsql.sql
	if [ $(PGVER_MAJOR) -ge 14 ]; then \
		cat sql/provsql.14.sql >> sql/provsql.sql; \
	fi

sql/provsql--$(EXTVERSION).sql: sql/provsql.sql
	cp $< $@

# Frozen install script for the oldest supported version (1.0.0).
# Concatenated from git-frozen source fixtures, gated on the same
# PG14+ test as the current install script.
sql/provsql--1.0.0.sql: sql/fixtures/provsql--1.0.0-common.sql sql/fixtures/provsql--1.0.0-14.sql
	cat sql/fixtures/provsql--1.0.0-common.sql > $@
	if [ $(PGVER_MAJOR) -ge 14 ]; then \
		cat sql/fixtures/provsql--1.0.0-14.sql >> $@; \
	fi

# Auto-generated empty dev-cycle upgrade script; see the DEV_UPGRADE
# block above for the rationale.  Only exists when EXTVERSION ends in
# -dev and a release tag is reachable from HEAD.
ifneq ($(DEV_UPGRADE),)
# If a committed upgrade script already exists for LATEST_RELEASE → bare
# version (without -dev), copy it so the dev-cycle upgrade path carries
# the same content (e.g. migration warnings).  Otherwise generate an
# empty placeholder.
BARE_EXTVERSION = $(subst -dev,,$(EXTVERSION))
RELEASE_UPGRADE = sql/upgrades/$(EXTENSION)--$(LATEST_RELEASE)--$(BARE_EXTVERSION).sql
$(DEV_UPGRADE): $(wildcard $(RELEASE_UPGRADE))
	if [ -f "$(RELEASE_UPGRADE)" ]; then cp "$(RELEASE_UPGRADE)" $@; else touch $@; fi
endif


%.o : %.c
	$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $<

ifeq ($(OS), Darwin)
HOMEBREW_PREFIX = $(shell brew --prefix)
BOOST_INSTALL_PATH = $(shell brew --prefix boost)

LINKER_FLAGS += -L$(BOOST_INSTALL_PATH)/lib -L$(HOMEBREW_PREFIX)/lib
endif

ifeq ($(ARCH), arm64)
PRECXXFLAGS=-I$(BOOST_INSTALL_PATH)/include
endif
ifeq ($(ARCH), x86_64)
PRECXXFLAGS=-I/usr/local/include
endif
# Boost's serialization / property_tree headers transitively include the
# deprecated global-namespace Bind placeholders (boost/bind.hpp), which emit a
# #pragma message under -W -Wall (e.g. when building tdkc).  We do not use the
# global _1/_2 placeholders ourselves; define BOOST_BIND_GLOBAL_PLACEHOLDERS to
# retain the current (working) behaviour and silence the deprecation note.  The
# PGXS object build stays quiet on its own, but keeping this in PRECXXFLAGS makes
# every target (tdkc, provsql_migrate_mmap, the extension) consistent.
PRECXXFLAGS+=-std=c++17 -Wno-register -fPIC -DBOOST_BIND_GLOBAL_PLACEHOLDERS

%.o : %.cpp
	$(CXX) $(PRECXXFLAGS) $(CXXFLAGS) $(CPPFLAGS) -c -o $@ $<

LINKER_FLAGS += -lstdc++ -lboost_serialization -Wno-lto-type-mismatch

VERSION     = $(shell $(PG_CONFIG) --version | awk '{print $$2}')
PGVER_MAJOR = $(shell echo $(VERSION) | awk -F. '{ print ($$1 + 0) }')

# Ways to pass linker flags have changed in PostgreSQL 16
ifeq ($(shell [ $(PGVER_MAJOR) -ge 16 ]; echo $$?), 0)
CFLAGS_SL_MODULE += $(LINKER_FLAGS)
else
LDFLAGS_SL += $(LINKER_FLAGS)
endif

test/schedule: $(wildcard test/schedule.*)
	cat test/schedule.common > test/schedule
	if [ $(PGVER_MAJOR) -ge 14 ]; then \
		cat test/schedule.14 >> test/schedule; \
	fi
	if [ $(PGVER_MAJOR) -ge 15 ]; then \
		cat test/schedule.15 >> test/schedule; \
	fi
ifneq ($(SKIP_UPGRADE_TEST),yes)
	echo "test: extension_upgrade" >> test/schedule
endif

$(EXTENSION).control: $(EXTENSION).common.control
	if [ $(PGVER_MAJOR) -lt 13 ]; then \
		grep -v trusted $< > $@; \
	else \
		cp $< $@; \
	fi

installcheck: test/schedule

# Run the "todo" regression suite: tests for features that are not yet
# implemented (test/todo/{sql,expected}/).  Failures are expected until
# the corresponding feature lands; the expected/ files describe the
# intended behavior.  Not part of the default `make installcheck`.
.PHONY: installcheck-todo
installcheck-todo:
	$(MAKE) installcheck REGRESS_OPTS="--load-extension=plpgsql --inputdir=test/todo --outputdir=$(shell mktemp -d /tmp/tmp.provsql-todoXXXX) --schedule test/todo/schedule"

tdkc: src/TreeDecomposition.cpp src/TreeDecomposition.h src/BooleanCircuit.cpp src/BooleanCircuit.h src/Circuit.hpp src/dDNNF.h src/dDNNF.cpp src/dDNNFTreeDecompositionBuilder.h src/dDNNFTreeDecompositionBuilder.cpp src/Circuit.h src/Graph.h src/PermutationStrategy.h src/TreeDecompositionKnowledgeCompiler.cpp src/kcmcp_protocol.cpp src/kcmcp_protocol.h src/kcmcp_server.cpp src/kcmcp_server.h src/dimacs_cnf.cpp src/dimacs_cnf.h src/tdkc_interrupt.h
	$(CXX) $(PRECXXFLAGS) -DTDKC -W -Wall -o tdkc src/TreeDecomposition.cpp src/BooleanCircuit.cpp src/dDNNF.cpp src/dDNNFTreeDecompositionBuilder.cpp src/TreeDecompositionKnowledgeCompiler.cpp src/kcmcp_protocol.cpp src/kcmcp_server.cpp src/dimacs_cnf.cpp

# Build tdkc and run the KCMCP protocol conformance check against it.
.PHONY: test-kcmcp
test-kcmcp: tdkc
	python3 test/kcmcp/conformance.py ./tdkc

provsql_migrate_mmap: src/provsql_migrate_mmap.cpp
	$(CXX) $(PRECXXFLAGS) -W -Wall \
		-I$(shell $(PG_CONFIG) --includedir) \
		-o provsql_migrate_mmap src/provsql_migrate_mmap.cpp \
		-L$(shell $(PG_CONFIG) --libdir) -lpq
