Files
ollama_ollama/llama/server/CMakeLists.txt
T
jmorganca 25223160d8 llama/compat: add in-memory shim so llama-server can load Ollama-format GGUFs
Older Ollama builds ship GGUFs that diverge slightly from upstream llama.cpp
in arch names, KV keys, tensor names, and (for vision models) file layout
(text+vision in one monolithic file). This adds a self-contained compat
layer that translates those files in memory at load time, so
~/.ollama/models/blobs/* can be served by upstream llama-server with no
re-conversion and no re-download.

Structure:
  llama/compat/
    llama-ollama-compat.{h,cpp}   — the shim (Ollama-owned, ~500 LOC)
    upstream-edits.patch          — ~48 lines of call-site hooks in 6 upstream files
    compat.cmake                  — include()-able CMake fragment
    README.md                     — what/why/how-to-regen

Integration: llama/server/CMakeLists.txt includes compat.cmake and passes
OLLAMA_LLAMA_CPP_COMPAT_PATCH_COMMAND to FetchContent_Declare via
PATCH_COMMAND. When OLLAMA_LLAMA_CPP_SOURCE is set (dev mode), the patch is
skipped so the developer's tree stays untouched.

Currently handles gemma3 (text + vision). Pattern is data-driven — adding
other archs is a new handle_<arch>() + one dispatch line. See README for
the per-arch checklist.

Verified end-to-end: `llama-server --model BLOB --mmproj BLOB` with an
Ollama gemma3:latest blob answers both text prompts ("Paris") and vision
prompts (correct image descriptions).
2026-04-20 09:29:34 -07:00

218 lines
9.9 KiB
CMake

cmake_minimum_required(VERSION 3.24)
project(ollama-llama-server C CXX)
# Handle cross-compilation on macOS: when CMAKE_OSX_ARCHITECTURES is set to a
# single architecture different from the host, make downstream architecture
# detection match the target slice. ggml uses CMAKE_SYSTEM_PROCESSOR to decide
# which CPU backend variants to build.
if(CMAKE_OSX_ARCHITECTURES AND NOT CMAKE_OSX_ARCHITECTURES MATCHES ";")
if(CMAKE_OSX_ARCHITECTURES STREQUAL "x86_64" AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64")
message(STATUS "Cross-compiling for x86_64: overriding CMAKE_SYSTEM_PROCESSOR from ${CMAKE_SYSTEM_PROCESSOR} to x86_64")
set(CMAKE_SYSTEM_PROCESSOR "x86_64")
elseif(CMAKE_OSX_ARCHITECTURES STREQUAL "arm64" AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64")
message(STATUS "Cross-compiling for arm64: overriding CMAKE_SYSTEM_PROCESSOR from ${CMAKE_SYSTEM_PROCESSOR} to arm64")
set(CMAKE_SYSTEM_PROCESSOR "arm64")
endif()
endif()
# Ensure all shared libraries and executables can find their dependencies
# in the same directory at runtime (via $ORIGIN on Linux, @loader_path on macOS).
if(NOT APPLE)
set(CMAKE_INSTALL_RPATH "$ORIGIN")
set(CMAKE_BUILD_RPATH "$ORIGIN")
endif()
include(FetchContent)
# Read pinned upstream commit from version file (shared with Dockerfile)
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/../../LLAMA_CPP_VERSION" LLAMA_CPP_GIT_TAG)
string(STRIP "${LLAMA_CPP_GIT_TAG}" LLAMA_CPP_GIT_TAG)
# Allow local source override via environment variable (like OLLAMA_MLX_SOURCE)
if(DEFINED ENV{OLLAMA_LLAMA_CPP_SOURCE})
get_filename_component(_src "$ENV{OLLAMA_LLAMA_CPP_SOURCE}" ABSOLUTE BASE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(FETCHCONTENT_SOURCE_DIR_LLAMA_CPP "${_src}" CACHE PATH "" FORCE)
message(STATUS "Using local llama.cpp source: ${_src}")
endif()
# Ollama-compat shim: overlays the fetched llama.cpp source with a tiny
# in-memory translation layer that lets upstream llama-server load GGUFs
# produced by older Ollama versions (e.g. existing ~/.ollama/models/blobs).
# See llama/compat/README.md for details.
#
# The patch only runs when fetching from GitHub — if a local source override
# is active, leave the developer's tree alone (they can apply by hand if
# they want to iterate on the compat layer).
set(_ollama_compat_patch_cmd "")
if(NOT DEFINED ENV{OLLAMA_LLAMA_CPP_SOURCE})
include(${CMAKE_CURRENT_SOURCE_DIR}/../compat/compat.cmake)
set(_ollama_compat_patch_cmd PATCH_COMMAND ${OLLAMA_LLAMA_CPP_COMPAT_PATCH_COMMAND})
endif()
# Configure upstream build options BEFORE FetchContent_MakeAvailable.
# When included via FetchContent, llama.cpp sets LLAMA_STANDALONE=OFF
# so all optional builds default to OFF. We explicitly enable what we need.
set(LLAMA_BUILD_COMMON ON CACHE BOOL "" FORCE)
set(LLAMA_BUILD_TOOLS ON CACHE BOOL "" FORCE)
set(LLAMA_BUILD_SERVER ON CACHE BOOL "" FORCE)
set(LLAMA_BUILD_HTML OFF CACHE BOOL "" FORCE)
set(LLAMA_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(LLAMA_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
set(LLAMA_TOOLS_INSTALL OFF CACHE BOOL "" FORCE)
set(LLAMA_CURL OFF CACHE BOOL "" FORCE)
set(LLAMA_OPENSSL OFF CACHE BOOL "" FORCE)
FetchContent_Declare(
llama_cpp
GIT_REPOSITORY "https://github.com/ggml-org/llama.cpp.git"
GIT_TAG ${LLAMA_CPP_GIT_TAG}
GIT_SHALLOW TRUE
${_ollama_compat_patch_cmd}
)
FetchContent_MakeAvailable(llama_cpp)
# Find GPU toolkits for runtime dependency bundling.
# The upstream llama.cpp build finds these internally, but we need the
# variables (CUDAToolkit_LIBRARY_DIR, etc.) in our install scope.
if(GGML_CUDA)
find_package(CUDAToolkit)
endif()
if(GGML_HIP)
find_package(hip)
endif()
# Install layout under lib/ollama/:
#
# CPU build (OLLAMA_RUNNER_DIR=""):
# lib/ollama/llama-server
# lib/ollama/libggml-base.so
# lib/ollama/libggml.so
# lib/ollama/libllama.so
# lib/ollama/libggml-cpu*.so (all CPU variants)
#
# GPU build (OLLAMA_RUNNER_DIR="cuda_v12" etc.):
# lib/ollama/cuda_v12/libggml-cuda.so (GPU backend only)
set(_base_dest "lib/ollama")
if(OLLAMA_RUNNER_DIR)
# GPU backend build — install the GPU backend .so/.dll module.
# install(CODE) runs at install time to handle both single-config and
# multi-config generator layouts.
install(CODE "
file(GLOB _gpu_backends
\"${CMAKE_BINARY_DIR}/bin/libggml-${OLLAMA_GPU_BACKEND}*\"
\"${CMAKE_BINARY_DIR}/bin/Release/ggml-${OLLAMA_GPU_BACKEND}*.dll\"
\"${CMAKE_BINARY_DIR}/bin/ggml-${OLLAMA_GPU_BACKEND}*.dll\"
)
foreach(_f \${_gpu_backends})
file(INSTALL \${_f} DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${_base_dest}/${OLLAMA_RUNNER_DIR}\")
endforeach()
" COMPONENT llama-server)
# Bundle GPU runtime libraries (cublas, cudart, rocblas, etc.)
# These are needed at runtime by the GPU backend .so
if(GGML_CUDA AND CUDAToolkit_FOUND)
# Find the actual ggml-cuda target to get its runtime dependencies
if(TARGET ggml-cuda)
install(TARGETS ggml-cuda
RUNTIME_DEPENDENCIES
DIRECTORIES ${CUDAToolkit_BIN_DIR} ${CUDAToolkit_BIN_DIR}/x64 ${CUDAToolkit_LIBRARY_DIR}
PRE_INCLUDE_REGEXES cublas cublasLt cudart
PRE_EXCLUDE_REGEXES ".*"
RUNTIME DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
LIBRARY DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
)
endif()
endif()
if(GGML_HIP)
if(TARGET ggml-hip)
install(TARGETS ggml-hip
RUNTIME_DEPENDENCY_SET rocm_deps
RUNTIME DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
LIBRARY DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
)
install(RUNTIME_DEPENDENCY_SET rocm_deps
DIRECTORIES ${HIP_BIN_INSTALL_DIR} ${HIP_LIB_INSTALL_DIR}
PRE_INCLUDE_REGEXES hipblas rocblas amdhip64 rocsolver amd_comgr hsa-runtime64 rocsparse tinfo rocprofiler-register roctx64 rocroller drm drm_amdgpu numa elf
PRE_EXCLUDE_REGEXES ".*"
POST_EXCLUDE_REGEXES "system32"
RUNTIME DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
LIBRARY DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
)
foreach(_hip_dir IN ITEMS ${HIP_BIN_INSTALL_DIR} ${HIP_LIB_INSTALL_DIR})
if(EXISTS ${_hip_dir}/rocblas)
install(DIRECTORY ${_hip_dir}/rocblas
DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}"
COMPONENT llama-server)
break()
endif()
endforeach()
endif()
endif()
if(GGML_VULKAN)
if(TARGET ggml-vulkan)
install(TARGETS ggml-vulkan
RUNTIME_DEPENDENCIES
PRE_INCLUDE_REGEXES vulkan
PRE_EXCLUDE_REGEXES ".*"
RUNTIME DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
LIBRARY DESTINATION "${_base_dest}/${OLLAMA_RUNNER_DIR}" COMPONENT llama-server
)
endif()
endif()
else()
# CPU/base build — install llama-server, llama-quantize + all shared libs + CPU backend modules
# RUNTIME covers executables and Windows DLLs; LIBRARY covers .so on Linux
install(TARGETS llama-server llama-quantize
RUNTIME DESTINATION ${_base_dest} COMPONENT llama-server OPTIONAL)
install(TARGETS ggml-base ggml llama mtmd
RUNTIME DESTINATION ${_base_dest} COMPONENT llama-server OPTIONAL
LIBRARY DESTINATION ${_base_dest} COMPONENT llama-server OPTIONAL)
# On Windows with /MD, bundle the MSVC CRT DLLs (vcruntime140.dll,
# msvcp140.dll, vcomp140.dll, etc.) alongside the executables so users
# don't need the VC++ Redistributable installed separately.
if(MSVC AND GGML_BACKEND_DL)
cmake_path(GET CMAKE_CXX_COMPILER PARENT_PATH _msvc_bin_dir)
# Walk up from the compiler bin dir to find the VC redist directory
# e.g. .../MSVC/14.44.35207/bin/Hostx64/x64 -> .../MSVC/14.44.35207
cmake_path(GET _msvc_bin_dir PARENT_PATH _tmp)
cmake_path(GET _tmp PARENT_PATH _tmp)
cmake_path(GET _tmp PARENT_PATH _msvc_ver_dir)
# The redist version may differ from the toolset version, so glob for it
file(GLOB _vc_redist_dirs "${_msvc_ver_dir}/../../../Redist/MSVC/*/x64/Microsoft.VC143.CRT")
file(GLOB _vc_openmp_dirs "${_msvc_ver_dir}/../../../Redist/MSVC/*/x64/Microsoft.VC143.OPENMP")
set(_vc_crt_dlls)
foreach(_dir ${_vc_redist_dirs} ${_vc_openmp_dirs})
file(GLOB _dlls "${_dir}/*.dll")
list(APPEND _vc_crt_dlls ${_dlls})
endforeach()
if(_vc_crt_dlls)
install(FILES ${_vc_crt_dlls}
DESTINATION ${_base_dest}
COMPONENT llama-server)
else()
message(WARNING "Could not find MSVC CRT redistributable DLLs to bundle")
endif()
endif()
# CPU backend modules (multiple variants from GGML_CPU_ALL_VARIANTS).
# install(CODE) runs at install time (not configure time) so the glob
# finds the built files. Handles both single-config (bin/) and
# multi-config (bin/Release/) generator layouts.
install(CODE "
file(GLOB _cpu_backends
\"${CMAKE_BINARY_DIR}/bin/libggml-cpu*\"
\"${CMAKE_BINARY_DIR}/bin/libggml-blas*\"
\"${CMAKE_BINARY_DIR}/bin/Release/ggml-cpu*.dll\"
\"${CMAKE_BINARY_DIR}/bin/Release/ggml-blas*.dll\"
\"${CMAKE_BINARY_DIR}/bin/ggml-cpu*.dll\"
\"${CMAKE_BINARY_DIR}/bin/ggml-blas*.dll\"
)
foreach(_f \${_cpu_backends})
file(INSTALL \${_f} DESTINATION \"\${CMAKE_INSTALL_PREFIX}/${_base_dest}\")
endforeach()
" COMPONENT llama-server)
endif()