cmake_minimum_required(VERSION 3.18)
project(dualdict_cpp LANGUAGES CXX)
find_package(Python 3.10 COMPONENTS Interpreter Development.Module REQUIRED)
execute_process(COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
                OUTPUT_VARIABLE nanobind_DIR OUTPUT_STRIP_TRAILING_WHITESPACE COMMAND_ERROR_IS_FATAL ANY)
find_package(nanobind 3.1 CONFIG REQUIRED)
option(DUALDICT_SANITIZERS "Build native backends with ASan and UBSan" OFF)
if(DUALDICT_SANITIZERS)
    if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang" OR MSVC)
        message(FATAL_ERROR "DUALDICT_SANITIZERS requires GCC or Clang on Unix")
    endif()
    add_compile_options(-fsanitize=address,undefined -fno-omit-frame-pointer -fno-sanitize-recover=all)
    add_link_options(-fsanitize=address,undefined)
endif()
nanobind_add_module(_dualdict_cpp NOMINSIZE src/module.cpp)
target_compile_features(_dualdict_cpp PRIVATE cxx_std_17)
install(TARGETS _dualdict_cpp LIBRARY DESTINATION . RUNTIME DESTINATION .)

# Keep V3 unchanged; cumulative stages make V3.1 improvements measurable.
foreach(stage IN ITEMS growth hash v31)
    set(target _dualdict_cpp_${stage})
    nanobind_add_module(${target} NOMINSIZE src/module_v31.cpp)
    target_compile_features(${target} PRIVATE cxx_std_17)
    target_compile_definitions(${target} PRIVATE V31_MODULE=${target} V31_GROWTH)
    if(NOT stage STREQUAL "growth")
        target_compile_definitions(${target} PRIVATE V31_HASH)
    endif()
    if(stage STREQUAL "v31")
        target_compile_definitions(${target} PRIVATE V31_COMPACT)
    endif()
    install(TARGETS ${target} LIBRARY DESTINATION . RUNTIME DESTINATION .)
endforeach()

nanobind_add_module(_dualdict_cpp_v32 NOMINSIZE src/module_v31.cpp)
target_compile_features(_dualdict_cpp_v32 PRIVATE cxx_std_17)
target_compile_definitions(_dualdict_cpp_v32 PRIVATE V31_MODULE=_dualdict_cpp_v32 V31_GROWTH V31_HASH V31_COMPACT V32_FIELDS)
install(TARGETS _dualdict_cpp_v32 LIBRARY DESTINATION . RUNTIME DESTINATION .)

nanobind_add_module(_dualdict_cpp_v33 NOMINSIZE src/module_v31.cpp)
target_compile_features(_dualdict_cpp_v33 PRIVATE cxx_std_17)
target_compile_definitions(_dualdict_cpp_v33 PRIVATE V31_MODULE=_dualdict_cpp_v33 V31_GROWTH V31_HASH V31_COMPACT V32_FIELDS V33_RAW)
install(TARGETS _dualdict_cpp_v33 LIBRARY DESTINATION . RUNTIME DESTINATION .)

nanobind_add_module(_dualdict_cpp_v331 NOMINSIZE src/module_v331.cpp)
target_compile_features(_dualdict_cpp_v331 PRIVATE cxx_std_17)
target_compile_definitions(_dualdict_cpp_v331 PRIVATE V31_MODULE=_dualdict_cpp_v331 V31_GROWTH V31_HASH V31_COMPACT V32_FIELDS V33_RAW)
install(TARGETS _dualdict_cpp_v331 LIBRARY DESTINATION . RUNTIME DESTINATION .)

# Private fault hooks are absent from normal wheels.
option(DUALDICT_TEST_ALLOCATIONS "Enable V3.3.1 pool allocation fault injection" OFF)
if(DUALDICT_TEST_ALLOCATIONS)
    target_compile_definitions(_dualdict_cpp_v331 PRIVATE DUALDICT_TEST_ALLOCATIONS)
endif()
