cmake_minimum_required(VERSION 3.25)
project(PrismEngineering LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
option(PRISM_SANITIZERS "Address and undefined behavior sanitizers" OFF)
option(PRISM_THREAD_SANITIZER "ThreadSanitizer (separate build from ASan)" OFF)
option(PRISM_ASIO "Build the pinned standalone Asio coroutine experiment" OFF)
if(PRISM_SANITIZERS AND PRISM_THREAD_SANITIZER)
  message(FATAL_ERROR "Use separate ASan/UBSan and ThreadSanitizer build directories")
endif()
add_library(prism_options INTERFACE)
target_compile_features(prism_options INTERFACE cxx_std_23)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
  target_compile_options(prism_options INTERFACE -Wall -Wextra -Wpedantic -Wconversion -Wshadow)
  if(PRISM_SANITIZERS)
    target_compile_options(prism_options INTERFACE -fsanitize=address,undefined -fno-omit-frame-pointer)
    target_link_options(prism_options INTERFACE -fsanitize=address,undefined)
  elseif(PRISM_THREAD_SANITIZER)
    target_compile_options(prism_options INTERFACE -fsanitize=thread -fno-omit-frame-pointer)
    target_link_options(prism_options INTERFACE -fsanitize=thread)
  endif()
elseif(PRISM_SANITIZERS OR PRISM_THREAD_SANITIZER)
  message(FATAL_ERROR "These sanitizer options require Clang or GCC")
endif()
find_package(Threads REQUIRED)
add_library(prism_engineering src/worker_pool.cpp src/asset_pipeline.cpp src/allocator_lab.cpp)
target_include_directories(prism_engineering PUBLIC include)
target_link_libraries(prism_engineering PUBLIC Threads::Threads PRIVATE prism_options)
target_compile_features(prism_engineering PUBLIC cxx_std_23)
add_executable(prism-engineering src/experiment.cpp)
target_link_libraries(prism-engineering PRIVATE prism_engineering prism_options)
add_executable(prism-gallery src/gallery_main.cpp)
target_link_libraries(prism-gallery PRIVATE prism_engineering prism_options)
add_executable(prism-allocators src/allocator_main.cpp)
target_link_libraries(prism-allocators PRIVATE prism_engineering prism_options)
include(CTest)
if(BUILD_TESTING)
  add_executable(prism-engineering-tests tests/worker_pool_tests.cpp)
  target_link_libraries(prism-engineering-tests PRIVATE prism_engineering prism_options)
  add_test(NAME engineering-contracts COMMAND prism-engineering-tests)
  add_test(NAME engineering-experiment COMMAND prism-engineering)
  add_executable(prism-async-tests tests/asset_pipeline_tests.cpp)
  target_link_libraries(prism-async-tests PRIVATE prism_engineering prism_options)
  add_test(NAME async-image-contracts COMMAND prism-async-tests)
  add_test(NAME async-gallery-experiment COMMAND prism-gallery)
  add_executable(prism-allocator-tests tests/allocator_tests.cpp)
  target_link_libraries(prism-allocator-tests PRIVATE prism_engineering prism_options)
  add_test(NAME allocator-contracts COMMAND prism-allocator-tests)
  add_test(NAME allocator-experiment COMMAND prism-allocators)
  set_tests_properties(engineering-contracts engineering-experiment async-image-contracts async-gallery-experiment allocator-contracts allocator-experiment PROPERTIES TIMEOUT 15)
endif()

if(PRISM_ASIO)
  include(FetchContent)
  FetchContent_Declare(asio
    URL https://github.com/chriskohlhoff/asio/archive/refs/tags/asio-1-38-2.tar.gz
    URL_HASH SHA256=9f2648fa483e58a6bf848d970ee0ea650ca19ed7769dfa520ed4f7b8d27af1db
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
  # No vendoring or system install. CMake keeps this optional dependency in the build tree.
  FetchContent_MakeAvailable(asio)
  add_executable(prism-coroutines src/coroutine_main.cpp)
  target_include_directories(prism-coroutines SYSTEM PRIVATE "${asio_SOURCE_DIR}/asio/include")
  target_compile_definitions(prism-coroutines PRIVATE ASIO_STANDALONE)
  target_link_libraries(prism-coroutines PRIVATE prism_options Threads::Threads)
  if(BUILD_TESTING)
    add_test(NAME coroutine-contracts COMMAND prism-coroutines)
    set_tests_properties(coroutine-contracts PROPERTIES TIMEOUT 15)
  endif()
endif()
