| # Copyright 2019 The Marl Authors. | 
 | # | 
 | # Licensed under the Apache License, Version 2.0 (the "License"); | 
 | # you may not use this file except in compliance with the License. | 
 | # You may obtain a copy of the License at | 
 | # | 
 | #     https://www.apache.org/licenses/LICENSE-2.0 | 
 | # | 
 | # Unless required by applicable law or agreed to in writing, software | 
 | # distributed under the License is distributed on an "AS IS" BASIS, | 
 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | # See the License for the specific language governing permissions and | 
 | # limitations under the License. | 
 |  | 
 | cmake_minimum_required(VERSION 3.0) | 
 |  | 
 | include(cmake/parse_version.cmake) | 
 | parse_version("${CMAKE_CURRENT_SOURCE_DIR}/CHANGES.md" MARL) | 
 |  | 
 | set(CMAKE_CXX_STANDARD 11) | 
 |  | 
 | project(Marl | 
 |     VERSION   "${MARL_VERSION_MAJOR}.${MARL_VERSION_MINOR}.${MARL_VERSION_PATCH}" | 
 |     LANGUAGES C CXX ASM | 
 | ) | 
 |  | 
 | if (EMSCRIPTEN) | 
 |     add_compile_options(-O3 -pthread) | 
 | endif() | 
 |  | 
 | include(CheckCXXSourceCompiles) | 
 |  | 
 | # MARL_IS_SUBPROJECT is 1 if added via add_subdirectory() from another project. | 
 | get_directory_property(MARL_IS_SUBPROJECT PARENT_DIRECTORY) | 
 | if(MARL_IS_SUBPROJECT) | 
 |     set(MARL_IS_SUBPROJECT 1) | 
 | endif() | 
 |  | 
 | ########################################################### | 
 | # Options | 
 | ########################################################### | 
 | function(option_if_not_defined name description default) | 
 |     if(NOT DEFINED ${name}) | 
 |         option(${name} ${description} ${default}) | 
 |     endif() | 
 | endfunction() | 
 |  | 
 | option_if_not_defined(MARL_WARNINGS_AS_ERRORS "Treat warnings as errors" OFF) | 
 | option_if_not_defined(MARL_BUILD_EXAMPLES "Build example applications" OFF) | 
 | option_if_not_defined(MARL_BUILD_TESTS "Build tests" OFF) | 
 | option_if_not_defined(MARL_BUILD_BENCHMARKS "Build benchmarks" OFF) | 
 | option_if_not_defined(MARL_BUILD_SHARED "Build marl as a shared / dynamic library (default static)" OFF) | 
 | option_if_not_defined(MARL_USE_PTHREAD_THREAD_LOCAL "Use pthreads for thread local storage" OFF) | 
 | option_if_not_defined(MARL_ASAN "Build marl with address sanitizer" OFF) | 
 | option_if_not_defined(MARL_MSAN "Build marl with memory sanitizer" OFF) | 
 | option_if_not_defined(MARL_TSAN "Build marl with thread sanitizer" OFF) | 
 | option_if_not_defined(MARL_UBSAN "Build marl with undefined-behavior sanitizer" OFF) | 
 | option_if_not_defined(MARL_INSTALL "Create marl install target" OFF) | 
 | option_if_not_defined(MARL_FULL_BENCHMARK "Run benchmarks for [0 .. numLogicalCPUs] with no stepping" OFF) | 
 | option_if_not_defined(MARL_FIBERS_USE_UCONTEXT "Use ucontext instead of assembly for fibers (ignored for platforms that do not support ucontext)" OFF) | 
 | option_if_not_defined(MARL_DEBUG_ENABLED "Enable debug checks even in release builds" OFF) | 
 |  | 
 | ########################################################### | 
 | # Directories | 
 | ########################################################### | 
 | function(set_if_not_defined name value) | 
 |     if(NOT DEFINED ${name}) | 
 |         set(${name} ${value} PARENT_SCOPE) | 
 |     endif() | 
 | endfunction() | 
 |  | 
 | set(MARL_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) | 
 | set(MARL_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/include) | 
 | set_if_not_defined(MARL_THIRD_PARTY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/third_party) | 
 | set_if_not_defined(MARL_GOOGLETEST_DIR ${MARL_THIRD_PARTY_DIR}/googletest) | 
 | set_if_not_defined(MARL_BENCHMARK_DIR ${MARL_THIRD_PARTY_DIR}/benchmark) | 
 |  | 
 | ########################################################### | 
 | # Submodules | 
 | ########################################################### | 
 | if(MARL_BUILD_TESTS) | 
 |     if(NOT EXISTS ${MARL_GOOGLETEST_DIR}/.git) | 
 |         message(WARNING "third_party/googletest submodule missing.") | 
 |         message(WARNING "Run: `git submodule update --init` to build tests.") | 
 |         set(MARL_BUILD_TESTS OFF) | 
 |     endif() | 
 | endif(MARL_BUILD_TESTS) | 
 |  | 
 | if(MARL_BUILD_BENCHMARKS) | 
 |     if(NOT EXISTS ${MARL_BENCHMARK_DIR}/.git) | 
 |         message(WARNING "third_party/benchmark submodule missing.") | 
 |         message(WARNING "Run: `git submodule update --init` to build benchmarks.") | 
 |         set(MARL_BUILD_BENCHMARKS OFF) | 
 |     endif() | 
 | endif(MARL_BUILD_BENCHMARKS) | 
 |  | 
 | if(MARL_BUILD_BENCHMARKS) | 
 |     set(BENCHMARK_ENABLE_TESTING FALSE CACHE BOOL FALSE FORCE) | 
 |     add_subdirectory(${MARL_BENCHMARK_DIR}) | 
 | endif(MARL_BUILD_BENCHMARKS) | 
 |  | 
 | ########################################################### | 
 | # Compiler feature tests | 
 | ########################################################### | 
 | # Check that the Clang Thread Safety Analysis' try_acquire_capability behaves | 
 | # correctly. This is broken on some earlier versions of clang. | 
 | # See: https://bugs.llvm.org/show_bug.cgi?id=32954 | 
 | set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) | 
 | set(CMAKE_REQUIRED_FLAGS "-Wthread-safety -Werror") | 
 | check_cxx_source_compiles( | 
 |     "int main() { | 
 |       struct __attribute__((capability(\"mutex\"))) Mutex { | 
 |         void Unlock() __attribute__((release_capability)) {}; | 
 |         bool TryLock() __attribute__((try_acquire_capability(true))) { return true; }; | 
 |       }; | 
 |       Mutex m; | 
 |       if (m.TryLock()) { | 
 |         m.Unlock();  // Should not warn. | 
 |       } | 
 |       return 0; | 
 |     }" | 
 |     MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED) | 
 | set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) | 
 |  | 
 | # Check whether ucontext is supported. | 
 | set(SAVE_CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS}) | 
 | set(CMAKE_REQUIRED_FLAGS "-Werror") | 
 | check_cxx_source_compiles( | 
 |     "#include <ucontext.h> | 
 |     int main() { | 
 |       ucontext_t ctx; | 
 |       getcontext(&ctx); | 
 |       makecontext(&ctx, nullptr, 2, 1, 2); | 
 |       swapcontext(&ctx, &ctx); | 
 |       return 0; | 
 |     }" | 
 |     MARL_UCONTEXT_SUPPORTED) | 
 | set(CMAKE_REQUIRED_FLAGS ${SAVE_CMAKE_REQUIRED_FLAGS}) | 
 | if (MARL_FIBERS_USE_UCONTEXT AND NOT MARL_UCONTEXT_SUPPORTED) | 
 |     # Disable MARL_FIBERS_USE_UCONTEXT and warn if MARL_UCONTEXT_SUPPORTED is 0. | 
 |     message(WARNING "MARL_FIBERS_USE_UCONTEXT is enabled, but ucontext is not supported by the target. Disabling") | 
 |     set(MARL_FIBERS_USE_UCONTEXT 0) | 
 | endif() | 
 |  | 
 | if(MARL_IS_SUBPROJECT) | 
 |     # Export supported flags as this may be useful to parent projects | 
 |     set(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED PARENT_SCOPE ${MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED}) | 
 |     set(MARL_UCONTEXT_SUPPORTED               PARENT_SCOPE ${MARL_UCONTEXT_SUPPORTED}) | 
 | endif() | 
 |  | 
 | ########################################################### | 
 | # File lists | 
 | ########################################################### | 
 | set(MARL_LIST | 
 |     ${MARL_SRC_DIR}/debug.cpp | 
 |     ${MARL_SRC_DIR}/memory.cpp | 
 |     ${MARL_SRC_DIR}/scheduler.cpp | 
 |     ${MARL_SRC_DIR}/thread.cpp | 
 |     ${MARL_SRC_DIR}/trace.cpp | 
 | ) | 
 | if(NOT MSVC) | 
 |     list(APPEND MARL_LIST | 
 |         ${MARL_SRC_DIR}/osfiber_aarch64.c | 
 |         ${MARL_SRC_DIR}/osfiber_arm.c | 
 |         ${MARL_SRC_DIR}/osfiber_asm_aarch64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_arm.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_loongarch64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_mips64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_ppc64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_rv64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_x64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_x86.S | 
 |         ${MARL_SRC_DIR}/osfiber_loongarch64.c | 
 |         ${MARL_SRC_DIR}/osfiber_mips64.c | 
 |         ${MARL_SRC_DIR}/osfiber_ppc64.c | 
 |         ${MARL_SRC_DIR}/osfiber_rv64.c | 
 |         ${MARL_SRC_DIR}/osfiber_x64.c | 
 |         ${MARL_SRC_DIR}/osfiber_x86.c | 
 |         ${MARL_SRC_DIR}/osfiber_emscripten.cpp | 
 |     ) | 
 |     # CMAKE_OSX_ARCHITECTURES settings aren't propagated to assembly files when | 
 |     # building for Apple platforms (https://gitlab.kitware.com/cmake/cmake/-/issues/20771), | 
 |     # we treat assembly files as C files to work around this bug. | 
 |     set_source_files_properties( | 
 |         ${MARL_SRC_DIR}/osfiber_asm_aarch64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_arm.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_loongarch64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_mips64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_ppc64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_x64.S | 
 |         ${MARL_SRC_DIR}/osfiber_asm_x86.S | 
 |         PROPERTIES LANGUAGE C | 
 |     ) | 
 | endif(NOT MSVC) | 
 |  | 
 | ########################################################### | 
 | # OS libraries | 
 | ########################################################### | 
 | find_package(Threads REQUIRED) | 
 |  | 
 | ########################################################### | 
 | # Functions | 
 | ########################################################### | 
 | function(marl_set_target_options target) | 
 |     if(MARL_THREAD_SAFETY_ANALYSIS_SUPPORTED) | 
 |         target_compile_options(${target} PRIVATE "-Wthread-safety") | 
 |     endif() | 
 |  | 
 |     # Enable all warnings | 
 |     if(MSVC) | 
 |         target_compile_options(${target} PRIVATE "-W4") | 
 |     else() | 
 |         target_compile_options(${target} PRIVATE "-Wall") | 
 |     endif() | 
 |  | 
 |     # Disable specific, pedantic warnings | 
 |     if(MSVC) | 
 |         target_compile_options(${target} PRIVATE | 
 |             "-D_CRT_SECURE_NO_WARNINGS" | 
 |             "/wd4127" # conditional expression is constant | 
 |             "/wd4324" # structure was padded due to alignment specifier | 
 |         ) | 
 |     endif() | 
 |  | 
 |     # Treat all warnings as errors | 
 |     if(MARL_WARNINGS_AS_ERRORS) | 
 |         if(MSVC) | 
 |             target_compile_options(${target} PRIVATE "/WX") | 
 |         else() | 
 |             target_compile_options(${target} PRIVATE "-Werror") | 
 |         endif() | 
 |     endif(MARL_WARNINGS_AS_ERRORS) | 
 |  | 
 |     if(MARL_USE_PTHREAD_THREAD_LOCAL) | 
 |         target_compile_definitions(${target} PRIVATE "MARL_USE_PTHREAD_THREAD_LOCAL=1") | 
 |         target_link_libraries(${target} PUBLIC pthread) | 
 |     endif() | 
 |  | 
 |     if(MARL_ASAN) | 
 |         target_compile_options(${target} PUBLIC "-fsanitize=address") | 
 |         target_link_libraries(${target} PUBLIC "-fsanitize=address") | 
 |     elseif(MARL_MSAN) | 
 |         target_compile_options(${target} PUBLIC "-fsanitize=memory") | 
 |         target_link_libraries(${target} PUBLIC "-fsanitize=memory") | 
 |     elseif(MARL_TSAN) | 
 |         target_compile_options(${target} PUBLIC "-fsanitize=thread") | 
 |         target_link_libraries(${target} PUBLIC "-fsanitize=thread") | 
 |     elseif(MARL_UBSAN) | 
 |         target_compile_options(${target} PUBLIC "-fsanitize=undefined") | 
 |         target_link_libraries(${target} PUBLIC "-fsanitize=undefined") | 
 |     endif() | 
 |  | 
 |     if(MARL_FIBERS_USE_UCONTEXT) | 
 |         target_compile_definitions(${target} PRIVATE "MARL_FIBERS_USE_UCONTEXT=1") | 
 |     endif() | 
 |  | 
 |     if(MARL_DEBUG_ENABLED) | 
 |         target_compile_definitions(${target} PRIVATE "MARL_DEBUG_ENABLED=1") | 
 |     endif() | 
 |  | 
 |     if(CMAKE_SYSTEM_PROCESSOR MATCHES "^rv.*") | 
 |         target_link_libraries(${target} INTERFACE atomic) #explicitly use -latomic for RISC-V linking | 
 |     endif() | 
 |  | 
 |     target_include_directories(${target} PUBLIC $<BUILD_INTERFACE:${MARL_INCLUDE_DIR}>) | 
 | endfunction(marl_set_target_options) | 
 |  | 
 | ########################################################### | 
 | # Targets | 
 | ########################################################### | 
 |  | 
 | # marl | 
 | if(MARL_BUILD_SHARED OR BUILD_SHARED_LIBS) | 
 |     add_library(marl SHARED ${MARL_LIST}) | 
 |     if(MSVC) | 
 |         target_compile_definitions(marl | 
 |             PRIVATE "MARL_BUILDING_DLL=1" | 
 |             PUBLIC  "MARL_DLL=1" | 
 |         ) | 
 |     endif() | 
 | else() | 
 |     add_library(marl ${MARL_LIST}) | 
 | endif() | 
 |  | 
 | if(NOT MSVC) | 
 |     # Public API symbols are made visible with the MARL_EXPORT annotation. | 
 |     target_compile_options(marl PRIVATE "-fvisibility=hidden") | 
 | endif() | 
 |  | 
 | set_target_properties(marl PROPERTIES | 
 |     POSITION_INDEPENDENT_CODE 1 | 
 |     VERSION ${MARL_VERSION} | 
 |     SOVERSION "${MARL_VERSION_MAJOR}" | 
 | ) | 
 |  | 
 | marl_set_target_options(marl) | 
 |  | 
 | target_link_libraries(marl PUBLIC Threads::Threads) | 
 |  | 
 | # install | 
 | if(MARL_INSTALL) | 
 |     include(CMakePackageConfigHelpers) | 
 |     include(GNUInstallDirs) | 
 |  | 
 |     configure_package_config_file( | 
 |         ${CMAKE_CURRENT_SOURCE_DIR}/cmake/marl-config.cmake.in | 
 |         ${CMAKE_CURRENT_BINARY_DIR}/marl-config.cmake | 
 |         INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/marl | 
 |     ) | 
 |  | 
 |     install(DIRECTORY ${MARL_INCLUDE_DIR}/marl | 
 |         DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} | 
 |         USE_SOURCE_PERMISSIONS | 
 |     ) | 
 |  | 
 |     install(TARGETS marl | 
 |         EXPORT marl-targets | 
 |         ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} | 
 |         LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} | 
 |         RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} | 
 |         INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} | 
 |     ) | 
 |  | 
 |     install(EXPORT marl-targets | 
 |         FILE marl-targets.cmake | 
 |         NAMESPACE marl:: | 
 |         DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/marl | 
 |     ) | 
 |  | 
 |     install(FILES ${CMAKE_CURRENT_BINARY_DIR}/marl-config.cmake | 
 |         DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/marl | 
 |     ) | 
 | endif(MARL_INSTALL) | 
 |  | 
 | # tests | 
 | if(MARL_BUILD_TESTS) | 
 |     set(MARL_TEST_LIST | 
 |         ${MARL_SRC_DIR}/blockingcall_test.cpp | 
 |         ${MARL_SRC_DIR}/conditionvariable_test.cpp | 
 |         ${MARL_SRC_DIR}/containers_test.cpp | 
 |         ${MARL_SRC_DIR}/dag_test.cpp | 
 |         ${MARL_SRC_DIR}/defer_test.cpp | 
 |         ${MARL_SRC_DIR}/event_test.cpp | 
 |         ${MARL_SRC_DIR}/marl_test.cpp | 
 |         ${MARL_SRC_DIR}/marl_test.h | 
 |         ${MARL_SRC_DIR}/memory_test.cpp | 
 |         ${MARL_SRC_DIR}/osfiber_test.cpp | 
 |         ${MARL_SRC_DIR}/parallelize_test.cpp | 
 |         ${MARL_SRC_DIR}/pool_test.cpp | 
 |         ${MARL_SRC_DIR}/scheduler_test.cpp | 
 |         ${MARL_SRC_DIR}/thread_test.cpp | 
 |         ${MARL_SRC_DIR}/ticket_test.cpp | 
 |         ${MARL_SRC_DIR}/waitgroup_test.cpp | 
 |         ${MARL_GOOGLETEST_DIR}/googletest/src/gtest-all.cc | 
 |         ${MARL_GOOGLETEST_DIR}/googlemock/src/gmock-all.cc | 
 |     ) | 
 |  | 
 |     set(MARL_TEST_INCLUDE_DIR | 
 |         ${MARL_GOOGLETEST_DIR}/googletest/include/ | 
 |         ${MARL_GOOGLETEST_DIR}/googlemock/include/ | 
 |         ${MARL_GOOGLETEST_DIR}/googletest/ | 
 |         ${MARL_GOOGLETEST_DIR}/googlemock/ | 
 |     ) | 
 |  | 
 |     add_executable(marl-unittests ${MARL_TEST_LIST}) | 
 |  | 
 |     set_target_properties(marl-unittests PROPERTIES | 
 |         INCLUDE_DIRECTORIES "${MARL_TEST_INCLUDE_DIR}" | 
 |         FOLDER "Tests" | 
 |     ) | 
 |  | 
 |     marl_set_target_options(marl-unittests) | 
 |  | 
 |     target_link_libraries(marl-unittests PRIVATE marl) | 
 | endif(MARL_BUILD_TESTS) | 
 |  | 
 | # benchmarks | 
 | if(MARL_BUILD_BENCHMARKS) | 
 |     set(MARL_BENCHMARK_LIST | 
 |         ${MARL_SRC_DIR}/blockingcall_bench.cpp | 
 |         ${MARL_SRC_DIR}/defer_bench.cpp | 
 |         ${MARL_SRC_DIR}/event_bench.cpp | 
 |         ${MARL_SRC_DIR}/marl_bench.cpp | 
 |         ${MARL_SRC_DIR}/non_marl_bench.cpp | 
 |         ${MARL_SRC_DIR}/scheduler_bench.cpp | 
 |         ${MARL_SRC_DIR}/ticket_bench.cpp | 
 |         ${MARL_SRC_DIR}/waitgroup_bench.cpp | 
 |     ) | 
 |  | 
 |     add_executable(marl-benchmarks ${MARL_BENCHMARK_LIST}) | 
 |     set_target_properties(${target} PROPERTIES FOLDER "Benchmarks") | 
 |  | 
 |     marl_set_target_options(marl-benchmarks) | 
 |  | 
 |     target_compile_definitions(marl-benchmarks PRIVATE | 
 |         "MARL_FULL_BENCHMARK=${MARL_FULL_BENCHMARK}" | 
 |     ) | 
 |  | 
 |     target_link_libraries(marl-benchmarks PRIVATE benchmark::benchmark marl) | 
 | endif(MARL_BUILD_BENCHMARKS) | 
 |  | 
 | # examples | 
 | if(MARL_BUILD_EXAMPLES) | 
 |     function(build_example target) | 
 |         add_executable(${target} "${CMAKE_CURRENT_SOURCE_DIR}/examples/${target}.cpp") | 
 |         set_target_properties(${target} PROPERTIES FOLDER "Examples") | 
 |         marl_set_target_options(${target}) | 
 |         target_link_libraries(${target} PRIVATE marl) | 
 |         if (EMSCRIPTEN) | 
 |             target_link_options(${target} PRIVATE | 
 |                     -O1 | 
 |                     -pthread -sPTHREAD_POOL_SIZE=2 -sPROXY_TO_PTHREAD | 
 |                     -sASYNCIFY # -sASYNCIFY_STACK_SIZE=1000000 | 
 |                     -sALLOW_MEMORY_GROWTH=1 -sASSERTIONS | 
 |                     -sENVIRONMENT=web,worker | 
 |                     "SHELL:--shell-file ${CMAKE_CURRENT_SOURCE_DIR}/examples/shell.emscripten.html") | 
 |             set_target_properties(${target} PROPERTIES SUFFIX .html) | 
 |         endif() | 
 |     endfunction(build_example) | 
 |  | 
 |     build_example(fractal) | 
 |     build_example(hello_task) | 
 |     build_example(primes) | 
 |     build_example(tasks_in_tasks) | 
 | endif(MARL_BUILD_EXAMPLES) |