Current section
Files
Jump to
Current section
Files
Makefile
SRC_DIR = c_src
DUCKDB_DIR = $(SRC_DIR)/duckdb
DUCKDB_ARCHIVE = $(SRC_DIR)/duckdb.tar.xz
DUCKDB_MANIFEST = $(DUCKDB_DIR)/.sources
DUCKDB_ARCHIVE_CHECKSUM = $(DUCKDB_DIR)/.archive.checksum
DUCKDB_ARCHIVE_SHA256 = $(SRC_DIR)/duckdb.tar.xz.sha256
# The archive is committed to git but not shipped in the Hex package (it does
# not fit Hex's size limit), so a build from the Hex package downloads it from
# the GitHub release of this version. Override DUCKDB_ARCHIVE_URL to use a
# mirror, or a file:// URL to build offline.
DUCKDBEX_VERSION = $(shell sed -n 's/^ @version "\(.*\)"/\1/p' mix.exs)
DUCKDB_ARCHIVE_URL ?= https://github.com/AlexR2D2/duckdbex/releases/download/v$(DUCKDBEX_VERSION)/duckdb.tar.xz
CXXFLAGS = -O3 -std=c++17
# Silence GCC's informational C++17 ABI notes (GCC 10.1 pair-passing change):
# DuckDB and the NIF are built by one compiler, so the ABI never mixes.
CXXFLAGS += -Wno-psabi
# Statically link the DuckDB build-baseline extensions (see
# extension/extension_config.cmake upstream): core_functions, parquet, json and icu.
CXXFLAGS += -DDUCKDB_EXTENSION_CORE_FUNCTIONS_LINKED=1
CXXFLAGS += -DDUCKDB_EXTENSION_PARQUET_LINKED=1
CXXFLAGS += -DDUCKDB_EXTENSION_JSON_LINKED=1
CXXFLAGS += -DDUCKDB_EXTENSION_ICU_LINKED=1
# Include roots emitted by DuckDB's scripts/package_build.py and recorded
# in c_src/duckdb/.include_dirs at generation time (see bin/regen_duckdb.sh).
DUCKDB_INCLUDE_DIRS = $(shell test -f $(DUCKDB_DIR)/.include_dirs && cat $(DUCKDB_DIR)/.include_dirs)
CXXFLAGS += $(foreach dir, $(DUCKDB_INCLUDE_DIRS), -I"$(DUCKDB_DIR)/$(dir)")
CXXFLAGS += -I"$(ERTS_INCLUDE_DIR)"
CXXFLAGS += -DNDEBUG=1
KERNEL_NAME := $(shell uname -s)
PRIV_DIR = $(MIX_APP_PATH)/priv
LIB_NAME = $(PRIV_DIR)/duckdb_nif.so
ifneq ($(CROSSCOMPILE),)
CXXFLAGS += -fPIC -fvisibility=hidden
LDFLAGS += -fPIC -shared
else
ifeq ($(KERNEL_NAME), Linux)
CXXFLAGS += -fPIC -fvisibility=hidden
LDFLAGS += -fPIC -shared
endif
ifeq ($(KERNEL_NAME), Darwin)
CXXFLAGS += -fPIC
LDFLAGS += -dynamiclib -undefined dynamic_lookup
endif
ifeq ($(KERNEL_NAME), $(filter $(KERNEL_NAME),OpenBSD FreeBSD NetBSD))
CXXFLAGS += -fPIC
LDFLAGS += -fPIC -shared
endif
endif
# Compile the file list emitted by DuckDB's scripts/package_build.py
# (unity builds + directly referenced sources), plus the NIF files.
# See c_src/duckdb/.sources for the generated list.
GENERATED_SRC = $(shell test -f $(DUCKDB_MANIFEST) && cat $(DUCKDB_MANIFEST))
# Since v2.0.0 package_build.py always lists the bundled jemalloc C sources.
# DuckDB only uses them with -DDUCKDB_ENABLE_JEMALLOC (64-bit Linux), which
# this build does not set, so skip them and keep the default allocator.
GENERATED_SRC := $(filter-out third_party/jemalloc/%, $(GENERATED_SRC))
NIF_SRC = $(SRC_DIR)/nif.cpp $(SRC_DIR)/config.cpp $(SRC_DIR)/term.cpp $(SRC_DIR)/term_to_value.cpp $(SRC_DIR)/value_to_term.cpp
SRC = $(addprefix $(DUCKDB_DIR)/, $(GENERATED_SRC)) $(NIF_SRC)
OBJ = $(patsubst %.cpp, %.o, $(patsubst %.cc, %.o, $(subst $(SRC_DIR), $(PRIV_DIR), $(SRC))))
.PRECIOUS: $(PRIV_DIR)/. $(PRIV_DIR)%/.
$(PRIV_DIR):
mkdir -p $@
$(PRIV_DIR)/.:
mkdir -p $@
$(PRIV_DIR)%/.:
mkdir -p $@
.SECONDEXPANSION:
$(PRIV_DIR)/%.o: $(SRC_DIR)/%.cpp | $$(@D)/.
$(CXX) $(CXXFLAGS) -c $< -o $@
$(PRIV_DIR)/%.o: $(SRC_DIR)/%.cc | $$(@D)/.
$(CXX) $(CXXFLAGS) -c $< -o $@
$(LIB_NAME): $(OBJ)
$(CXX) $(LDFLAGS) $^ -o $@
all: prepare-duckdb
$(MAKE) build
build: $(PRIV_DIR) $(SRC) $(LIB_NAME)
$(DUCKDB_ARCHIVE):
@set -eu; \
echo "Downloading vendored DuckDB sources from $(DUCKDB_ARCHIVE_URL)"; \
tmp="$$(mktemp "$(SRC_DIR)/duckdb.download.XXXXXX")"; \
trap 'rm -f "$$tmp"' EXIT; \
curl -fsSL --retry 3 -o "$$tmp" "$(DUCKDB_ARCHIVE_URL)" || { \
echo "ERROR: could not download $(DUCKDB_ARCHIVE_URL); set DUCKDB_ARCHIVE_URL to a mirror or a file:// path"; exit 1; }; \
if command -v sha256sum >/dev/null 2>&1; then actual="$$(sha256sum < "$$tmp")"; else actual="$$(shasum -a 256 < "$$tmp")"; fi; \
actual="$${actual%% *}"; \
expected="$$(cat "$(DUCKDB_ARCHIVE_SHA256)")"; \
if [ "$$actual" != "$$expected" ]; then \
echo "ERROR: sha256 mismatch for $(DUCKDB_ARCHIVE_URL): expected $$expected, got $$actual"; exit 1; \
fi; \
mv "$$tmp" "$(DUCKDB_ARCHIVE)"
prepare-duckdb: $(DUCKDB_ARCHIVE)
@set -eu; \
archive_checksum="$$(cksum < "$(DUCKDB_ARCHIVE)")"; \
installed_checksum="$$(cat "$(DUCKDB_ARCHIVE_CHECKSUM)" 2>/dev/null || true)"; \
if [ ! -f "$(DUCKDB_MANIFEST)" ] || [ "$$archive_checksum" != "$$installed_checksum" ]; then \
echo "Extracting vendored DuckDB sources"; \
extract_dir="$$(mktemp -d "$(SRC_DIR)/duckdb.extract.XXXXXX")"; \
trap 'rm -rf "$$extract_dir"' EXIT; \
tar -xJf "$(DUCKDB_ARCHIVE)" -C "$$extract_dir"; \
test -f "$$extract_dir/duckdb/.sources" || { echo "ERROR: invalid $(DUCKDB_ARCHIVE)"; exit 1; }; \
rm -rf "$(DUCKDB_DIR)"; \
mv "$$extract_dir/duckdb" "$(DUCKDB_DIR)"; \
printf '%s\n' "$$archive_checksum" > "$(DUCKDB_ARCHIVE_CHECKSUM)"; \
find "$(DUCKDB_DIR)" -type f -exec touch {} +; \
fi
# Guard against a broken/partial vendor tree: every file in .sources must
# exist on disk (run this in CI to catch rsync/LFS accidents).
verify-sources: prepare-duckdb
@missing=""; while IFS= read -r f; do \
[ -f "$(DUCKDB_DIR)/$$f" ] || missing="$$missing $$f"; \
done < "$(DUCKDB_DIR)/.sources"; \
if [ -n "$$missing" ]; then \
echo "ERROR: missing vendored sources:$$missing"; exit 1; \
fi; \
echo "OK: all vendored sources present"
clean:
$(RM) -rf run $(OBJ)
$(RM) -f $(LIB_NAME)
.PHONY: all build prepare-duckdb clean verify-sources