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