# Copyright (C) 2026 Intel Corporation # SPDX-License-Identifier: PostgreSQL EXTENSION = svs EXTVERSION = 0.1.0 MODULE_big = svs DATA = sql/$(EXTENSION)--$(EXTVERSION).sql OBJS = src/svs.o \ src/vamana.o \ src/svs_cpu_budget.o \ src/svs_memory.o \ src/svs_index_residency.o \ src/svs_slot_naming.o \ src/vamanalauncher.o \ src/vamana_replication.o \ src/vamana_checkpoint.o \ src/vamana_undo.o \ src/vamana_subxid_pending_array.o \ src/vamana_subxact_guard.o \ src/svs_vector_buffer.o \ src/vamanabuild.o \ src/svs_parallel_build.o \ src/svs_build_thread_grant.o \ src/svs_build_request_protocol.o \ src/svs_cpu_slots.o \ src/vamanacache.o \ src/vamanaio.o \ src/vamanainsert.o \ src/vamanascan.o \ src/vamanautils.o \ src/vamanavacuum.o \ src/vamanaworker.o \ src/vamanaworkerstats.o \ src/vamanaworkershmem.o \ src/vamanaworkerindex.o \ src/vamanaworkersearch.o \ src/vamanaworkerwrite.o \ src/svs_wrapper.o \ src/svs_capacity_search.o \ src/vamana_databases.o \ src/vamana_teardown.o \ src/vamana_warmup.o HEADERS = src/vamana.h src/svs_wrapper.h src/vamana_databases.h src/vamanalauncher.h src/svs_cpu_budget.h src/svs_memory.h src/svs_index_residency.h src/svs_slot_naming.h src/svs_cpu_slots.h src/svs_capacity_search.h src/svs_vector_buffer.h # halfvec_compression/vector_compression build real LeanVec and LVQ indexes, # which require hardware most CI runners lack. Excluded from TESTS/REGRESS so # plain 'make installcheck' never attempts them; run via 'make installcheck-hw'. HW_TESTS = halfvec_compression vector_compression TESTS = $(filter-out $(addprefix test/sql/,$(addsuffix .sql,$(HW_TESTS))),$(wildcard test/sql/*.sql)) # Same hardware limitation, TAP side: these files build real LeanVec/LVQ # indexes. Excluded from the default 'prove_installcheck' file list so it # never attempts them; run via 'make prove_installcheck-hw'. HW_TAP_TESTS = test/t/39_persistence_compression.pl test/t/44_build_memory_calibration_compression.pl REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) # Load pgvector first (for vector/halfvec types), then this extension REGRESS_OPTS = --inputdir=test --load-extension=vector --load-extension=$(EXTENSION) # To compile for portability, run: make OPTFLAGS="" OPTFLAGS = -march=native # Mac ARM doesn't always support -march=native ifeq ($(shell uname -s), Darwin) ifeq ($(shell uname -p), arm) OPTFLAGS = endif endif # PowerPC doesn't support -march=native ifneq ($(filter ppc64%, $(shell uname -m)), ) OPTFLAGS = endif # RISC-V64 doesn't support -march=native ifeq ($(shell uname -m), riscv64) OPTFLAGS = endif # Hardening flags. -Wall, -Wformat, -Wformat-security, -Wimplicit-fallthrough, # and -fPIC already come from pg_config; they are restated here for clarity. # New protections added by this Makefile: -Werror=format-security, # -fstack-protector-strong, -fstack-clash-protection, _FORTIFY_SOURCE uplift, # and -Wl,-z,now (see HARDENING_LDFLAGS). # -Werror is opt-in via WERROR=1 (e.g. in CI): a shipped -Werror is not safe # across future compiler and PostgreSQL releases. -Werror=format-security is # unconditional as it guards a specific, narrow class of bug. # -Wconversion/-Wextra omitted: trigger errors in PostgreSQL system headers. # -U_FORTIFY_SOURCE before -D avoids a redefinition diagnostic on distros that # predefine it. Level 3 requires GCC 12+; GCC 11 gets 2. FORTIFY_LEVEL := $(shell [ "$$(${CC:-gcc} -dumpversion 2>/dev/null | cut -d. -f1)" -ge 12 ] 2>/dev/null && echo 3 || echo 2) HARDENING_CFLAGS = -Wall -Wimplicit-fallthrough \ -Wformat -Wformat-security -Werror=format-security \ -fstack-protector-strong -fstack-clash-protection \ -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=$(FORTIFY_LEVEL) -fPIC ifeq ($(WERROR),1) HARDENING_CFLAGS += -Werror endif # Linker hardening flags. -z,noexecstack and -z,relro are distro defaults; # -z,now is the load-bearing addition (full RELRO; not a default for shared libs). # -z,nodlopen omitted: PostgreSQL loads extensions via dlopen. HARDENING_LDFLAGS = -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now PG_CFLAGS += $(OPTFLAGS) -ftree-vectorize -fassociative-math -fno-signed-zeros -fno-trapping-math $(HARDENING_CFLAGS) # Coverage instrumentation, opt-in via 'make COVERAGE=1'. Kept out of default # builds because --coverage slows the binary and writes .gcda files at runtime. ifeq ($(COVERAGE),1) PG_CFLAGS += --coverage SHLIB_LINK += --coverage endif # SVS library paths SVS_INSTALL ?= svs_install_public # Resolve to an absolute path: a relative RUNPATH is resolved by the dynamic # loader against the process's current working directory, and the postmaster # changes into $PGDATA before loading shared_preload_libraries. override SVS_INSTALL := $(abspath $(SVS_INSTALL)) # Validate SVS library exists ifeq (,$(wildcard $(SVS_INSTALL)/lib/libsvs_c_api.so)) $(error SVS library not found at $(SVS_INSTALL)/lib/libsvs_c_api.so. Run build_svs_public.sh first or set SVS_INSTALL correctly) endif # -isystem, not -I, for the SVS C API headers: they are a third-party # dependency this project does not control, and svs_c.h trips # -Wdeclaration-after-statement (part of pg_config's own --cflags). -isystem # suppresses warnings from headers found through it, so WERROR=1 in CI is not # tripped by someone else's header. PG_CPPFLAGS += -isystem $(SVS_INSTALL)/include -I$(shell $(PG_CONFIG) --includedir-server)/extension/vector SHLIB_LINK += -L$(SVS_INSTALL)/lib -lsvs_c_api -Wl,-rpath,$(SVS_INSTALL)/lib $(HARDENING_LDFLAGS) PG_CONFIG ?= pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) # Remove GCC coverage artefacts on 'make clean' EXTRA_CLEAN = $(wildcard src/*.gcda) $(wildcard src/*.gcno) include $(PGXS) # Expose the build's injection-point setting to TAP tests (fault-path tests # skip themselves when it is not 'yes'). export enable_injection_points # for Mac ifeq ($(PROVE),) PROVE = prove endif # for Postgres < 15 PROVE_FLAGS += -I ./test/perl # --------------------------------------------------------------------------- # Stale-install guard # # 'installcheck' and 'prove_installcheck' run against whatever is already # installed in pkglibdir, not against what was just built: neither target # depends on 'install'. That means a tree that fails to build, or one whose # source changed since the last 'make install', can still report a full test # pass, because the tests never look at the tree. This guard closes that gap # by refusing to run either target when the built and installed shlib differ. # # It deliberately does not add 'install' as a prerequisite: testing an # already-installed build is a normal developer workflow, and an implicit # install would write into a system directory without being asked. # --------------------------------------------------------------------------- .PHONY: guard-fresh-install guard-fresh-install: @if [ ! -f '$(DESTDIR)$(pkglibdir)/$(shlib)' ]; then \ echo "error: $(shlib) is not installed at $(DESTDIR)$(pkglibdir)/$(shlib)."; \ echo " Run 'make install' first."; \ exit 1; \ fi @if ! cmp -s '$(CURDIR)/$(shlib)' '$(DESTDIR)$(pkglibdir)/$(shlib)'; then \ echo "error: built $(CURDIR)/$(shlib) differs from installed $(DESTDIR)$(pkglibdir)/$(shlib)."; \ echo " Run 'make install' to test what was just built."; \ exit 1; \ fi installcheck: guard-fresh-install # --------------------------------------------------------------------------- # Hardware-dependent regression tests # # halfvec_compression.sql and vector_compression.sql build real LeanVec and # LVQ compressed indexes via SVS, which require hardware most CI runners # lack. Run this target explicitly on hardware that supports it; it is not # part of plain 'installcheck'. # --------------------------------------------------------------------------- .PHONY: installcheck-hw installcheck-hw: guard-fresh-install $(MAKE) installcheck REGRESS="$(HW_TESTS)" # --------------------------------------------------------------------------- # Hardening verification (SDL429 evidence) # # Confirms the protections in HARDENING_CFLAGS/HARDENING_LDFLAGS actually took # effect in the built svs.so, using only readelf and nm from binutils, which # the build already requires. Checks svs.so only: the SVS C API library it # links against is a dependency this project does not build or release. # --------------------------------------------------------------------------- .PHONY: hardening-check SVS_SO ?= $(CURDIR)/$(shlib) hardening-check: @sh $(CURDIR)/ci/hardening_check.sh '$(SVS_SO)' prove_installcheck: guard-fresh-install rm -rf $(CURDIR)/tmp_check cd $(srcdir) && TESTDIR='$(CURDIR)' PATH="$(bindir):$$PATH" LD_LIBRARY_PATH="$(shell $(PG_CONFIG) --libdir):$$LD_LIBRARY_PATH" PGPORT='6$(DEF_PGPORT)' PG_REGRESS='$(top_builddir)/src/test/regress/pg_regress' $(PROVE) $(PG_PROVE_FLAGS) $(PROVE_FLAGS) $(if $(PROVE_TESTS),$(PROVE_TESTS),$(filter-out $(HW_TAP_TESTS),$(wildcard test/t/*.pl))) # --------------------------------------------------------------------------- # Hardware-dependent TAP tests # # 39_persistence_compression.pl and 44_build_memory_calibration_compression.pl # build real LeanVec and LVQ compressed indexes via SVS, which require # hardware most CI runners lack. Run this target explicitly on hardware that # supports it; it is not part of plain 'prove_installcheck'. # --------------------------------------------------------------------------- .PHONY: prove_installcheck-hw prove_installcheck-hw: guard-fresh-install $(MAKE) prove_installcheck PROVE_TESTS="$(HW_TAP_TESTS)" # --------------------------------------------------------------------------- # Coverage report targets # # Workflow: # 1. make COVERAGE=1 (builds with instrumentation) # 2. make install && make prove_installcheck (accumulates .gcda counters) # 3. make coverage (generates all three report formats) # # To start a clean measurement run: # make coverage-clean && make prove_installcheck && make coverage # --------------------------------------------------------------------------- GCOVR ?= gcovr COVERAGE_FILTER = --filter src/ # coverage: generate text summary, AI-consumable JSON, and interactive HTML coverage: mkdir -p coverage_reports $(GCOVR) $(COVERAGE_FILTER) --txt coverage_reports/coverage.txt $(GCOVR) $(COVERAGE_FILTER) --json-pretty --output coverage_reports/coverage.json $(GCOVR) $(COVERAGE_FILTER) --html-details coverage_reports/index.html @echo "Coverage reports written to coverage_reports/" # coverage-clean: zero .gcda counters so the next test run starts fresh. # Does NOT remove .gcno files (those require a recompile to regenerate). coverage-clean: find $(CURDIR)/src -name '*.gcda' -delete @echo "Coverage counters reset (*.gcda removed)" .PHONY: coverage coverage-clean