# gguf.cpp — the unified engine behind the gguf-cpp package.
#
# One CMake project, one set of compute kernels, three front ends:
#
#   kernels/     the shared ggml (CPU + optional GPU backends), built once
#   llm/         llama runtime + the OpenAI-compatible HTTP server (gguf-server)
#   diffusion/   the stable-diffusion runtime + its CLI (diffusion)
#   quantizer/   the GGUF quantizer shared library (gguf_quantizer, ctypes)
#
# Previously each front end carried its own copy of ggml and was configured and
# compiled separately. Here they are configured together and every backend —
# CPU, CUDA, HIP, Vulkan, Metal — is compiled exactly once and linked into all
# three artifacts.
#
# Layout is produced by ../scripts/vendor_engine.py; this file and the
# CMakeLists.txt of llm/, diffusion/ and quantizer/ are the hand written part.

cmake_minimum_required(VERSION 3.15...3.28)
project("gguf.cpp" C CXX)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

if (NOT XCODE AND NOT MSVC AND NOT CMAKE_BUILD_TYPE)
    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
message(STATUS "gguf.cpp: CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")

set(CMAKE_C_STANDARD          11)
set(CMAKE_C_STANDARD_REQUIRED true)
set(CMAKE_CXX_STANDARD        17)
set(CMAKE_CXX_STANDARD_REQUIRED true)

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# The quantizer ships as a shared library that links the otherwise static ggml,
# so every object in the build has to be position independent.
set(CMAKE_POSITION_INDEPENDENT_CODE ON)

if (WIN32)
    add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()
if (MSVC)
    add_compile_definitions(_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING)
    add_compile_options("$<$<COMPILE_LANGUAGE:C>:/utf-8>"   "$<$<COMPILE_LANGUAGE:CXX>:/utf-8>")
    add_compile_options("$<$<COMPILE_LANGUAGE:C>:/bigobj>"  "$<$<COMPILE_LANGUAGE:CXX>:/bigobj>")
    add_compile_options("$<$<COMPILE_LANGUAGE:C>:/MP>"      "$<$<COMPILE_LANGUAGE:CXX>:/MP>")
    list(APPEND CMAKE_VS_GLOBALS UseMultiToolTask=true)
    list(APPEND CMAKE_VS_GLOBALS EnforceProcessCountAcrossBuilds=true)
    # statically link the CRT so the binaries run without the VC++ redistributable
    set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>" CACHE STRING "" FORCE)
endif()

# ── Components ───────────────────────────────────────────────────────────────

option(GGUF_CPP_SERVER    "gguf.cpp: build the LLM server engine"  ON)
option(GGUF_CPP_DIFFUSION "gguf.cpp: build the diffusion engine"   ON)
option(GGUF_CPP_QUANTIZER "gguf.cpp: build the quantizer engine"   ON)
option(GGUF_CPP_INSTALL   "gguf.cpp: install the built artifacts"  ON)

# ── Backends ─────────────────────────────────────────────────────────────────
#
# One switch per accelerator for the whole engine: the backend is compiled once
# and shared by the server, the diffuser and the quantizer's ggml path. The
# per-engine spellings the three source trees used before (GGUF_SERVER_CUDA,
# SD_CUDA, QUANTIZER_CUDA, …) are accepted as aliases so existing build
# invocations keep working.

if (APPLE)
    set(GGUF_CPP_METAL_DEFAULT ON)
else()
    set(GGUF_CPP_METAL_DEFAULT OFF)
endif()

option(GGUF_CPP_CUDA   "gguf.cpp: CUDA backend (NVIDIA)"        OFF)
option(GGUF_CPP_HIP    "gguf.cpp: HIP/ROCm backend (AMD)"       OFF)
option(GGUF_CPP_VULKAN "gguf.cpp: Vulkan backend"               OFF)
option(GGUF_CPP_METAL  "gguf.cpp: Metal backend (Apple)"        ${GGUF_CPP_METAL_DEFAULT})
option(GGUF_CPP_BLAS   "gguf.cpp: BLAS backend"                 OFF)

foreach (alias IN ITEMS GGUF_SERVER_CUDA SD_CUDA QUANTIZER_CUDA)
    if (${alias})
        set(GGUF_CPP_CUDA ON)
    endif()
endforeach()
foreach (alias IN ITEMS GGUF_SERVER_HIP SD_HIPBLAS QUANTIZER_HIP)
    if (${alias})
        set(GGUF_CPP_HIP ON)
    endif()
endforeach()
foreach (alias IN ITEMS GGUF_SERVER_VULKAN SD_VULKAN)
    if (${alias})
        set(GGUF_CPP_VULKAN ON)
    endif()
endforeach()
foreach (alias IN ITEMS GGUF_SERVER_METAL SD_METAL QUANTIZER_METAL)
    if (${alias})
        set(GGUF_CPP_METAL ON)
    endif()
endforeach()
if (GGUF_SERVER_BLAS)
    set(GGUF_CPP_BLAS ON)
endif()

if (GGUF_CPP_CUDA AND GGUF_CPP_HIP)
    message(FATAL_ERROR "gguf.cpp: GGUF_CPP_CUDA and GGUF_CPP_HIP are mutually exclusive")
endif()

