# Copyright (c) 2026 Benoit Chesneau. Licensed under the MIT License.
# See the LICENSE file at the project root.
cmake_minimum_required(VERSION 3.20)
project(erllama_nif C CXX)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMake")
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
# -----------------------------------------------------------------------------
# llama.cpp configuration
# -----------------------------------------------------------------------------
# Build llama.cpp as static libraries that we link into our NIF .so.
# Backend selection is platform-driven by default; users override via
# environment variables consumed in do_cmake.sh.
set(LLAMA_BUILD_COMMON OFF CACHE BOOL "" FORCE)
set(LLAMA_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(LLAMA_BUILD_TOOLS OFF CACHE BOOL "" FORCE)
set(LLAMA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(LLAMA_BUILD_SERVER OFF CACHE BOOL "" FORCE)
set(BUILD_SHARED_LIBS OFF CACHE BOOL "" FORCE)
set(GGML_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GGML_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(GGML_STATIC ON CACHE BOOL "" FORCE)
# We link the ggml/llama static libs into a shared NIF .so. On Linux,
# the system libgomp.a is built without -fPIC, so an OpenMP-enabled
# static ggml-cpu drags in a non-PIC libgomp that fails to relocate
# into our .so ("R_X86_64_TPOFF32 against hidden symbol gomp_tls_data
# can not be used when making a shared object"). Disabling OpenMP at
# the ggml level avoids the libgomp dependency entirely. The cost is
# CPU-path parallelism inside ggml; the GPU paths (Metal on Apple,
# CUDA on Linux when enabled) are unaffected.
set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
# Silence "ccache not found" diagnostic from ggml when ccache is
# not installed on the build host (FreeBSD CI VMs, fresh dev boxes).
set(GGML_CCACHE OFF CACHE BOOL "" FORCE)
# Backends we never ship in the vendored tree (sources are not present).
set(GGML_VULKAN OFF CACHE BOOL "" FORCE)
set(GGML_SYCL OFF CACHE BOOL "" FORCE)
set(GGML_OPENCL OFF CACHE BOOL "" FORCE)
set(GGML_CANN OFF CACHE BOOL "" FORCE)
set(GGML_HEXAGON OFF CACHE BOOL "" FORCE)
set(GGML_HIP OFF CACHE BOOL "" FORCE)
set(GGML_MUSA OFF CACHE BOOL "" FORCE)
set(GGML_RPC OFF CACHE BOOL "" FORCE)
set(GGML_ZDNN OFF CACHE BOOL "" FORCE)
# Backends that ship; defaults can be flipped via -DGGML_*=OFF on the
# CMake command line (see do_cmake.sh).
if(APPLE)
if(NOT DEFINED GGML_METAL)
set(GGML_METAL ON CACHE BOOL "" FORCE)
endif()
if(NOT DEFINED GGML_BLAS)
set(GGML_BLAS ON CACHE BOOL "" FORCE)
set(GGML_BLAS_VENDOR "Apple" CACHE STRING "" FORCE)
endif()
endif()
if(NOT DEFINED GGML_CUDA)
set(GGML_CUDA OFF CACHE BOOL "" FORCE)
endif()
add_subdirectory(llama.cpp EXCLUDE_FROM_ALL)
# -----------------------------------------------------------------------------
# Erlang ERTS include dir (via FindErlang.cmake, copied from
# erlang-rocksdb). Honour ERTS_INCLUDE_DIR if the caller pre-set it.
# -----------------------------------------------------------------------------
if(DEFINED ENV{ERTS_INCLUDE_DIR})
set(ERTS_INCLUDE_DIR $ENV{ERTS_INCLUDE_DIR})
else()
include(FindErlang)
set(ERTS_INCLUDE_DIR ${ERLANG_ERTS_INCLUDE_PATH})
endif()
# -----------------------------------------------------------------------------
# erllama_nif shared library
# -----------------------------------------------------------------------------
add_library(erllama_nif SHARED
erllama_nif.c
erllama_safe.cpp
crc32c.c
)
target_compile_options(erllama_nif PRIVATE
-O2 -g -Wall -Wextra -Wpedantic -fPIC
)
target_include_directories(erllama_nif PRIVATE
${ERTS_INCLUDE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/llama.cpp/include
${CMAKE_CURRENT_SOURCE_DIR}/llama.cpp/ggml/include
)
# pthread is used by erllama_nif.c for per-resource mutexes; depend
# on the standard CMake Threads find module so toolchains that need
# -pthread (e.g. some Linux distros) get it added.
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
target_link_libraries(erllama_nif PRIVATE
llama
ggml
Threads::Threads
)
# Erlang NIFs need this dance for symbol resolution.
set_target_properties(erllama_nif PROPERTIES
PREFIX ""
SUFFIX ".so"
OUTPUT_NAME "erllama_nif"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../priv"
)
if(APPLE)
target_link_options(erllama_nif PRIVATE
-undefined dynamic_lookup
-flat_namespace
)
endif()