cmake_minimum_required(VERSION 3.24)
project(cdr VERSION 0.1.0 LANGUAGES CXX)

# Applies to fetched dependencies too (Catch2 must be built with the same
# standard as the test TUs, or its C++17+ StringMaker symbols go missing).
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_library(cdr INTERFACE)
add_library(cdr::cdr ALIAS cdr)
target_compile_features(cdr INTERFACE cxx_std_23)
target_include_directories(cdr INTERFACE
  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
  $<INSTALL_INTERFACE:include>)

include(FetchContent)

# Reflection backend. Only the core reflection headers are used (rfl/to_view)
# — header-only, so we take reflect-cpp's include directory and never build
# its compiled library (which exists to bundle yyjson for the JSON backend).
# Consumers may instead provide the headers themselves (e.g. the espp
# reflect_cpp component vendors the same tag as a submodule) by setting
# CDR_REFLECTCPP_INCLUDE_DIR.
set(CDR_REFLECTCPP_INCLUDE_DIR "" CACHE PATH
  "Existing reflect-cpp include dir; empty = fetch v0.25.0")
if(CDR_REFLECTCPP_INCLUDE_DIR)
  target_include_directories(cdr INTERFACE ${CDR_REFLECTCPP_INCLUDE_DIR})
else()
  FetchContent_Declare(reflectcpp
    URL https://github.com/getml/reflect-cpp/archive/refs/tags/v0.25.0.tar.gz
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE
    EXCLUDE_FROM_ALL)
  FetchContent_MakeAvailable(reflectcpp)
  target_include_directories(cdr INTERFACE
    $<BUILD_INTERFACE:${reflectcpp_SOURCE_DIR}/include>)
endif()

if(PROJECT_IS_TOP_LEVEL)
  option(CDR_BUILD_TESTS "Build cdr tests" ON)
else()
  option(CDR_BUILD_TESTS "Build cdr tests" OFF)
endif()

if(CDR_BUILD_TESTS)
  enable_testing()
  FetchContent_Declare(catch2
    URL https://github.com/catchorg/Catch2/archive/refs/tags/v3.15.3.tar.gz
    DOWNLOAD_EXTRACT_TIMESTAMP TRUE)
  FetchContent_MakeAvailable(catch2)
  add_subdirectory(tests)

  # Differential-test fixture emitter; pipe into tools/differential/check_pycdr2.py
  add_executable(emit_fixtures tools/differential/emit_fixtures.cpp)
  target_link_libraries(emit_fixtures PRIVATE cdr::cdr)
  set_target_properties(emit_fixtures PROPERTIES
    RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tools)
endif()

# libFuzzer harnesses. Needs a clang with libFuzzer (AppleClang lacks it):
#   cmake -B build-fuzz -DCDR_BUILD_FUZZERS=ON \
#         -DCMAKE_CXX_COMPILER=$(brew --prefix llvm)/bin/clang++
option(CDR_BUILD_FUZZERS "Build libFuzzer targets" OFF)
if(CDR_BUILD_FUZZERS)
  foreach(fuzzer fuzz_deserialize fuzz_param_list)
    add_executable(${fuzzer} fuzz/${fuzzer}.cpp)
    target_link_libraries(${fuzzer} PRIVATE cdr::cdr)
    target_compile_options(${fuzzer} PRIVATE
      -fsanitize=fuzzer,address,undefined -g -O1)
    target_link_options(${fuzzer} PRIVATE
      -fsanitize=fuzzer,address,undefined)
    set_target_properties(${fuzzer} PROPERTIES
      RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/fuzz)
  endforeach()
endif()
