1# Copyright 2019 The Marl Authors. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# https://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15cmake_minimum_required(VERSION 3.0) 16 17include(cmake/parse_version.cmake) 18parse_version("${CMAKE_CURRENT_SOURCE_DIR}/CHANGES.md" MARL) 19 20set(CMAKE_CXX_STANDARD 11) 21 22project(Marl 23 VERSION "${MARL_VERSION_MAJOR}.${MARL_VERSION_MINOR}.${MARL_VERSION_PATCH}" 24 LANGUAGES C CXX ASM 25) 26 27include(CheckCXXSourceCompiles) 28 29# MARL_IS_SUBPROJECT is 1 if added via add_subdirectory() from another project. 30get_directory_property(MARL_IS_SUBPROJECT PARENT_DIRECTORY) 31if(MARL_IS_SUBPROJECT) 32 set(MARL_IS_SUBPROJECT 1) 33endif() 34 35########################################################### 36# Options 37########################################################### 38function(option_if_not_defined name description default) 39 if(NOT DEFINED ${name}) 40 option(${name} ${description} ${default}) 41 endif() 42endfunction() 43 44option_if_not_defined(MARL_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) 45option_if_not_defined(MARL_BUILD_EXAMPLES "Build example applications" OFF) 46option_if_not_defined(MARL_BUILD_TESTS "Build tests" OFF) 47option_if_not_defined(MARL_BUILD_BENCHMARKS "Build benchmarks" OFF) 48option_if_not_defined(MARL_BUILD_SHARED "Build marl as a shared / dynamic library (default static)" OFF) 49option_if_not_defined(MARL_ASAN "Build marl with address sanitizer" OFF) 50option_if_not_defined(MARL_MSAN "Build marl with memory sanitizer" OFF) 51option_if_not_defined(MARL_TSAN "Build marl with thread sanitizer" OFF) 52option_if_not_defined(MARL_INSTALL "Create marl install target" OFF) 53option_if_not_defined(MARL_FULL_BENCHMARK "Run benchmarks for [0 .. numLogicalCPUs] with no stepping" OFF) 54option_if_not_defined(MARL_FIBERS_USE_UCONTEXT "Use ucontext instead of assembly for fibers (ignored for platforms that do not support ucontext)" OFF) 55option_if_not_defined(MARL_DEBUG_ENABLED "Enable debug checks even in release builds" OFF) 56 57########################################################### 58# Directories 59########################################################### 60function(set_if_not_defined name value) 61 if(NOT DEFINED ${name}) 62 set(${name} ${value} PARENT_SCOPE) 63 endif() 64endfunction() 65 66set(MARL_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) 67set(MARL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) 68set_if_not_defined(MARL_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party) 69set_if_not_defined(MARL_GOOGLETEST_DIR ${MARL_THIRD_PARTY_DIR}/googletest) 70set_if_not_defined(MARL_BENCHMARK_DIR ${MARL_THIRD_PARTY_DIR}/benchmark) 71 72########################################################### 73# Submodules 74########################################################### 75if(MARL_BUILD_TESTS) 76 if(NOT EXISTS ${MARL_GOOGLETEST_DIR}/.git) 77 message(WARNING "third_party/googletest submodule missing.") 78 message(WARNING "Run: `git submodule update --init` to build tests.") 79 set(MARL_BUILD_TESTS OFF) 80 endif() 81endif(MARL_BUILD_TESTS) 82 83if(MARL_BUILD_BENCHMARKS) 84 if(NOT EXISTS ${MARL_BENCHMARK_DIR}/.git) 85 message(WARNING "third_party/benchmark submodule missing.") 86 message(WARNING "Run: `git submodule update --init` to build benchmarks.") 87 set(MARL_BUILD_BENCHMARKS OFF) 88 endif() 89endif(MARL_BUILD_BENCHMARKS) 90 91if(MARL_BUILD_BENCHMARKS) 92 set(BENCHMARK_ENABLE_TESTING FALSE CACHE BOOL FALSE FORCE) 93 add_subdirectory(${MARL_BENCHMARK_DIR}) 94endif(MARL_BUILD_BENCHMARKS) 95 96########################################################### 97# Compiler feature tests 98########################################################### 99# Check that the Clang Thread Safety Analysis' try_acquire_capability behaves 100# correctly. This is broken on some earlier versions of clang. 101# See: https://bugs.llvm.org/show_bug.cgi?id=32954 102set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) 103set(CMAKE_REQUIRED_FLAGS "-Wthread-safety -Werror") 104check_cxx_source_compiles( 105 "int main() { 106 struct __attribute__((capability(\"mutex\"))) Mutex { 107 void Unlock() __attribute__((release_capability)) {}; 108 bool TryLock() __attribute__((try_acquire_capability(true))) { return true; }; 109 }; 110 Mutex m; 111 if (m.TryLock()) { 112 m.Unlock(); // Should not warn. 113 } 114 return 0; 115 }" 116 MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED) 117set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) 118 119# Check whether ucontext is supported. 120set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) 121set(CMAKE_REQUIRED_FLAGS "-Werror") 122check_cxx_source_compiles( 123 "#include <ucontext.h> 124 int main() { 125 ucontext_t ctx; 126 getcontext(&ctx); 127 makecontext(&ctx, nullptr, 2, 1, 2); 128 swapcontext(&ctx, &ctx); 129 return 0; 130 }" 131 MARL_UCONTEXT_SUPPORTED) 132set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) 133if (MARL_FIBERS_USE_UCONTEXT AND NOT MARL_UCONTEXT_SUPPORTED) 134 # Disable MARL_FIBERS_USE_UCONTEXT and warn if MARL_UCONTEXT_SUPPORTED is 0. 135 message(WARNING "MARL_FIBERS_USE_UCONTEXT is enabled, but ucontext is not supported by the target. Disabling") 136 set(MARL_FIBERS_USE_UCONTEXT 0) 137endif() 138 139if(MARL_IS_SUBPROJECT) 140 # Export supported flags as this may be useful to parent projects 141 set(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED PARENT_SCOPE ${MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED}) 142 set(MARL_UCONTEXT_SUPPORTED PARENT_SCOPE ${MARL_UCONTEXT_SUPPORTED}) 143endif() 144 145########################################################### 146# File lists 147########################################################### 148set(MARL_LIST 149 ${MARL_SRC_DIR}/debug.cpp 150 ${MARL_SRC_DIR}/memory.cpp 151 ${MARL_SRC_DIR}/scheduler.cpp 152 ${MARL_SRC_DIR}/thread.cpp 153 ${MARL_SRC_DIR}/trace.cpp 154) 155if(NOT MSVC) 156 list(APPEND MARL_LIST 157 ${MARL_SRC_DIR}/osfiber_aarch64.c 158 ${MARL_SRC_DIR}/osfiber_arm.c 159 ${MARL_SRC_DIR}/osfiber_asm_aarch64.S 160 ${MARL_SRC_DIR}/osfiber_asm_arm.S 161 ${MARL_SRC_DIR}/osfiber_asm_mips64.S 162 ${MARL_SRC_DIR}/osfiber_asm_ppc64.S 163 ${MARL_SRC_DIR}/osfiber_asm_x64.S 164 ${MARL_SRC_DIR}/osfiber_asm_x86.S 165 ${MARL_SRC_DIR}/osfiber_mips64.c 166 ${MARL_SRC_DIR}/osfiber_ppc64.c 167 ${MARL_SRC_DIR}/osfiber_x64.c 168 ${MARL_SRC_DIR}/osfiber_x86.c 169 ) 170 # CMAKE_OSX_ARCHITECTURES settings aren't propagated to assembly files when 171 # building for Apple platforms (https://gitlab.kitware.com/cmake/cmake/-/issues/20771), 172 # we treat assembly files as C files to work around this bug. 173 set_source_files_properties( 174 ${MARL_SRC_DIR}/osfiber_asm_aarch64.S 175 ${MARL_SRC_DIR}/osfiber_asm_arm.S 176 ${MARL_SRC_DIR}/osfiber_asm_mips64.S 177 ${MARL_SRC_DIR}/osfiber_asm_ppc64.S 178 ${MARL_SRC_DIR}/osfiber_asm_x64.S 179 ${MARL_SRC_DIR}/osfiber_asm_x86.S 180 PROPERTIES LANGUAGE C 181 ) 182endif(NOT MSVC) 183 184########################################################### 185# OS libraries 186########################################################### 187find_package(Threads REQUIRED) 188 189########################################################### 190# Functions 191########################################################### 192function(marl_set_target_options target) 193 if(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED) 194 target_compile_options(${target} PRIVATE "-Wthread-safety") 195 endif() 196 197 # Enable all warnings 198 if(MSVC) 199 target_compile_options(${target} PRIVATE "-W4") 200 else() 201 target_compile_options(${target} PRIVATE "-Wall") 202 endif() 203 204 # Disable specific, pedantic warnings 205 if(MSVC) 206 target_compile_options(${target} PRIVATE 207 "-D_CRT_SECURE_NO_WARNINGS" 208 "/wd4127" # conditional expression is constant 209 "/wd4324" # structure was padded due to alignment specifier 210 ) 211 endif() 212 213 # Treat all warnings as errors 214 if(MARL_WARNINGS_AS_ERRORS) 215 if(MSVC) 216 target_compile_options(${target} PRIVATE "/WX") 217 else() 218 target_compile_options(${target} PRIVATE "-Werror") 219 endif() 220 endif(MARL_WARNINGS_AS_ERRORS) 221 222 if(MARL_ASAN) 223 target_compile_options(${target} PUBLIC "-fsanitize=address") 224 target_link_libraries(${target} PUBLIC "-fsanitize=address") 225 elseif(MARL_MSAN) 226 target_compile_options(${target} PUBLIC "-fsanitize=memory") 227 target_link_libraries(${target} PUBLIC "-fsanitize=memory") 228 elseif(MARL_TSAN) 229 target_compile_options(${target} PUBLIC "-fsanitize=thread") 230 target_link_libraries(${target} PUBLIC "-fsanitize=thread") 231 endif() 232 233 if(MARL_FIBERS_USE_UCONTEXT) 234 target_compile_definitions(${target} PRIVATE "MARL_FIBERS_USE_UCONTEXT=1") 235 endif() 236 237 if(MARL_DEBUG_ENABLED) 238 target_compile_definitions(${target} PRIVATE "MARL_DEBUG_ENABLED=1") 239 endif() 240 241 target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${MARL_INCLUDE_DIR}>) 242endfunction(marl_set_target_options) 243 244########################################################### 245# Targets 246########################################################### 247 248# marl 249if(MARL_BUILD_SHARED OR BUILD_SHARED_LIBS) 250 add_library(marl SHARED ${MARL_LIST}) 251 if(MSVC) 252 target_compile_definitions(marl 253 PRIVATE "MARL_BUILDING_DLL=1" 254 PUBLIC "MARL_DLL=1" 255 ) 256 endif() 257else() 258 add_library(marl ${MARL_LIST}) 259endif() 260 261if(NOT MSVC) 262 # Public API symbols are made visible with the MARL_EXPORT annotation. 263 target_compile_options(marl PRIVATE "-fvisibility=hidden") 264endif() 265 266set_target_properties(marl PROPERTIES 267 POSITION_INDEPENDENT_CODE 1 268 VERSION ${MARL_VERSION} 269 SOVERSION "${MARL_VERSION_MAJOR}" 270) 271 272marl_set_target_options(marl) 273 274target_link_libraries(marl PUBLIC Threads::Threads) 275 276# install 277if(MARL_INSTALL) 278 include(CMakePackageConfigHelpers) 279 include(GNUInstallDirs) 280 281 configure_package_config_file( 282 ${CMAKE_CURRENT_SOURCE_DIR}/cmake/marl-config.cmake.in 283 ${CMAKE_CURRENT_BINARY_DIR}/marl-config.cmake 284 INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/marl 285 ) 286 287 install(DIRECTORY ${MARL_INCLUDE_DIR}/marl 288 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 289 USE_SOURCE_PERMISSIONS 290 ) 291 292 install(TARGETS marl 293 EXPORT marl-targets 294 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 295 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 296 RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 297 INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 298 ) 299 300 install(EXPORT marl-targets 301 FILE marl-targets.cmake 302 NAMESPACE marl:: 303 DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/marl 304 ) 305 306 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/marl-config.cmake 307 DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/marl 308 ) 309endif(MARL_INSTALL) 310 311# tests 312if(MARL_BUILD_TESTS) 313 set(MARL_TEST_LIST 314 ${MARL_SRC_DIR}/blockingcall_test.cpp 315 ${MARL_SRC_DIR}/conditionvariable_test.cpp 316 ${MARL_SRC_DIR}/containers_test.cpp 317 ${MARL_SRC_DIR}/dag_test.cpp 318 ${MARL_SRC_DIR}/defer_test.cpp 319 ${MARL_SRC_DIR}/event_test.cpp 320 ${MARL_SRC_DIR}/marl_test.cpp 321 ${MARL_SRC_DIR}/marl_test.h 322 ${MARL_SRC_DIR}/memory_test.cpp 323 ${MARL_SRC_DIR}/osfiber_test.cpp 324 ${MARL_SRC_DIR}/parallelize_test.cpp 325 ${MARL_SRC_DIR}/pool_test.cpp 326 ${MARL_SRC_DIR}/scheduler_test.cpp 327 ${MARL_SRC_DIR}/thread_test.cpp 328 ${MARL_SRC_DIR}/ticket_test.cpp 329 ${MARL_SRC_DIR}/waitgroup_test.cpp 330 ${MARL_GOOGLETEST_DIR}/googletest/src/gtest-all.cc 331 ${MARL_GOOGLETEST_DIR}/googlemock/src/gmock-all.cc 332 ) 333 334 set(MARL_TEST_INCLUDE_DIR 335 ${MARL_GOOGLETEST_DIR}/googletest/include/ 336 ${MARL_GOOGLETEST_DIR}/googlemock/include/ 337 ${MARL_GOOGLETEST_DIR}/googletest/ 338 ${MARL_GOOGLETEST_DIR}/googlemock/ 339 ) 340 341 add_executable(marl-unittests ${MARL_TEST_LIST}) 342 343 set_target_properties(marl-unittests PROPERTIES 344 INCLUDE_DIRECTORIES "${MARL_TEST_INCLUDE_DIR}" 345 FOLDER "Tests" 346 ) 347 348 marl_set_target_options(marl-unittests) 349 350 target_link_libraries(marl-unittests PRIVATE marl) 351endif(MARL_BUILD_TESTS) 352 353# benchmarks 354if(MARL_BUILD_BENCHMARKS) 355 set(MARL_BENCHMARK_LIST 356 ${MARL_SRC_DIR}/blockingcall_bench.cpp 357 ${MARL_SRC_DIR}/defer_bench.cpp 358 ${MARL_SRC_DIR}/event_bench.cpp 359 ${MARL_SRC_DIR}/marl_bench.cpp 360 ${MARL_SRC_DIR}/non_marl_bench.cpp 361 ${MARL_SRC_DIR}/scheduler_bench.cpp 362 ${MARL_SRC_DIR}/ticket_bench.cpp 363 ${MARL_SRC_DIR}/waitgroup_bench.cpp 364 ) 365 366 add_executable(marl-benchmarks ${MARL_BENCHMARK_LIST}) 367 set_target_properties(${target} PROPERTIES FOLDER "Benchmarks") 368 369 marl_set_target_options(marl-benchmarks) 370 371 target_compile_definitions(marl-benchmarks PRIVATE 372 "MARL_FULL_BENCHMARK=${MARL_FULL_BENCHMARK}" 373 ) 374 375 target_link_libraries(marl-benchmarks PRIVATE benchmark::benchmark marl) 376endif(MARL_BUILD_BENCHMARKS) 377 378# examples 379if(MARL_BUILD_EXAMPLES) 380 function(build_example target) 381 add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp") 382 set_target_properties(${target} PROPERTIES FOLDER "Examples") 383 marl_set_target_options(${target}) 384 target_link_libraries(${target} PRIVATE marl) 385 endfunction(build_example) 386 387 build_example(fractal) 388 build_example(hello_task) 389 build_example(primes) 390 build_example(tasks_in_tasks) 391endif(MARL_BUILD_EXAMPLES) 392