cmake_minimum_required(VERSION 3.14...3.28) # for add_link_options and implicit target directories.
project("gk-diffuser-engine" C CXX)

#
# The gk-diffuser engine: the diffusion runtime + `diffusion` CLI compiled on
# top of the gk compute kernels. This tree is the diffusion slice of the
# unified ggk engine; gk/ and quantizer/src/kernels are shared verbatim with
# gk-server, so the two packages can never disagree about a kernel or a block
# format.
#
#   gk/          the gk compute kernels (CPU + optional GPU backends), and the
#                ggml compatibility layer the runtime above still speaks
#   quantizer/   only src/kernels: the qz_* block codecs gk compiles in
#   diffusion/   the diffusion runtime + `diffusion` CLI (with its own
#                vendored thirdparty: nlohmann/json, stb, darts)
#
# There is no ggml anywhere: the graphs the runtime builds are evaluated by
# gk, and everything is compiled from the sources in this directory, in a
# single build.
#

set(CMAKE_WARN_UNUSED_CLI YES)
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("CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}")

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

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

if (MINGW OR EMSCRIPTEN)
    set(BUILD_SHARED_LIBS_DEFAULT OFF)
else()
    set(BUILD_SHARED_LIBS_DEFAULT ON)
endif()

option(BUILD_SHARED_LIBS "build shared libraries" ${BUILD_SHARED_LIBS_DEFAULT})

if (WIN32)
    add_compile_definitions(_CRT_SECURE_NO_WARNINGS)
endif()

if (MSVC)
    add_compile_options("$<$<COMPILE_LANGUAGE:C>:/utf-8>")
    add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/utf-8>")
    add_compile_options("$<$<COMPILE_LANGUAGE:C>:/bigobj>")
    add_compile_options("$<$<COMPILE_LANGUAGE:CXX>:/bigobj>")
    list(APPEND CMAKE_VS_GLOBALS UseMultiToolTask=true)
    list(APPEND CMAKE_VS_GLOBALS EnforceProcessCountAcrossBuilds=true)
endif()

#
# Options
#

# backends - these forward straight to gk; declaring them here first wins,
# because option() leaves an existing entry alone. Metal defaults on for
# macOS, matching the unified ggk engine.
option(GK_CUDA   "gk: CUDA backend"     OFF)
option(GK_HIP    "gk: ROCm/HIP backend" OFF)
option(GK_METAL  "gk: Metal backend"    ${APPLE})
option(GK_VULKAN "gk: Vulkan backend"   OFF)

if (GK_CUDA)
    message(STATUS "gk-diffuser: CUDA backend enabled")
endif()
if (GK_HIP)
    message(STATUS "gk-diffuser: HIP backend enabled")
    # the device objects must be position-independent, or the default-PIE
    # executable link fails on distros that default to PIE
    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
endif()
if (GK_METAL)
    message(STATUS "gk-diffuser: Metal backend enabled")
endif()
if (GK_VULKAN)
    message(STATUS "gk-diffuser: Vulkan backend enabled")
endif()

#
# gk options
#
# gk's own test programs check differentially against a reference ggml, which
# this tree does not carry; they are built from the standalone gk checkout.
#

set(GK_BUILD_TESTS OFF CACHE BOOL "gk: build the test programs" FORCE)

#
# Build
#

# Diffusion models carry tensor names longer than ggml's default 64-byte cap.
# The compat layer's ggml_tensor and gk's gk_tensor are layout-identical by
# construction — a static assert in ggml-compat-impl.h checks it — so the name
# field has to be widened on both sides together, globally, before anything
# compiles. Widening only one is what makes that assert fire.
add_compile_definitions(GGML_MAX_NAME=128 GK_MAX_NAME=128)

add_subdirectory(gk/compat)          # gk, and the ggml API on top of it
add_subdirectory(diffusion)          # diffusion runtime + CLI
