Packages

NIF Wrapper around llama.cpp allowing elixir code to load/infer against gguf format models.

Current section

Files

Jump to
ex_llama Makefile
Raw

Makefile

# Makefile for ExLLama C NIF
# Called by elixir_make during `mix compile`
PRIV_DIR = priv
NIF_SO = $(PRIV_DIR)/ex_llama_nif.so
UNAME_S := $(shell uname -s)
# Erlang include path
ERL_INCLUDE = $(shell erl -noshell -eval 'io:format("~s", [lists:concat([code:root_dir(), "/erts-", erlang:system_info(version), "/include"])])' -s init stop)
# llama.cpp paths and pinned version
LLAMA_DIR = c_src/llama.cpp
LLAMA_REPO = https://github.com/ggml-org/llama.cpp.git
LLAMA_TAG = b8322
LLAMA_BUILD = $(LLAMA_DIR)/build
LLAMA_LIB = $(LLAMA_BUILD)/src/libllama.a
GGML_LIB = $(LLAMA_BUILD)/ggml/src/libggml.a
GGML_BASE_LIB = $(LLAMA_BUILD)/ggml/src/libggml-base.a
GGML_CPU_LIB = $(LLAMA_BUILD)/ggml/src/libggml-cpu.a
GGML_BLAS_LIB = $(LLAMA_BUILD)/ggml/src/ggml-blas/libggml-blas.a
# Metal support on macOS
ifeq ($(UNAME_S),Darwin)
GGML_METAL_LIB = $(LLAMA_BUILD)/ggml/src/ggml-metal/libggml-metal.a
METAL_FLAGS = -framework Foundation -framework Metal -framework MetalKit -framework Accelerate
# Darwin: BLAS via Apple's Accelerate framework (cmake default on Darwin)
BLAS_CMAKE_FLAGS = -DGGML_BLAS=ON
LINUX_LINK_LIBS =
else
GGML_METAL_LIB =
METAL_FLAGS =
# Linux: BLAS via OpenBLAS (requires libopenblas-dev at build time)
BLAS_CMAKE_FLAGS = -DGGML_BLAS=ON -DGGML_BLAS_VENDOR=OpenBLAS
# Linux link-time deps for static archives:
# -lopenblas satisfies sgemm_/dgemm_ used by libggml-blas.a
# -lgomp satisfies OpenMP symbols used by libggml-cpu.a
# -ldl satisfies dynamic-loader symbols used by libggml.a
LINUX_LINK_LIBS = -lopenblas -lgomp -ldl
endif
CFLAGS = -fPIC -O2 -Wall -I$(ERL_INCLUDE) -I$(LLAMA_DIR)/include -I$(LLAMA_DIR)/ggml/include
CXXFLAGS = $(CFLAGS) -std=c++17
LDFLAGS = -shared
ifeq ($(UNAME_S),Darwin)
LDFLAGS += -undefined dynamic_lookup -flat_namespace
endif
# All static libs to link
LLAMA_LIBS = $(LLAMA_LIB) $(GGML_LIB) $(GGML_BASE_LIB) $(GGML_CPU_LIB) $(GGML_BLAS_LIB) $(GGML_METAL_LIB)
LINK_LIBS = -lstdc++ -lm -lpthread $(METAL_FLAGS) $(LINUX_LINK_LIBS)
all: $(PRIV_DIR) $(LLAMA_LIB) $(NIF_SO)
$(PRIV_DIR):
mkdir -p $(PRIV_DIR)
# Fetch llama.cpp if not present (hex package installs won't have the submodule)
$(LLAMA_DIR)/CMakeLists.txt:
git clone --depth 1 --branch $(LLAMA_TAG) $(LLAMA_REPO) $(LLAMA_DIR)
# Build llama.cpp as static library via cmake.
# GGML_BLAS is explicitly enabled because LLAMA_LIBS links libggml-blas.a;
# without this flag llama.cpp defaults to BLAS=OFF on Linux and the NIF
# link fails with "No such file or directory" for libggml-blas.a.
# CMAKE_POSITION_INDEPENDENT_CODE=ON is required so the static archives
# can be linked into the NIF shared object (.so) on all platforms;
# without it, aarch64 Linux fails with "relocation ... can not be used
# when making a shared object".
$(LLAMA_LIB): $(LLAMA_DIR)/CMakeLists.txt
cmake -B $(LLAMA_BUILD) -S $(LLAMA_DIR) \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON \
-DLLAMA_BUILD_TESTS=OFF \
-DLLAMA_BUILD_EXAMPLES=OFF \
-DLLAMA_BUILD_SERVER=OFF \
$(BLAS_CMAKE_FLAGS)
cmake --build $(LLAMA_BUILD) --config Release -j$(shell sysctl -n hw.ncpu 2>/dev/null || nproc)
# Build NIF shared object, linking llama.cpp statically
$(NIF_SO): c_src/ex_llama_nif.cpp $(LLAMA_LIB)
$(CXX) $(CXXFLAGS) $(LDFLAGS) -o $@ c_src/ex_llama_nif.cpp $(LLAMA_LIBS) $(LINK_LIBS)
# Test binary (standalone, no Erlang needed)
TEST_BIN = $(PRIV_DIR)/test_llama_nif
TEST_CXXFLAGS = -O2 -Wall -I$(LLAMA_DIR)/include -I$(LLAMA_DIR)/ggml/include -std=c++17
test: $(PRIV_DIR) $(LLAMA_LIB) $(TEST_BIN)
$(TEST_BIN) priv/models/local_llama/tiny_llama/tinyllama-1.1b-chat-v1.0.Q4_K_M.gguf
$(TEST_BIN): c_src/test_llama_nif.cpp $(LLAMA_LIB)
$(CXX) $(TEST_CXXFLAGS) -o $@ c_src/test_llama_nif.cpp $(LLAMA_LIBS) $(LINK_LIBS)
clean:
rm -f $(NIF_SO) $(TEST_BIN)
rm -rf $(LLAMA_BUILD)
.PHONY: all clean test