c_src/CMakeLists.txt

# 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()