root_dir := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))

TARGET = pg_query
ARLIB = lib$(TARGET).a
PGDIR = $(root_dir)/tmp/postgres
PGDIRBZ2 = $(root_dir)/tmp/postgres.tar.bz2

PG_VERSION = 10.16

SRC_FILES := $(wildcard src/*.c src/postgres/*.c)
OBJ_FILES := $(SRC_FILES:.c=.o)
NOT_OBJ_FILES := src/pg_query_fingerprint_defs.o src/pg_query_fingerprint_conds.o src/pg_query_json_defs.o src/pg_query_json_conds.o src/postgres/guc-file.o src/postgres/scan.o src/pg_query_json_helper.o
OBJ_FILES := $(filter-out $(NOT_OBJ_FILES), $(OBJ_FILES))

CFLAGS = -g -I. -I./src/postgres/include -Wall -Wno-unused-function -Wno-unused-value -Wno-unused-variable -fno-strict-aliasing -fwrapv -fPIC

TEST_CFLAGS = -I. -g
TEST_LDFLAGS = -pthread

PG_CONFIGURE_FLAGS = -q --without-readline --without-zlib

CFLAGS_OPT_LEVEL = -O3
ifeq ($(DEBUG),1)
	CFLAGS_OPT_LEVEL = -O0
endif
ifeq ($(VALGRIND),1)
	CFLAGS_OPT_LEVEL = -O0
endif
CFLAGS += $(CFLAGS_OPT_LEVEL)

ifeq ($(DEBUG),1)
	# We always add -g, so this only has to enable assertion checking
	CFLAGS += -D USE_ASSERT_CHECKING
endif
ifeq ($(VALGRIND),1)
	CFLAGS += -DUSE_VALGRIND
	TEST_CFLAGS += -DUSE_VALGRIND
endif

CLEANLIBS = $(ARLIB)
CLEANOBJS = $(OBJ_FILES)
CLEANFILES = $(PGDIRBZ2)

AR = ar rs
RM = rm -f
ECHO = echo

VALGRIND_MEMCHECK = valgrind --leak-check=full --gen-suppressions=all \
  --suppressions=test/valgrind.supp --time-stamp=yes \
  --error-markers=VALGRINDERROR-BEGIN,VALGRINDERROR-END \
  --log-file=test/valgrind.log --trace-children=yes --show-leak-kinds=all \
  --error-exitcode=1 --errors-for-leak-kinds=all

CC ?= cc

all: examples test build

build: $(ARLIB)

clean:
	-@ $(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES) $(EXAMPLES) $(TESTS)
	-@ $(RM) -rf {test,examples}/*.dSYM
	-@ $(RM) -r $(PGDIR) $(PGDIRBZ2)

.PHONY: all clean build extract_source examples test

$(PGDIR):
	curl -o $(PGDIRBZ2) https://ftp.postgresql.org/pub/source/v$(PG_VERSION)/postgresql-$(PG_VERSION).tar.bz2
	tar -xjf $(PGDIRBZ2)
	mv $(root_dir)/postgresql-$(PG_VERSION) $(PGDIR)
	cd $(PGDIR); patch -p1 < $(root_dir)/patches/01_parse_replacement_char.patch
	cd $(PGDIR); patch -p1 < $(root_dir)/patches/06_make_memory_context_methods_const.patch
	cd $(PGDIR); patch -p1 < $(root_dir)/patches/07_plpgsql_start_finish_datums.patch
	cd $(PGDIR); CFLAGS="$(PG_CFLAGS)" ./configure $(PG_CONFIGURE_FLAGS)
	cd $(PGDIR); make -C src/port pg_config_paths.h
	cd $(PGDIR); make -C src/backend parser-recursive # Triggers copying of includes to where they belong, as well as generating gram.c/scan.c

extract_source: $(PGDIR)
	-@ $(RM) -rf ./src/postgres/
	mkdir ./src/postgres
	mkdir ./src/postgres/include
	ruby ./scripts/extract_source.rb $(PGDIR)/ ./src/postgres/
	cp $(PGDIR)/src/include/storage/dsm_impl.h ./src/postgres/include/storage
	cp $(PGDIR)/src/include/port/atomics/arch-arm.h ./src/postgres/include/port/atomics
	cp $(PGDIR)/src/include/port/atomics/arch-ppc.h ./src/postgres/include/port/atomics
	touch ./src/postgres/guc-file.c
	# This causes compatibility problems on some Linux distros, with "xlocale.h" not being available
	echo "#undef HAVE_LOCALE_T" >> ./src/postgres/include/pg_config.h
	echo "#undef LOCALE_T_IN_XLOCALE" >> ./src/postgres/include/pg_config.h
	echo "#undef WCSTOMBS_L_IN_XLOCALE" >> ./src/postgres/include/pg_config.h
	# Support 32-bit systems without reconfiguring
	echo "#undef PG_INT128_TYPE" >> ./src/postgres/include/pg_config.h
	# Support gcc earlier than 4.6.0 without reconfiguring
	echo "#undef HAVE__STATIC_ASSERT" >> ./src/postgres/include/pg_config.h
	# Avoid problems with static asserts
	echo "#undef StaticAssertDecl" >> ./src/postgres/include/c.h
	echo "#define StaticAssertDecl(condition, errmessage)" >> ./src/postgres/include/c.h
	# Copy version information so its easily accessible
	sed -i "" '$(shell echo 's/\#define PG_MAJORVERSION .*/'`grep "\#define PG_MAJORVERSION " ./src/postgres/include/pg_config.h`'/')' pg_query.h
	sed -i "" '$(shell echo 's/\#define PG_VERSION .*/'`grep "\#define PG_VERSION " ./src/postgres/include/pg_config.h`'/')' pg_query.h
	sed -i "" '$(shell echo 's/\#define PG_VERSION_NUM .*/'`grep "\#define PG_VERSION_NUM " ./src/postgres/include/pg_config.h`'/')' pg_query.h

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

$(ARLIB): $(OBJ_FILES) Makefile
	@$(AR) $@ $(OBJ_FILES)

EXAMPLES = examples/simple examples/normalize examples/simple_error examples/normalize_error examples/simple_plpgsql
examples: $(EXAMPLES)
	examples/simple
	examples/normalize
	examples/simple_error
	examples/normalize_error
	examples/simple_plpgsql

examples/simple: examples/simple.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ -g examples/simple.c $(ARLIB) $(TEST_LDFLAGS)

examples/normalize: examples/normalize.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ -g examples/normalize.c $(ARLIB) $(TEST_LDFLAGS)

examples/simple_error: examples/simple_error.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ -g examples/simple_error.c $(ARLIB) $(TEST_LDFLAGS)

examples/normalize_error: examples/normalize_error.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ -g examples/normalize_error.c $(ARLIB) $(TEST_LDFLAGS)

examples/simple_plpgsql: examples/simple_plpgsql.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ -g examples/simple_plpgsql.c $(ARLIB) $(TEST_LDFLAGS)

TESTS = test/complex test/concurrency test/fingerprint test/normalize test/parse test/parse_plpgsql
test: $(TESTS)
ifeq ($(VALGRIND),1)
	$(VALGRIND_MEMCHECK) test/complex || (cat test/valgrind.log && false)
	$(VALGRIND_MEMCHECK) test/concurrency || (cat test/valgrind.log && false)
	$(VALGRIND_MEMCHECK) test/fingerprint || (cat test/valgrind.log && false)
	$(VALGRIND_MEMCHECK) test/normalize || (cat test/valgrind.log && false)
	$(VALGRIND_MEMCHECK) test/parse || (cat test/valgrind.log && false)
	# Output-based tests
	$(VALGRIND_MEMCHECK) test/parse_plpgsql || (cat test/valgrind.log && false)
	diff -Naur test/plpgsql_samples.expected.json test/plpgsql_samples.actual.json
else
	test/complex
	test/concurrency
	test/fingerprint
	test/normalize
	test/parse
	# Output-based tests
	test/parse_plpgsql
	diff -Naur test/plpgsql_samples.expected.json test/plpgsql_samples.actual.json
endif

test/complex: test/complex.c $(ARLIB)
	# We have "-Isrc/" because this test uses pg_query_fingerprint_with_opts
	$(CC) $(TEST_CFLAGS) -o $@ -Isrc/ test/complex.c $(ARLIB) $(TEST_LDFLAGS)

test/concurrency: test/concurrency.c test/parse_tests.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ test/concurrency.c $(ARLIB) $(TEST_LDFLAGS)

test/fingerprint: test/fingerprint.c test/fingerprint_tests.c $(ARLIB)
	# We have "-Isrc/" because this test uses pg_query_fingerprint_with_opts
	$(CC) $(TEST_CFLAGS) -o $@ -Isrc/ test/fingerprint.c $(ARLIB) $(TEST_LDFLAGS)

test/normalize: test/normalize.c test/normalize_tests.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ test/normalize.c $(ARLIB) $(TEST_LDFLAGS)

test/parse: test/parse.c test/parse_tests.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ test/parse.c $(ARLIB) $(TEST_LDFLAGS)

test/parse_plpgsql: test/parse_plpgsql.c test/parse_tests.c $(ARLIB)
	$(CC) $(TEST_CFLAGS) -o $@ test/parse_plpgsql.c $(ARLIB) $(TEST_LDFLAGS)
