448 lines
17 KiB
CMake
448 lines
17 KiB
CMake
cmake_minimum_required(VERSION 3.5...3.18 FATAL_ERROR)
|
|
project(CAF CXX)
|
|
|
|
# -- includes ------------------------------------------------------------------
|
|
|
|
include(CMakeDependentOption)
|
|
include(CMakePackageConfigHelpers)
|
|
include(CheckCXXSourceCompiles)
|
|
include(GNUInstallDirs)
|
|
include(GenerateExportHeader)
|
|
|
|
# -- override CMake defaults for internal cache entries ------------------------
|
|
|
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON
|
|
CACHE INTERNAL "Write JSON compile commands database")
|
|
|
|
# -- general options -----------------------------------------------------------
|
|
|
|
option(BUILD_SHARED_LIBS "Build shared library targets" ON)
|
|
option(THREADS_PREFER_PTHREAD_FLAG "Prefer -pthread flag if available " ON)
|
|
|
|
# -- CAF options that are off by default ---------------------------------------
|
|
|
|
option(CAF_ENABLE_CURL_EXAMPLES "Build examples with libcurl" OFF)
|
|
option(CAF_ENABLE_PROTOBUF_EXAMPLES "Build examples with Google Protobuf" OFF)
|
|
option(CAF_ENABLE_QT6_EXAMPLES "Build examples with the Qt6 framework" OFF)
|
|
option(CAF_ENABLE_RUNTIME_CHECKS "Build CAF with extra runtime assertions" OFF)
|
|
option(CAF_ENABLE_ACTOR_PROFILER "Enable experimental profiler API" OFF)
|
|
|
|
# -- CAF options that are on by default ----------------------------------------
|
|
|
|
option(CAF_ENABLE_EXAMPLES "Build small programs showcasing CAF features" ON)
|
|
option(CAF_ENABLE_IO_MODULE "Build legacy networking I/O module" ON)
|
|
option(CAF_ENABLE_NET_MODULE "Build networking module" ON)
|
|
option(CAF_ENABLE_TESTING "Build unit test suites" ON)
|
|
option(CAF_ENABLE_TOOLS "Build utility programs such as caf-run" ON)
|
|
option(CAF_ENABLE_EXCEPTIONS "Build CAF with support for exceptions" ON)
|
|
|
|
# -- CAF options that depend on others -----------------------------------------
|
|
|
|
cmake_dependent_option(CAF_ENABLE_OPENSSL_MODULE "Build OpenSSL module" ON
|
|
"CAF_ENABLE_IO_MODULE" OFF)
|
|
|
|
# -- CAF options with non-boolean values ---------------------------------------
|
|
|
|
set(CAF_LOG_LEVEL "QUIET" CACHE STRING "Set log verbosity of CAF components")
|
|
set(CAF_SANITIZERS "" CACHE STRING
|
|
"Comma separated sanitizers, e.g., 'address,undefined'")
|
|
set(CAF_BUILD_INFO_FILE_PATH "" CACHE FILEPATH
|
|
"Optional path for writing CMake and compiler version information")
|
|
|
|
# -- macOS-specific options ----------------------------------------------------
|
|
|
|
if(APPLE)
|
|
set(CMAKE_MACOSX_RPATH ON CACHE INTERNAL "Use rpaths on macOS and iOS")
|
|
endif()
|
|
|
|
# -- project-specific CMake settings -------------------------------------------
|
|
|
|
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
|
|
|
|
# -- sanity checking -----------------------------------------------------------
|
|
|
|
if(CAF_ENABLE_OPENSSL_MODULE AND NOT CAF_ENABLE_IO_MODULE)
|
|
message(FATAL_ERROR "Invalid options: cannot build OpenSSL without I/O")
|
|
endif()
|
|
|
|
set(CAF_VALID_LOG_LEVELS QUIET ERROR WARNING INFO DEBUG TRACE)
|
|
if(NOT CAF_LOG_LEVEL IN_LIST CAF_VALID_LOG_LEVELS)
|
|
message(FATAL_ERROR "Invalid log level: \"${CAF_LOG_LEVEL}\"")
|
|
endif()
|
|
|
|
if(MSVC AND CAF_SANITIZERS)
|
|
message(FATAL_ERROR "Sanitizer builds are currently not supported on MSVC")
|
|
endif()
|
|
|
|
# -- base target setup ---------------------------------------------------------
|
|
|
|
# This target propagates compiler flags, extra dependencies, etc. All other CAF
|
|
# targets pull this target in as a PRIVATE dependency. Users that embed CAF into
|
|
# their own CMake scaffold (e.g., via FetchContent) may pass this target in with
|
|
# some properties predefined in order to force compiler flags or dependencies.
|
|
if(NOT TARGET caf_internal)
|
|
add_library(caf_internal INTERFACE)
|
|
endif()
|
|
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
|
target_compile_options(caf_internal INTERFACE -Wall -Wextra -pedantic
|
|
-ftemplate-depth=512 -ftemplate-backtrace-limit=0)
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
|
target_compile_options(caf_internal INTERFACE -Wdocumentation)
|
|
else()
|
|
target_compile_options(caf_internal INTERFACE
|
|
-Wno-missing-field-initializers)
|
|
endif()
|
|
endif()
|
|
|
|
# -- unit testing setup / caf_add_test_suites function ------------------------
|
|
|
|
if(CAF_ENABLE_TESTING)
|
|
enable_testing()
|
|
function(caf_add_test_suites target)
|
|
foreach(suiteName ${ARGN})
|
|
string(REPLACE "." "/" suitePath ${suiteName})
|
|
target_sources(${target} PRIVATE
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/test/${suitePath}.cpp")
|
|
add_test(NAME ${suiteName}
|
|
COMMAND ${target} -r300 -n -v5 -s "^${suiteName}$")
|
|
endforeach()
|
|
endfunction()
|
|
endif()
|
|
|
|
# -- make sure we have at least C++17 available --------------------------------
|
|
|
|
# TODO: simply set CXX_STANDARD when switching to CMake ≥ 3.9.6
|
|
if(NOT CMAKE_CROSSCOMPILING)
|
|
try_compile(caf_has_cxx_17
|
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/check-compiler-features.cpp")
|
|
if(NOT caf_has_cxx_17)
|
|
if(MSVC)
|
|
set(cxx_flag "/std:c++17")
|
|
else()
|
|
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang"
|
|
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
|
|
set(cxx_flag "-std=c++1z")
|
|
else()
|
|
set(cxx_flag "-std=c++17")
|
|
endif()
|
|
endif()
|
|
# Re-run compiler check.
|
|
try_compile(caf_has_cxx_17
|
|
"${CMAKE_CURRENT_BINARY_DIR}"
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/check-compiler-features.cpp"
|
|
COMPILE_DEFINITIONS "${cxx_flag}"
|
|
OUTPUT_VARIABLE cxx_check_output)
|
|
if(NOT caf_has_cxx_17)
|
|
message(FATAL_ERROR "\nFatal error: unable activate C++17 mode!\
|
|
\nPlease see README.md for supported compilers.\
|
|
\n\ntry_compile output:\n${cxx_check_output}")
|
|
endif()
|
|
target_compile_options(caf_internal INTERFACE "${cxx_flag}")
|
|
endif()
|
|
endif()
|
|
|
|
# -- export internal target (may be useful for re-using compiler flags) --------
|
|
|
|
set_target_properties(caf_internal PROPERTIES EXPORT_NAME internal)
|
|
|
|
add_library(CAF::internal ALIAS caf_internal)
|
|
|
|
install(TARGETS caf_internal EXPORT CAFTargets)
|
|
|
|
# -- get CAF version -----------------------------------------------------------
|
|
|
|
# Get line containing the version from config.hpp and extract version number.
|
|
file(READ "libcaf_core/caf/config.hpp" CAF_CONFIG_HPP)
|
|
string(REGEX MATCH "#define CAF_VERSION [0-9]+" CAF_VERSION_LINE "${CAF_CONFIG_HPP}")
|
|
string(REGEX MATCH "[0-9]+" CAF_VERSION_INT "${CAF_VERSION_LINE}")
|
|
# Calculate major, minor, and patch version.
|
|
math(EXPR CAF_VERSION_MAJOR "${CAF_VERSION_INT} / 10000")
|
|
math(EXPR CAF_VERSION_MINOR "( ${CAF_VERSION_INT} / 100) % 100")
|
|
math(EXPR CAF_VERSION_PATCH "${CAF_VERSION_INT} % 100")
|
|
# Create full version string.
|
|
set(CAF_VERSION "${CAF_VERSION_MAJOR}.${CAF_VERSION_MINOR}.${CAF_VERSION_PATCH}"
|
|
CACHE INTERNAL "The full CAF version string")
|
|
# Set the library version for our shared library targets.
|
|
if(CMAKE_HOST_SYSTEM_NAME MATCHES "OpenBSD")
|
|
set(CAF_LIB_VERSION "${CAF_VERSION_MAJOR}.${CAF_VERSION_MINOR}"
|
|
CACHE INTERNAL "The version string used for shared library objects")
|
|
else()
|
|
set(CAF_LIB_VERSION "${CAF_VERSION}"
|
|
CACHE INTERNAL "The version string used for shared library objects")
|
|
endif()
|
|
|
|
# -- install testing DSL headers -----------------------------------------------
|
|
|
|
add_library(libcaf_test INTERFACE)
|
|
|
|
set_target_properties(libcaf_test PROPERTIES EXPORT_NAME test)
|
|
|
|
target_include_directories(libcaf_test INTERFACE
|
|
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/libcaf_test>)
|
|
|
|
add_library(CAF::test ALIAS libcaf_test)
|
|
|
|
install(DIRECTORY libcaf_test/caf/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/caf
|
|
FILES_MATCHING PATTERN "*.hpp")
|
|
|
|
install(TARGETS libcaf_test
|
|
EXPORT CAFTargets
|
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT test
|
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT test
|
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT test)
|
|
|
|
# -- add uninstall target if it does not exist yet -----------------------------
|
|
|
|
if(NOT TARGET uninstall)
|
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake/uninstall.cmake.in"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake"
|
|
IMMEDIATE @ONLY)
|
|
add_custom_target(
|
|
uninstall
|
|
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/uninstall.cmake)
|
|
endif()
|
|
|
|
# -- utility functions ---------------------------------------------------------
|
|
|
|
# generates the implementation file for the enum that contains to_string,
|
|
# from_string and from_integer
|
|
function(caf_add_enum_type target enum_name)
|
|
string(REPLACE "." "/" path "${enum_name}")
|
|
set(hpp_file "${CMAKE_CURRENT_SOURCE_DIR}/caf/${path}.hpp")
|
|
set(cpp_file "${CMAKE_CURRENT_BINARY_DIR}/src/${path}_strings.cpp")
|
|
set(gen_file "${PROJECT_SOURCE_DIR}/cmake/caf-generate-enum-strings.cmake")
|
|
add_custom_command(OUTPUT "${cpp_file}"
|
|
COMMAND ${CMAKE_COMMAND}
|
|
"-DINPUT_FILE=${hpp_file}"
|
|
"-DOUTPUT_FILE=${cpp_file}"
|
|
-P "${gen_file}"
|
|
DEPENDS "${hpp_file}" "${gen_file}")
|
|
target_sources(${target} PRIVATE "${cpp_file}")
|
|
endfunction()
|
|
|
|
function(caf_export_and_install_lib component)
|
|
add_library(CAF::${component} ALIAS libcaf_${component})
|
|
string(TOUPPER "CAF_${component}_EXPORT" export_macro_name)
|
|
target_include_directories(libcaf_${component} INTERFACE
|
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
|
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
|
|
$<INSTALL_INTERFACE:include>)
|
|
generate_export_header(
|
|
libcaf_${component}
|
|
EXPORT_MACRO_NAME ${export_macro_name}
|
|
EXPORT_FILE_NAME "caf/detail/${component}_export.hpp")
|
|
set_target_properties(libcaf_${component} PROPERTIES
|
|
EXPORT_NAME ${component}
|
|
SOVERSION ${CAF_VERSION}
|
|
VERSION ${CAF_LIB_VERSION}
|
|
OUTPUT_NAME caf_${component})
|
|
install(TARGETS libcaf_${component}
|
|
EXPORT CAFTargets
|
|
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${component}
|
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT ${component}
|
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT ${component})
|
|
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/caf"
|
|
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
|
|
COMPONENT ${component}
|
|
FILES_MATCHING PATTERN "*.hpp")
|
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/caf/detail/${component}_export.hpp"
|
|
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/caf/detail/")
|
|
endfunction()
|
|
|
|
# TODO: remove when switching to CMake > 3.12
|
|
function(caf_target_link_libraries target)
|
|
if(CMAKE_VERSION VERSION_LESS 3.12)
|
|
get_target_property(target_type ${target} TYPE)
|
|
if (NOT target_type STREQUAL OBJECT_LIBRARY)
|
|
target_link_libraries(${target} ${ARGN})
|
|
else()
|
|
cmake_parse_arguments(CAF_TARGET_LINK_LIBRARIES "" ""
|
|
"PUBLIC;PRIVATE;INTERFACE" ${ARGN})
|
|
# If we can't link against it, at least make sure to pull in include paths
|
|
# and compiler options.
|
|
foreach(arg IN LISTS CAF_TARGET_LINK_LIBRARIES_PUBLIC
|
|
CAF_TARGET_LINK_LIBRARIES_PRIVATE)
|
|
if (TARGET ${arg})
|
|
target_include_directories(
|
|
${target} PRIVATE
|
|
$<TARGET_PROPERTY:${arg},INTERFACE_INCLUDE_DIRECTORIES>)
|
|
target_compile_options(
|
|
${target} PRIVATE
|
|
$<TARGET_PROPERTY:${arg},INTERFACE_COMPILE_OPTIONS>)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
else()
|
|
target_link_libraries(${target} ${ARGN})
|
|
endif()
|
|
endfunction()
|
|
|
|
# -- convenience function for automating our component setup -------------------
|
|
|
|
# Usage:
|
|
# caf_add_component(
|
|
# foo
|
|
# DEPENDENCIES
|
|
# INTERFACE
|
|
# ...
|
|
# PUBLIC
|
|
# ...
|
|
# PRIVATE
|
|
# ...
|
|
# HEADERS
|
|
# ...
|
|
# SOURCES
|
|
# ...
|
|
# TEST_SOURCES
|
|
# ...
|
|
# TEST_SUITES
|
|
# ...
|
|
# )
|
|
function(caf_add_component name)
|
|
set(varargs DEPENDENCIES HEADERS SOURCES TEST_SOURCES TEST_SUITES ENUM_TYPES)
|
|
cmake_parse_arguments(CAF_ADD_COMPONENT "" "" "${varargs}" ${ARGN})
|
|
if(NOT CAF_ADD_COMPONENT_HEADERS)
|
|
message(FATAL_ERROR "Cannot add CAF component without at least one header.")
|
|
endif()
|
|
if(NOT CAF_ADD_COMPONENT_SOURCES)
|
|
message(FATAL_ERROR "Cannot add CAF component without at least one source.")
|
|
endif()
|
|
foreach(param DEPENDENCIES HEADERS SOURCES)
|
|
if(NOT CAF_ADD_COMPONENT_${param})
|
|
message(FATAL_ERROR "caf_add_component(): missing parameter ${param}")
|
|
endif()
|
|
endforeach()
|
|
set(pub_lib_target "libcaf_${name}")
|
|
set(obj_lib_target "libcaf_${name}_obj")
|
|
set(targets ${pub_lib_target} ${obj_lib_target})
|
|
add_library(${obj_lib_target} OBJECT
|
|
${CAF_ADD_COMPONENT_HEADERS} ${CAF_ADD_COMPONENT_SOURCES})
|
|
set_property(TARGET ${obj_lib_target} PROPERTY POSITION_INDEPENDENT_CODE ON)
|
|
caf_target_link_libraries(${obj_lib_target}
|
|
${CAF_ADD_COMPONENT_DEPENDENCIES})
|
|
add_library(${pub_lib_target}
|
|
"${PROJECT_SOURCE_DIR}/cmake/dummy.cpp"
|
|
$<TARGET_OBJECTS:${obj_lib_target}>)
|
|
if(CAF_ENABLE_TESTING AND CAF_ADD_COMPONENT_TEST_SOURCES)
|
|
set(tst_bin_target "caf-${name}-test")
|
|
list(APPEND targets ${tst_bin_target})
|
|
add_executable(${tst_bin_target}
|
|
${CAF_ADD_COMPONENT_TEST_SOURCES}
|
|
$<TARGET_OBJECTS:${obj_lib_target}>)
|
|
target_link_libraries(${tst_bin_target} PRIVATE CAF::test
|
|
${CAF_ADD_COMPONENT_DEPENDENCIES})
|
|
target_include_directories(${tst_bin_target} PRIVATE
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/test")
|
|
if(CAF_ADD_COMPONENT_TEST_SUITES)
|
|
caf_add_test_suites(${tst_bin_target} ${CAF_ADD_COMPONENT_TEST_SUITES})
|
|
endif()
|
|
endif()
|
|
target_link_libraries(${pub_lib_target} ${CAF_ADD_COMPONENT_DEPENDENCIES})
|
|
if(CAF_ADD_COMPONENT_ENUM_TYPES)
|
|
foreach(enum_name ${CAF_ADD_COMPONENT_ENUM_TYPES})
|
|
if(obj_lib_target)
|
|
caf_add_enum_type(${obj_lib_target} ${enum_name})
|
|
else()
|
|
caf_add_enum_type(${pub_lib_target} ${enum_name})
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
foreach(target ${targets})
|
|
target_compile_definitions(${target} PRIVATE "libcaf_${name}_EXPORTS")
|
|
target_include_directories(${target} PRIVATE
|
|
"${CMAKE_CURRENT_SOURCE_DIR}"
|
|
"${CMAKE_CURRENT_BINARY_DIR}")
|
|
if(BUILD_SHARED_LIBS)
|
|
set_target_properties(${target} PROPERTIES
|
|
CXX_VISIBILITY_PRESET hidden
|
|
VISIBILITY_INLINES_HIDDEN ON)
|
|
endif()
|
|
endforeach()
|
|
caf_export_and_install_lib(${name})
|
|
endfunction()
|
|
|
|
# -- build all components the user asked for -----------------------------------
|
|
|
|
add_subdirectory(libcaf_core)
|
|
|
|
if(CAF_ENABLE_IO_MODULE)
|
|
add_subdirectory(libcaf_io)
|
|
endif()
|
|
|
|
if(CAF_ENABLE_NET_MODULE)
|
|
add_subdirectory(libcaf_net)
|
|
endif()
|
|
|
|
if(CAF_ENABLE_OPENSSL_MODULE)
|
|
add_subdirectory(libcaf_openssl)
|
|
endif()
|
|
|
|
if(CAF_ENABLE_EXAMPLES)
|
|
add_subdirectory(examples)
|
|
endif()
|
|
|
|
if(CAF_ENABLE_TOOLS)
|
|
add_subdirectory(tools)
|
|
endif()
|
|
|
|
# -- add top-level compiler and linker flags that propagate to clients ---------
|
|
|
|
# Disable warnings regarding C++ classes at ABI boundaries on MSVC.
|
|
if(BUILD_SHARED_LIBS AND MSVC)
|
|
target_compile_options(libcaf_core INTERFACE /wd4275 /wd4251)
|
|
endif()
|
|
|
|
# Propgatate sanitizer flags to downstream targets.
|
|
if(CAF_SANITIZERS)
|
|
foreach(target caf_internal libcaf_core)
|
|
target_compile_options(${target} INTERFACE
|
|
-fsanitize=${CAF_SANITIZERS}
|
|
-fno-omit-frame-pointer)
|
|
if(CMAKE_VERSION VERSION_LESS 3.13)
|
|
target_link_libraries(${target} INTERFACE
|
|
-fsanitize=${CAF_SANITIZERS}
|
|
-fno-omit-frame-pointer)
|
|
else()
|
|
target_link_options(${target} INTERFACE
|
|
-fsanitize=${CAF_SANITIZERS}
|
|
-fno-omit-frame-pointer)
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
|
|
# -- generate and install .cmake files -----------------------------------------
|
|
|
|
export(EXPORT CAFTargets FILE CAFTargets.cmake NAMESPACE CAF::)
|
|
|
|
install(EXPORT CAFTargets
|
|
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CAF"
|
|
NAMESPACE CAF::)
|
|
|
|
write_basic_package_version_file(
|
|
"${CMAKE_CURRENT_BINARY_DIR}/CAFConfigVersion.cmake"
|
|
VERSION ${CAF_VERSION}
|
|
COMPATIBILITY ExactVersion)
|
|
|
|
configure_package_config_file(
|
|
"${CMAKE_CURRENT_SOURCE_DIR}/cmake/CAFConfig.cmake.in"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/CAFConfig.cmake"
|
|
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CAF")
|
|
|
|
install(
|
|
FILES
|
|
"${CMAKE_CURRENT_BINARY_DIR}/CAFConfig.cmake"
|
|
"${CMAKE_CURRENT_BINARY_DIR}/CAFConfigVersion.cmake"
|
|
DESTINATION
|
|
"${CMAKE_INSTALL_LIBDIR}/cmake/CAF")
|
|
|
|
# -- extra file output (primarily for CAF CI) ----------------------------------
|
|
|
|
if(CAF_BUILD_INFO_FILE_PATH)
|
|
configure_file("${PROJECT_SOURCE_DIR}/cmake/caf-build-info.txt.in"
|
|
"${CAF_BUILD_INFO_FILE_PATH}"
|
|
@ONLY)
|
|
endif()
|