cmake_minimum_required(VERSION 3.19) if(POLICY CMP0167) cmake_policy(SET CMP0167 NEW) endif() project(salmanoff VERSION 0.01.001 LANGUAGES CXX) include(CMakeDependentOption) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/DAPSS.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/DebugOpts.cmake) include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/VerifyBoostDynamic.cmake) include(GNUInstallDirs) # Set C++ standard set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Build type if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug FORCE) endif() # Compiler flags set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") # Ensure installed directories use Debian-standard permissions instead of # inheriting group-writable bits from the build tree. set(CMAKE_INSTALL_DEFAULT_DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) # Mind oscillator configuration set(MIND_VOSCILLATOR_PERIOD_MS 33 CACHE STRING "Mind's virtual osc clock rate (ms)") if(NOT MIND_VOSCILLATOR_PERIOD_MS GREATER 0) message(FATAL_ERROR "MIND_VOSCILLATOR_PERIOD_MS must be a positive integer > 0") endif() math(EXPR MIND_VOSCILLATOR_FREQ_MS "1000 / ${MIND_VOSCILLATOR_PERIOD_MS}") # Device manager reattacher configuration set(MRNTT_DEVMGR_REATTACHER_PERIOD_MS 2000 CACHE STRING "Device manager reattacher period (ms)") if(NOT MRNTT_DEVMGR_REATTACHER_PERIOD_MS GREATER 0) message(FATAL_ERROR "MRNTT_DEVMGR_REATTACHER_PERIOD_MS must be a positive integer > 0") endif() # Stimulus buffer frame period configuration set(STIMBUFF_FRAME_PERIOD_MS 33 CACHE STRING "Stimulus buffer frame period (ms)") if(NOT STIMBUFF_FRAME_PERIOD_MS GREATER 0) message(FATAL_ERROR "STIMBUFF_FRAME_PERIOD_MS must be a positive integer > 0") endif() # World thread configuration option(WORLD_USE_BODY_THREAD "Use body thread for world component instead of separate world thread" OFF) # Test configuration option(ENABLE_TESTS "Enable building tests" OFF) # Set the debug locks variable for config.h if(ENABLE_DEBUG_LOCKS) set(CONFIG_ENABLE_DEBUG_LOCKS TRUE) endif() # Set the debug trace callables variable for config.h if(ENABLE_DEBUG_TRACE_CALLABLES) set(CONFIG_DEBUG_TRACE_CALLABLES TRUE) # Suppress frame-address warnings when using __builtin_return_address() # with values above 0 (See callableTracer.h). set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-frame-address") endif() # Set the world thread variable for config.h if(WORLD_USE_BODY_THREAD) set(CONFIG_WORLD_USE_BODY_THREAD TRUE) endif() # Set the timeout variable for config.h set(CONFIG_DEBUG_QUTEX_DEADLOCK_TIMEOUT_MS ${DEBUG_QUTEX_DEADLOCK_TIMEOUT_MS}) # Set the stimulus buffer frame period variable for config.h set(CONFIG_STIMBUFF_FRAME_PERIOD_MS ${STIMBUFF_FRAME_PERIOD_MS}) # Configure config.h configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/include/config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/config.h @ONLY ) # Include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/include ${CMAKE_CURRENT_SOURCE_DIR}/smocore/include ${CMAKE_CURRENT_BINARY_DIR}/include ) # Find core dependencies # We cannot use header-only Boost.Asio because we need both our dlopen()'d # libraries and the main binary to refer to the same instances of boost::asio's # metadata. If we use header-only Boost.Asio, each dlopen()'d library will have # its own copy of boost::asio's metadata, which will cause a segfault if # boost::asio objects are used inside of a dlopen()'d library. # # Boost.System became header-only in Boost 1.69, and Boost 1.89 removed the # compiled stub library that older package sets still exposed as # libboost_system. This project intentionally links Boost.System as a shared # library to keep one Boost symbol set across the main binary and dlopen()'d # libraries, so Boost 1.89+ is not acceptable for this build. # # Tell CMake we're linking against shared libraries, not static Boost libraries. set(Boost_USE_STATIC_LIBS OFF) set(Boost_USE_HEADER_ONLY OFF) find_package(Boost 1.69...<1.89 REQUIRED) if(Boost_VERSION VERSION_GREATER_EQUAL "1.89.0") message(FATAL_ERROR "Boost ${Boost_VERSION} was found, but salmanoff requires Boost < 1.89 " "because Boost 1.89 removed the dynamically linkable Boost.System " "library. Install a Boost 1.69 through 1.88 development package set " "that provides Boost::system as a shared library. On Ubuntu 26.04, " "install libboost1.88-dev, libboost-system1.88-dev, and " "libboost-log1.88-dev, then clear this build directory's CMake cache " "or configure with -DBoost_DIR=/usr/lib//cmake/Boost-1.88.0.") endif() find_package(Boost 1.69...<1.89 REQUIRED COMPONENTS system log) # Define BOOST_ALL_DYN_LINK project-wide to ensure all Boost libraries use dynamic linking add_compile_definitions(BOOST_ALL_DYN_LINK) find_package(PkgConfig REQUIRED) find_package(FLEX REQUIRED) find_package(BISON REQUIRED) # It's important to note that as of 2025-12-02, RustICL on the RPi5 suddenly # began requiring that the user running Smo be a member of the "render" group. # We need to take that into account when we eventually build installer packages. # Users may also need to be members of the "video" group. # Find OpenCL 1.2 or higher: try find_package first, fall back to pkg-config find_package(OpenCL 1.2 QUIET) if(OpenCL_FOUND) # Normalize find_package variables to match pkg_check_modules naming set(OPENCL_FOUND TRUE) set(OPENCL_INCLUDE_DIRS ${OpenCL_INCLUDE_DIRS}) # Handle both OpenCL_LIBRARY (singular) and OpenCL_LIBRARIES (plural) if(OpenCL_LIBRARIES) set(OPENCL_LIBRARIES ${OpenCL_LIBRARIES}) else() set(OPENCL_LIBRARIES ${OpenCL_LIBRARY}) endif() set(OPENCL_LIBRARY_DIRS "") message(STATUS "Found OpenCL using find_package") # Check if version is available and validate if(OpenCL_VERSION) if(OpenCL_VERSION VERSION_LESS "1.2") message(FATAL_ERROR "OpenCL version ${OpenCL_VERSION} found, but 1.2 or higher is required") endif() message(STATUS "OpenCL version: ${OpenCL_VERSION}") else() message(WARNING "OpenCL version could not be determined. " "Version 1.2+ is required at runtime.") endif() else() # Fall back to pkg-config pkg_check_modules(OPENCL OpenCL) if(NOT OPENCL_FOUND) message(FATAL_ERROR "Failed to find OpenCL: both find_package and " "pkg_check_modules failed. Try installing the " "'ocl-icd-opencl-dev' package (or the appropriate " "OpenCL development package for your system)." ) endif() message(STATUS "Found OpenCL using pkg-config") message(WARNING "OpenCL version could not be determined via pkg-config. " "Version 1.2+ is required at runtime.") endif() # Need dlopen() and dlsym() find_library(DL_LIBRARY NAMES dl ldl) if(NOT DL_LIBRARY) message(FATAL_ERROR "Dynamic linking library (libdl/libldl) not found") endif() # Add third-party dependencies if(ENABLE_TESTS) add_subdirectory(third_party) set(LIBSPINSCALE_BUILD_TESTS ON CACHE BOOL "Build libspinscale unit tests" FORCE) endif() add_subdirectory(buildmach) add_subdirectory(libspinscale) # Add core components add_subdirectory(smocore) add_subdirectory(commonLibs) add_subdirectory(comparatorLibs) add_subdirectory(stimBuffApis) add_subdirectory(wilzorApis) add_subdirectory(devices) # Main executable add_executable(salmanoff main.cpp) target_link_libraries(salmanoff Boost::system Boost::log smocore ${DL_LIBRARY} attachmentSupport ) # Verify Boost dynamic dependencies after build add_custom_command(TARGET salmanoff POST_BUILD COMMAND ${CMAKE_COMMAND} -DVERIFY_FILE="$" -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/VerifyBoostDynamic.cmake COMMENT "Verifying Boost dynamic dependencies for salmanoff" ) # Add all registered DAPSS targets as dependencies add_all_daps_dependencies() add_daps_clean_target() # Add tests if enabled if(ENABLE_TESTS) enable_testing() add_subdirectory(tests) endif() install(TARGETS salmanoff DESTINATION ${CMAKE_INSTALL_BINDIR}) # Install device configuration files (preprocessed .daps files) install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/devices/ DESTINATION ${CMAKE_INSTALL_DATADIR}/salmanoff/devices DIRECTORY_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE FILE_PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ FILES_MATCHING PATTERN "*.daps" ) # Install documentation install(FILES README.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) install(FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR}) # Install example configurations if they exist if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples") install(DIRECTORY examples/ DESTINATION ${CMAKE_INSTALL_DATADIR}/salmanoff/examples) endif() # Include CPack configuration include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/CPackConfig.cmake) include(CPack)