# -- Project Setup ------------------------------------------------------------ cmake_minimum_required(VERSION 3.15 FATAL_ERROR) project(broker C CXX) include(CMakePackageConfigHelpers) include(GNUInstallDirs) include(cmake/CommonCMakeConfig.cmake) include(cmake/ZeekBundle.cmake) set(BROKER_TEST_TIMEOUT 60) get_directory_property(parent_dir PARENT_DIRECTORY) if(parent_dir) set(broker_is_subproject ON) else() set(broker_is_subproject OFF) endif() unset(parent_dir) if ( MSVC ) message(STATUS "Broker currently only supports static bulids on Windows") message(STATUS "Note: continue with ENABLE_STATIC_ONLY") set(ENABLE_STATIC_ONLY ON) endif () # Check the thread library info early as setting compiler flags seems to # interfere with the detection and causes CMAKE_THREAD_LIBS_INIT to not # include -lpthread when it should. if (NOT TARGET Threads::Threads) find_package(Threads REQUIRED) endif () set(LINK_LIBS ${LINK_LIBS} Threads::Threads) # Leave most compiler flags alone when building as subdirectory. if (NOT broker_is_subproject) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/lib) if ( ENABLE_CCACHE ) find_program(CCACHE_PROGRAM ccache) if ( NOT CCACHE_PROGRAM ) message(FATAL_ERROR "ccache not found") endif () message(STATUS "Using ccache: ${CCACHE_PROGRAM}") set(CMAKE_C_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) set(CMAKE_CXX_COMPILER_LAUNCHER ${CCACHE_PROGRAM}) endif () if ( BROKER_SANITIZERS ) set(_sanitizer_flags "-fsanitize=${BROKER_SANITIZERS}") set(_sanitizer_flags "${_sanitizer_flags} -fno-omit-frame-pointer") set(_sanitizer_flags "${_sanitizer_flags} -fno-optimize-sibling-calls") # Technically, then we also need to use the compiler to drive linking and # give the sanitizer flags there, too. However, CMake, by default, uses # the compiler for linking and so the flags automatically get used. See # https://cmake.org/pipermail/cmake/2014-August/058268.html set(CAF_EXTRA_FLAGS "${_sanitizer_flags}") # Set EXTRA_FLAGS if broker isn't being built as part of a Zeek build. # The Zeek build sets it otherwise. if ( NOT ZEEK_SANITIZERS ) set(EXTRA_FLAGS "${EXTRA_FLAGS} ${_sanitizer_flags}") endif () endif() endif() if ( MSVC ) # Allow more sections in object files, otherwise Broker fails to compile. set(EXTRA_FLAGS "${EXTRA_FLAGS} /bigobj") # Similar to -funsigned-char on other platforms. set(EXTRA_FLAGS "${EXTRA_FLAGS} /J") else () # Increase warnings. set(EXTRA_FLAGS "${EXTRA_FLAGS} -Wall -Wno-unused -pedantic") # Increase maximum number of instantiations. set(EXTRA_FLAGS "${EXTRA_FLAGS} -ftemplate-depth=512") # Make sure to get the full context on template errors. set(EXTRA_FLAGS "${EXTRA_FLAGS} -ftemplate-backtrace-limit=0") endif () # Append our extra flags to the existing value of CXXFLAGS. set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_FLAGS}") include(RequireCXX17) include(CheckCXXSourceCompiles) # Unfortunately, std::filesystem is a mess. The feature checks at compile time # via __has_include() or __cpp_lib_filesystem only tell half of the # story. On some older Clang releases, one also needs to additional link to # -lc++fs. On some older GCC releases, -lstdc++-fs is required. We do have a # fallback for pre-std-filesystem on POSIX systems. So, instead of doing all the # flag guessing, we simply check once whether code using compiles, # links and runs. Otherwise, we fall back to our pre-std-filesystem # implementation - or raise an error when building on Windows (since we don't # have a fallback on this platform). check_cxx_source_compiles(" #include #include int main(int, char**) { auto cwd = std::filesystem::current_path(); auto str = cwd.string(); return str.empty() ? EXIT_FAILURE : EXIT_SUCCESS; } " BROKER_HAS_STD_FILESYSTEM) if (MSVC) if (NOT BROKER_HAS_STD_FILESYSTEM) message(FATAL_ERROR "No std::filesystem support! Required on MSVC.") endif () set(BROKER_USE_SSE2 OFF) elseif (NOT BROKER_DISABLE_SSE2_CHECK) include(CheckIncludeFiles) set(CMAKE_REQUIRED_FLAGS -msse2) check_include_files(emmintrin.h BROKER_USE_SSE2) set(CMAKE_REQUIRED_FLAGS) if (BROKER_USE_SSE2) add_definitions(-msse2) endif () endif () if (NOT BROKER_DISABLE_ATOMICS_CHECK) set(atomic_64bit_ops_test " #include struct s64 { char a, b, c, d, e, f, g, h; }; int main() { std::atomic x; x.store({}); x.load(); return 0; } ") check_cxx_source_compiles("${atomic_64bit_ops_test}" atomic64_builtin) if ( NOT atomic64_builtin ) set(CMAKE_REQUIRED_LIBRARIES atomic) check_cxx_source_compiles("${atomic_64bit_ops_test}" atomic64_with_lib) set(CMAKE_REQUIRED_LIBRARIES) if ( atomic64_with_lib ) set(LINK_LIBS ${LINK_LIBS} atomic) else () # Guess we'll find out for sure when we compile/link. message(WARNING "build may fail due to missing 64-bit atomic support") endif () endif () endif () # -- Platform Setup ---------------------------------------------------------- if (APPLE) set(BROKER_APPLE true) elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux") set(BROKER_LINUX true) elseif(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD") set(BROKER_FREEBSD true) elseif(WIN32) set(BROKER_WINDOWS true) endif () include(TestBigEndian) test_big_endian(BROKER_BIG_ENDIAN) # -- Dependencies ------------------------------------------------------------- if (WIN32) set(LINK_LIBS ${LINK_LIBS} ws2_32 iphlpapi crypt32) endif () # Search for OpenSSL if not already provided by parent project if (NOT OPENSSL_LIBRARIES) find_package(OpenSSL REQUIRED) include_directories(BEFORE ${OPENSSL_INCLUDE_DIR}) endif() set(LINK_LIBS ${LINK_LIBS} OpenSSL::SSL OpenSSL::Crypto) function(add_bundled_caf) # Disable unnecessary features and make sure CAF builds static libraries. set(CAF_ENABLE_EXAMPLES OFF) set(CAF_ENABLE_TESTING OFF) set(CAF_ENABLE_TOOLS OFF) set(BUILD_SHARED_LIBS OFF) if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") add_subdirectory(caf EXCLUDE_FROM_ALL SYSTEM) else() add_subdirectory(caf EXCLUDE_FROM_ALL) endif() # Disable a few false positives / irrelevant warnings while building CAF. if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU") target_compile_options(caf_internal INTERFACE -Wno-invalid-offsetof -Wno-documentation) endif () endfunction() # Bundle prometheus-cpp. This approach is currently experimental. if (CMAKE_MSVC_RUNTIME_LIBRARY) set(msvc_zeekbundle_args CMAKE_MSVC_RUNTIME_LIBRARY=${CMAKE_MSVC_RUNTIME_LIBRARY} CMAKE_MSVC_RUNTIME_LIBRARY_FLAG=${CMAKE_MSVC_RUNTIME_LIBRARY_FLAG}) endif () ZeekBundle_Add( NAME prometheus-cpp FETCH SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/auxil/prometheus-cpp CONFIGURE ${msvc_zeekbundle_args} ENABLE_COMPRESSION=OFF ENABLE_PUSH=OFF) unset(msvc_zeekbundle_args) # Bundle all prometheus-cpp libraries that we need as single interface library. add_library(broker-prometheus-cpp INTERFACE) target_link_libraries( broker-prometheus-cpp INTERFACE $ $) # Prometheus-cpp sets C++ 11 via CMake property, but we need C++ 17. target_compile_features(broker-prometheus-cpp INTERFACE cxx_std_17) # Must be exported to keep CMake happy, even though we only need it internally. install(TARGETS broker-prometheus-cpp EXPORT BrokerTargets DESTINATION ${CMAKE_INSTALL_LIBDIR}) # NOTE: building and linking against an external CAF version is NOT supported! # This variable is FOR DEVELOPMENT ONLY. The only officially supported CAF # version is the bundled version! if (CAF_ROOT) message(STATUS "Using system CAF version ${CAF_VERSION}") find_package(CAF REQUIRED COMPONENTS test io core net) list(APPEND LINK_LIBS CAF::core CAF::io CAF::net) set(BROKER_USE_EXTERNAL_CAF ON) else () # Note: we use the object libraries here to avoid having dependencies on the # actual CAF targets that would force consumers of Broker to have CMake being # able to find a CAF installation. message(STATUS "Using bundled CAF") add_bundled_caf() set(BROKER_USE_EXTERNAL_CAF OFF) endif () if ( NOT BROKER_DISABLE_TESTS ) enable_testing() endif() # -- libroker ----------------------------------------------------------------- file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/VERSION" BROKER_VERSION LIMIT_COUNT 1) string(REPLACE "." " " _version_numbers ${BROKER_VERSION}) separate_arguments(_version_numbers) list(GET _version_numbers 0 BROKER_VERSION_MAJOR) list(GET _version_numbers 1 BROKER_VERSION_MINOR) # The SO number shall increase only if binary interface changes. set(BROKER_SOVERSION 4) set(ENABLE_SHARED true) if (ENABLE_STATIC_ONLY) set(ENABLE_STATIC true) set(ENABLE_SHARED false) endif () # Make sure there are no old header versions on disk. install( CODE "MESSAGE(STATUS \"Removing: ${CMAKE_INSTALL_PREFIX}/include/broker\")" CODE "file(REMOVE_RECURSE \"${CMAKE_INSTALL_PREFIX}/include/broker\")") # Install all headers except the files from broker/internal. install(DIRECTORY libbroker/broker DESTINATION include FILES_MATCHING PATTERN "*.hh" PATTERN "libbroker/broker/internal" EXCLUDE PATTERN "*.test.hh" EXCLUDE) if (NOT BROKER_EXTERNAL_SQLITE_TARGET) include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty) set_source_files_properties(3rdparty/sqlite3.c PROPERTIES COMPILE_FLAGS -DSQLITE_OMIT_LOAD_EXTENSION) list(APPEND OPTIONAL_SRC ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/sqlite3.c) else() list(APPEND LINK_LIBS ${BROKER_EXTERNAL_SQLITE_TARGET}) endif() add_subdirectory(libbroker) set(tidyCfgFile "${CMAKE_SOURCE_DIR}/.clang-tidy") if (BROKER_ENABLE_TIDY) # Create a preprocessor definition that depends on .clang-tidy content so # the compile command will change when .clang-tidy changes. This ensures # that a subsequent build re-runs clang-tidy on all sources even if they # do not otherwise need to be recompiled. file(SHA1 ${CMAKE_CURRENT_SOURCE_DIR}/.clang-tidy clang_tidy_sha1) set(BROKER_CLANG_TIDY_DEF "CLANG_TIDY_SHA1=${clang_tidy_sha1}") unset(clang_tidy_sha1) add_custom_target(clang_tidy_dummy DEPENDS ${tidyCfgFile}) set_target_properties(${BROKER_LIBRARY} PROPERTIES CXX_CLANG_TIDY "clang-tidy;--config-file=${tidyCfgFile}") target_compile_definitions(${BROKER_LIBRARY} PRIVATE ${BROKER_CLANG_TIDY_DEF}) configure_file(.clang-tidy .clang-tidy COPYONLY) endif () # -- Tools and benchmarks ----------------------------------------------------- if (NOT broker_is_subproject) add_subdirectory(broker-node) add_subdirectory(broker-pipe) add_subdirectory(broker-throughput) endif () # -- Bindings ----------------------------------------------------------------- if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/bindings/python/3rdparty/pybind11/CMakeLists.txt") message(WARNING "Skipping Python bindings: pybind11 submodule not available") set(DISABLE_PYTHON_BINDINGS true) endif () if (NOT DISABLE_PYTHON_BINDINGS) set(Python_ADDITIONAL_VERSIONS 3) find_package(Python 3.9 COMPONENTS Interpreter Development) if (NOT Python_FOUND) message(STATUS "Skipping Python bindings: Python interpreter not found") else () set (BROKER_PYTHON_BINDINGS true) set (BROKER_PYTHON_STAGING_DIR ${CMAKE_CURRENT_BINARY_DIR}/python) add_subdirectory(bindings/python) endif () endif () # -- Zeek --------------------------------------------------------------------- if (NOT "${ZEEK_EXECUTABLE}" STREQUAL "") set(ZEEK_FOUND true) set(ZEEK_FOUND_MSG "${ZEEK_EXECUTABLE}") else () set(ZEEK_FOUND false) find_file(ZEEK_PATH_DEV zeek-path-dev.sh PATHS ${CMAKE_CURRENT_BINARY_DIR}/../../../build NO_DEFAULT_PATH) if (EXISTS ${ZEEK_PATH_DEV}) set(ZEEK_FOUND true) set(ZEEK_FOUND_MSG "${ZEEK_PATH_DEV}") endif () endif () # -- Unit Tests --------------------------------------------------------------- if ( NOT BROKER_DISABLE_TESTS ) add_subdirectory(tests) endif () # -- Documentation ------------------------------------------------------------ if (NOT WIN32 AND NOT BROKER_DISABLE_DOCS) add_subdirectory(doc) endif () # -- CMake package install ---------------------------------------------------- export(EXPORT BrokerTargets FILE BrokerTargets.cmake) install( EXPORT BrokerTargets DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Broker") configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/libbroker/BrokerConfig.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/BrokerConfig.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Broker") write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/BrokerConfigVersion.cmake" VERSION ${BROKER_VERSION} COMPATIBILITY ExactVersion) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/BrokerConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/BrokerConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Broker") # -- Build Summary ------------------------------------------------------------ if (CMAKE_BUILD_TYPE) string(TOUPPER ${CMAKE_BUILD_TYPE} BuildType) endif () macro(display test desc summary) if ( ${test} ) set(${summary} ${desc}) else () set(${summary} no) endif() endmacro() display(ENABLE_SHARED yes shared_summary) display(ENABLE_STATIC yes static_summary) display(BROKER_PYTHON_BINDINGS yes python_summary) display(ZEEK_FOUND "${ZEEK_FOUND_MSG}" zeek_summary) set(summary "==================| Broker Config Summary |====================" "\nVersion: ${BROKER_VERSION}" "\nSO version: ${BROKER_SOVERSION}" "\n" "\nBuild Type: ${CMAKE_BUILD_TYPE}" "\nInstall prefix: ${CMAKE_INSTALL_PREFIX}" "\nLibrary prefix: ${CMAKE_INSTALL_LIBDIR}" "\nShared libs: ${shared_summary}" "\nStatic libs: ${static_summary}" "\n" "\nCC: ${CMAKE_C_COMPILER}" "\nCFLAGS: ${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${BuildType}}" "\nCXX: ${CMAKE_CXX_COMPILER}" "\nCXXFLAGS: ${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${BuildType}}" "\n" "\nCAF: ${CAF_VERSION}" "\nPython bindings: ${python_summary}" "\nZeek: ${zeek_summary}" "\n=================================================================") message("\n" ${summary} "\n") file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/config.summary ${summary}) include(UserChangedWarning)