cmake_minimum_required(VERSION 3.21) project(kpnpp VERSION 0.1.0 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) option(KPN_BUILD_TESTS "Build tests" ON) option(KPN_BUILD_PYTHON "Build Python bindings (requires nanobind)" ON) option(KPN_BUILD_EXAMPLES "Build examples" ON) option(KPN_WEB_DEBUG "Enable web debug UI (cpp-httplib)" OFF) # Sanitizer build. Empty = off. Accepts "thread", "address", "undefined", # or a combination like "address,undefined". Applied to all kpn targets via # the kpn_sanitizer_flags() helper below. # # The lock-free SPSC Channel (include/kpn/channel.hpp) has hand-reasoned # acquire/release ordering; -DKPN_SANITIZER=thread + the channel stress test # (tests/test_channel_stress.cpp) is the dynamic half of verifying it. The # static half is the CDSChecker model-check harness (see verify/). set(KPN_SANITIZER "" CACHE STRING "Build with sanitizer: thread | address | undefined | (empty = off)") # Translate KPN_SANITIZER into compile/link flags. No-op when empty. function(kpn_sanitizer_flags out_var) if(KPN_SANITIZER) set(${out_var} -fsanitize=${KPN_SANITIZER} -fno-omit-frame-pointer -g PARENT_SCOPE) else() set(${out_var} "" PARENT_SCOPE) endif() endfunction() # ── Core library (header-only) ──────────────────────────────────────────────── add_library(kpn INTERFACE) target_include_directories(kpn INTERFACE $ $ ) target_compile_features(kpn INTERFACE cxx_std_20) # Threads required by node/channel implementation find_package(Threads REQUIRED) target_link_libraries(kpn INTERFACE Threads::Threads) # ── Web debug UI (optional) ─────────────────────────────────────────────────── if(KPN_WEB_DEBUG) include(FetchContent) FetchContent_Declare( cpp-httplib GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git GIT_TAG v0.18.0 ) FetchContent_MakeAvailable(cpp-httplib) # httplib made available but NOT forced onto kpn interface — targets opt in # by defining KPN_WEB_DEBUG=1 and linking httplib::httplib themselves. # This prevents tests and other examples from pulling in the HTTP server. endif() # Convenience function for targets that want web debug function(kpn_target_enable_web_debug target) target_compile_definitions(${target} PRIVATE KPN_WEB_DEBUG=1) target_link_libraries(${target} PRIVATE httplib::httplib) endfunction() # ── Tests ───────────────────────────────────────────────────────────────────── if(KPN_BUILD_TESTS) enable_testing() add_subdirectory(tests) endif() # ── Benchmarks ──────────────────────────────────────────────────────────────── option(KPN_BUILD_BENCHMARKS "Build benchmarks" OFF) if(KPN_BUILD_BENCHMARKS) add_subdirectory(benchmarks) endif() # ── Python bindings ─────────────────────────────────────────────────────────── if(KPN_BUILD_PYTHON) find_package(Python 3.8 COMPONENTS Interpreter Development.Module REQUIRED) find_package(nanobind CONFIG QUIET) if(NOT nanobind_FOUND) # Fall back to FetchContent if nanobind not installed system-wide include(FetchContent) FetchContent_Declare( nanobind GIT_REPOSITORY https://github.com/wjakob/nanobind.git GIT_TAG v2.12.0 ) FetchContent_MakeAvailable(nanobind) endif() add_subdirectory(python) endif() # ── Examples ────────────────────────────────────────────────────────────────── if(KPN_BUILD_EXAMPLES) add_subdirectory(examples) endif() # ── Docs (README generation) ────────────────────────────────────────────────── find_package(Python3 QUIET COMPONENTS Interpreter) if(Python3_FOUND) add_custom_target(docs COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/render_readme.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMENT "Rendering README.md from README.md.in" VERBATIM ) endif()