set(GGUF_CPP_BACKENDS "CPU")
if (GGUF_CPP_CUDA)
    set(GGML_CUDA ON)
    list(APPEND GGUF_CPP_BACKENDS "CUDA")
endif()
if (GGUF_CPP_HIP)
    set(GGML_HIP ON)
    list(APPEND GGUF_CPP_BACKENDS "HIP")
endif()
if (GGUF_CPP_VULKAN)
    set(GGML_VULKAN ON)
    list(APPEND GGUF_CPP_BACKENDS "Vulkan")
endif()
if (GGUF_CPP_METAL)
    set(GGML_METAL ON)
    list(APPEND GGUF_CPP_BACKENDS "Metal")
endif()
if (GGUF_CPP_BLAS)
    set(GGML_BLAS ON)
    list(APPEND GGUF_CPP_BACKENDS "BLAS")
endif()
string(REPLACE ";" ", " GGUF_CPP_BACKEND_LIST "${GGUF_CPP_BACKENDS}")
message(STATUS "gguf.cpp: backends: ${GGUF_CPP_BACKEND_LIST}")

# The quantizer's GPU paths are its own hand written kernels, kept independent
# of the ggml backends (they run without a ggml context).
set(QUANTIZER_CUDA  ${GGUF_CPP_CUDA})
set(QUANTIZER_HIP   ${GGUF_CPP_HIP})
set(QUANTIZER_METAL ${GGUF_CPP_METAL})

# ── Diagnostics ──────────────────────────────────────────────────────────────

option(GGUF_CPP_ALL_WARNINGS      "gguf.cpp: enable all compiler warnings" ON)
option(GGUF_CPP_FATAL_WARNINGS    "gguf.cpp: enable -Werror"               OFF)
option(GGUF_CPP_SANITIZE_THREAD    "gguf.cpp: thread sanitizer"            OFF)
option(GGUF_CPP_SANITIZE_ADDRESS   "gguf.cpp: address sanitizer"           OFF)
option(GGUF_CPP_SANITIZE_UNDEFINED "gguf.cpp: undefined sanitizer"         OFF)

# the vendored sources still use the LLAMA_* names internally
set(LLAMA_ALL_WARNINGS       ${GGUF_CPP_ALL_WARNINGS})
set(LLAMA_FATAL_WARNINGS     ${GGUF_CPP_FATAL_WARNINGS})
set(LLAMA_SANITIZE_THREAD    ${GGUF_CPP_SANITIZE_THREAD})
set(LLAMA_SANITIZE_ADDRESS   ${GGUF_CPP_SANITIZE_ADDRESS})
set(LLAMA_SANITIZE_UNDEFINED ${GGUF_CPP_SANITIZE_UNDEFINED})

set(GGML_ALL_WARNINGS   ${GGUF_CPP_ALL_WARNINGS})
set(GGML_FATAL_WARNINGS ${GGUF_CPP_FATAL_WARNINGS})

# ── Shared kernels ───────────────────────────────────────────────────────────

# The diffusion runtime asserts GGML_MAX_NAME >= 128 (ggml_extend.hpp). It is a
# compile-time constant that changes the layout of struct ggml_tensor, so it has
# to be set for *everything* — kernels, llama runtime and both front ends —
# before the first target is defined.
add_compile_definitions(GGML_MAX_NAME=128)

list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/llm/cmake/")
include(build-info)

if (NOT DEFINED LLAMA_BUILD_NUMBER)
    set(LLAMA_BUILD_NUMBER ${BUILD_NUMBER})
endif()
if (NOT DEFINED LLAMA_BUILD_COMMIT)
    set(LLAMA_BUILD_COMMIT ${BUILD_COMMIT})
endif()
set(LLAMA_INSTALL_VERSION 0.0.${LLAMA_BUILD_NUMBER})
set(GGML_BUILD_NUMBER ${LLAMA_BUILD_NUMBER})
set(GGML_BUILD_COMMIT ${LLAMA_BUILD_COMMIT})

if (NOT DEFINED GGML_LLAMAFILE)
    set(GGML_LLAMAFILE_DEFAULT ON)
endif()
if (NOT DEFINED GGML_CUDA_GRAPHS)
    set(GGML_CUDA_GRAPHS_DEFAULT ON)
endif()

# Everything links ggml statically: one executable per front end, no shared
# object to locate at run time.
if (NOT DEFINED BUILD_SHARED_LIBS)
    set(BUILD_SHARED_LIBS OFF)
endif()

set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)

add_subdirectory(kernels)   # ggml — built once, shared by all three front ends

# ── Front ends ───────────────────────────────────────────────────────────────

if (GGUF_CPP_SERVER)
    add_subdirectory(llm)
endif()

if (GGUF_CPP_DIFFUSION)
    add_subdirectory(diffusion)
endif()

if (GGUF_CPP_QUANTIZER)
    add_subdirectory(quantizer)
endif()

# A single target that builds every enabled front end in one go — this is what
# the Python package's CMakeLists depends on.
add_custom_target(gguf-cpp-engines ALL)
foreach (component IN ITEMS gguf-server diffusion-cli gguf_quantizer)
    if (TARGET ${component})
        add_dependencies(gguf-cpp-engines ${component})
    endif()
endforeach()
