From 43934dfb620a0d85275cc2193bee5e241c748238 Mon Sep 17 00:00:00 2001 From: Laurynas Biveinis Date: Mon, 13 Jan 2025 07:13:13 +0200 Subject: [PATCH] Create an example of including UnoDB as a CMake subdirectory - Update UnoDB CMakeLists.txt to use GSL, Google Test, & Google Benchmark CMake targets if they are already set up by parent CMake. - Convert examples/CMakeLists.txt to be a top-level CMake script, showing an example of using UnoDB as a dependency. - Update STANDALONE CMake option to skip building benchmarks and tests when OFF. - Update CI to build examples from the top-level script. --- .circleci/config.yml | 81 ++++-- .github/workflows/build.yml | 69 +++++- .github/workflows/msvc-build.yml | 8 - .github/workflows/old-compilers.yml | 46 +++- .github/workflows/ubuntu-20.04.yml | 10 +- CMakeLists.txt | 366 +++++++++++++++------------- README.md | 32 ++- examples/CMakeLists.txt | 33 ++- test/CMakeLists.txt | 8 +- 9 files changed, 404 insertions(+), 249 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 49bdfd85..7ec4796b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,9 +23,6 @@ jobs: resource_class: arm.medium steps: - checkout - - run: - name: Checkout submodules - command: git submodule update --init - run: name: Installing dependencies (common) command: | @@ -70,6 +67,44 @@ jobs: - run: name: Create build environment command: mkdir build + - when: + condition: + not: + or: + - << parameters.asan >> + - << parameters.tsan >> + - << parameters.ubsan >> + steps: + - run: + name: Create build environment (examples) + command: mkdir build/build-examples + - run: + name: Installing dependencies (examples) + command: | + sudo apt-get install libmsgsl-dev + - run: + name: Configure CMake (examples) + working_directory: build/build-examples + command: | + readonly BUILD_TYPE=<< parameters.build_type >> + readonly COMPILER=<< parameters.compiler >> + if [[ $COMPILER == "gcc" ]]; then + V=13 + export CC=gcc-$V + export CXX=g++-$V + elif [[ $COMPILER == "clang" ]]; then + V=19 + export CC=clang-$V + export CXX=clang++-$V + fi + cmake ../../examples -DCMAKE_BUILD_TYPE=$BUILD_TYPE + - run: + name: Examples + working_directory: build/build-examples + command: make -j2 examples + - run: + name: Checkout submodules + command: git submodule update --init - run: name: Configure CMake working_directory: build @@ -107,10 +142,6 @@ jobs: name: Build working_directory: build command: make -j2 - - run: - name: Examples - working_directory: build - command: make examples - run: name: Correctness test working_directory: build @@ -160,9 +191,6 @@ jobs: resource_class: macos.m1.medium.gen1 steps: - checkout - - run: - name: Checkout submodules - command: git submodule update --init --recursive - run: name: Install dependencies command: | @@ -171,6 +199,35 @@ jobs: - run: name: Create build environment command: mkdir build + - when: + condition: + not: + or: + - << parameters.asan >> + - << parameters.tsan >> + - << parameters.ubsan >> + steps: + - run: + name: Create build environment (examples) + command: mkdir build/build-examples + - run: + name: Installing dependencies (examples) + command: brew install cpp-gsl googletest google-benchmark + - run: + name: Configure CMake (examples) + working_directory: build/build-examples + command: | + readonly BUILD_TYPE=<< parameters.build_type >> + export CC=clang + export CXX=clang++ + cmake ../../examples -DCMAKE_BUILD_TYPE=$BUILD_TYPE + - run: + name: Examples + working_directory: build/build-examples + command: make -j3 examples + - run: + name: Checkout submodules + command: git submodule update --init --recursive - run: name: Configure CMake working_directory: build @@ -188,10 +245,6 @@ jobs: name: Build working_directory: build command: make -j3 - - run: - name: Examples - working_directory: build - command: make examples - run: name: Correctness test working_directory: build diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 94ae12c8..6452ae68 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -286,7 +286,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true + submodules: false - name: Setup common dependencies for Linux run: | @@ -340,6 +340,13 @@ jobs: sudo cpanm install JSON if: runner.os == 'Linux' && env.COVERAGE == 'ON' + - name: Set up dependencies for Linux (examples) + run: sudo apt-get install libmsgsl-dev + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' && env.STATIC_ANALYSIS != 'ON' + && runner.os == 'Linux' && env.COVERAGE != 'ON' + - name: Set up dependencies for macOS (common) run: | brew install boost include-what-you-use python-setuptools @@ -349,15 +356,64 @@ jobs: run: brew install cppcheck if: runner.os == 'macOS' && env.CPPCHECK == 'ON' - - name: Create build environment - run: mkdir ${{github.workspace}}/build + - name: Set up dependencies for macOS (examples) + run: brew install cpp-gsl + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' && env.STATIC_ANALYSIS != 'ON' + && runner.os == 'macOS' && env.COVERAGE != 'ON' + + - name: Configure CMake (examples) + # Use a bash shell so we can use the same syntax for environment + # variable access regardless of the host operating system + shell: bash + run: | + mkdir ${{github.workspace}}/build-examples + cd ${{github.workspace}}/build-examples + COMPILER="${COMPILER:-$DEFAULT_COMPILER}" + export PATH="$HOME/.local/bin:$PATH" + if [[ -n "$BUILD_TYPE" ]]; then + CBT="-DCMAKE_BUILD_TYPE=$BUILD_TYPE" + else + CBT="" + fi + if [[ $COMPILER == "gcc" ]]; then + V=13 + export CC=gcc-$V + export CXX=g++-$V + elif [[ $COMPILER == "clang" ]]; then + V=19 + export CC=clang-$V + export CXX=clang++-$V + elif [[ $COMPILER == "macos-clang" ]]; then + export CC=clang + export CXX=clang++ + fi + cmake "$GITHUB_WORKSPACE/examples" "$CBT" + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' && env.STATIC_ANALYSIS != 'ON' + && env.COVERAGE != 'ON' + + - name: Examples + working-directory: ${{github.workspace}}/build-examples + run: make -j3 examples + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' && env.STATIC_ANALYSIS != 'ON' + && env.COVERAGE != 'ON' + + - uses: actions/checkout@v4 + with: + submodules: true - name: Configure CMake # Use a bash shell so we can use the same syntax for environment # variable access regardless of the host operating system shell: bash - working-directory: ${{github.workspace}}/build run: | + mkdir ${{github.workspace}}/build + cd ${{github.workspace}}/build COMPILER="${COMPILER:-$DEFAULT_COMPILER}" SANITIZE_ADDRESS="${SANITIZE_ADDRESS:-$DEFAULT_SANITIZE_ADDRESS}" SANITIZE_THREAD="${SANITIZE_THREAD:-$DEFAULT_SANITIZE_THREAD}" @@ -426,11 +482,6 @@ jobs: --force-analyze-debug-code make -j3; if: env.STATIC_ANALYSIS == 'ON' && env.COMPILER == 'clang' - - name: Examples - working-directory: ${{github.workspace}}/build - run: make examples - if: env.STATIC_ANALYSIS != 'ON' && env.COVERAGE != 'ON' - - name: Correctness test working-directory: ${{github.workspace}}/build run: ctest -j3 -V diff --git a/.github/workflows/msvc-build.yml b/.github/workflows/msvc-build.yml index 318e12e9..3433e16c 100644 --- a/.github/workflows/msvc-build.yml +++ b/.github/workflows/msvc-build.yml @@ -91,14 +91,6 @@ jobs: && (matrix.preset == 'msvc-static-analysis-release' || matrix.preset == 'msvc-static-analysis-debug') - - name: Examples - run: | - $Env:P = "${{ matrix.preset }}" - cmake --build --preset "$env:P" --target examples - if: > - (matrix.preset != 'msvc-static-analysis-release') - && (matrix.preset != 'msvc-static-analysis-debug') - - name: Correctness test run: ctest -V --preset "${{ matrix.preset }}" if: > diff --git a/.github/workflows/old-compilers.yml b/.github/workflows/old-compilers.yml index 1232820c..b1362197 100644 --- a/.github/workflows/old-compilers.yml +++ b/.github/workflows/old-compilers.yml @@ -446,7 +446,7 @@ jobs: steps: - uses: actions/checkout@v4 with: - submodules: true + submodules: false - name: Setup common dependencies run: | @@ -485,8 +485,42 @@ jobs: sudo apt-get install -y gcc if: env.COMPILER == 'gcc' && env.VERSION == '11' - - name: Create build environment - run: mkdir ${{github.workspace}}/build + - name: Set up dependencies for examples + run: sudo apt-get install libmsgsl-dev + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' + + - name: Configure CMake (examples) + # Use a bash shell so we can use the same syntax for environment + # variable access regardless of the host operating system + shell: bash + run: | + mkdir ${{github.workspace}}/build-examples + cd ${{github.workspace}}/build-examples + export PATH=$HOME/.local/bin:$PATH + if [[ $COMPILER == "gcc" ]]; then + export CC="gcc-${VERSION}" + export CXX="g++-${VERSION}" + else + export CC="clang-${VERSION}" + export CXX="clang++-${VERSION}" + fi + cmake "$GITHUB_WORKSPACE/examples" "-DCMAKE_BUILD_TYPE=$BUILD_TYPE" + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' + + - name: Examples + working-directory: ${{github.workspace}}/build-examples + run: make -j3 examples + if: > + env.SANITIZE_ADDRESS != 'ON' && env.SANITIZE_THREAD != 'ON' + && env.SANITIZE_UB != 'ON' + + - uses: actions/checkout@v4 + with: + submodules: true - name: Configure CMake # Use a bash shell so we can use the same syntax for environment @@ -494,6 +528,8 @@ jobs: shell: bash working-directory: ${{github.workspace}}/build run: | + mkdir ${{github.workspace}}/build + cd ${{github.workspace}}/build SANITIZE_ADDRESS="${SANITIZE_ADDRESS:-$DEFAULT_SANITIZE_ADDRESS}" SANITIZE_THREAD="${SANITIZE_THREAD:-$DEFAULT_SANITIZE_THREAD}" SANITIZE_UB="${SANITIZE_UB:-$DEFAULT_SANITIZE_UB}" @@ -530,10 +566,6 @@ jobs: working-directory: ${{github.workspace}}/build run: make -j3 - - name: Examples - working-directory: ${{github.workspace}}/build - run: make examples - - name: Correctness test working-directory: ${{github.workspace}}/build run: ctest -j3 -V diff --git a/.github/workflows/ubuntu-20.04.yml b/.github/workflows/ubuntu-20.04.yml index 63aa22ec..5805c835 100644 --- a/.github/workflows/ubuntu-20.04.yml +++ b/.github/workflows/ubuntu-20.04.yml @@ -195,15 +195,13 @@ jobs: "lld-${VERSION}" if: env.COMPILER == 'clang' && env.BUILD_TYPE == 'Release' - - name: Create build environment - run: mkdir ${{github.workspace}}/build - - name: Configure CMake # Use a bash shell so we can use the same syntax for environment # variable access regardless of the host operating system shell: bash - working-directory: ${{github.workspace}}/build run: | + mkdir ${{github.workspace}}/build + cd ${{github.workspace}}/build SANITIZE_ADDRESS="${SANITIZE_ADDRESS:-$DEFAULT_SANITIZE_ADDRESS}" SANITIZE_THREAD="${SANITIZE_THREAD:-$DEFAULT_SANITIZE_THREAD}" SANITIZE_UB="${SANITIZE_UB:-$DEFAULT_SANITIZE_UB}" @@ -232,10 +230,6 @@ jobs: working-directory: ${{github.workspace}}/build run: make -j3 - - name: Examples - working-directory: ${{github.workspace}}/build - run: make examples - - name: Correctness test working-directory: ${{github.workspace}}/build run: ctest -j3 -V diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f5c6a8a..5fbfd5d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,7 +43,7 @@ endif() set_bool(is_not_msvc_clang ${is_clang} AND NOT ${is_msvc_clang}) option(STANDALONE - "Build UnoDB not for linking with outside code. Enables extra global checks") + "Build UnoDB as a standalone project. Builds tests & benchmarks, enables extra global checks") option(MAINTAINER_MODE "Maintainer mode: compiler warnings are fatal, IWYU required") @@ -187,6 +187,9 @@ set(MSVC_STATIC_ANALYSIS_FLAGS "/analyze" "/analyze:external-" option(COVERAGE "Enable code coverage reporting") if(COVERAGE) + if(NOT STANDALONE) + message(FATAL_ERROR "Code coverage reporting only allowed in the standalone build") + endif() if(MSVC) message(FATAL_ERROR "MSVC is incompatible with code code coverage reporting") endif() @@ -242,18 +245,20 @@ if(SANITIZE_ADDRESS) "-fsanitize-address-use-after-scope" "-fsanitize=pointer-compare" "-fsanitize=pointer-subtract") endif() - string(CONCAT ASAN_ENV "ASAN_OPTIONS=" - "check_initialization_order=true:detect_stack_use_after_return=true:" - "alloc_dealloc_mismatch=true:strict_string_checks=true") - if(NOT is_gxx) - # False positive on std::vector::capacity with GCC 12 - string(APPEND ASAN_ENV ":detect_invalid_pointer_pairs=2") - endif() - if(is_clang OR is_gxx) - string(APPEND ASAN_ENV ":detect_leaks=1") + if(STANDALONE) + string(CONCAT ASAN_ENV "ASAN_OPTIONS=" + "check_initialization_order=true:detect_stack_use_after_return=true:" + "alloc_dealloc_mismatch=true:strict_string_checks=true") + if(NOT is_gxx) + # False positive on std::vector::capacity with GCC 12 + string(APPEND ASAN_ENV ":detect_invalid_pointer_pairs=2") + endif() + if(is_clang OR is_gxx) + string(APPEND ASAN_ENV ":detect_leaks=1") + endif() + set(SANITIZER_ENV ${ASAN_ENV}) + unset(ASAN_ENV) endif() - set(SANITIZER_ENV ${ASAN_ENV}) - unset(ASAN_ENV) else() if(MSVC AND NOT is_clang) string(REGEX REPLACE "/Zi" "/ZI" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") @@ -287,10 +292,12 @@ if(SANITIZE_UB) endif() endif() set(SANITIZER_LD_FLAGS "-fsanitize=undefined") - string(CONCAT UBSAN_ENV "UBSAN_OPTIONS=" - "print_stacktrace=1:halt_on_error=1:abort_on_error=1") - set(SANITIZER_ENV ${UBSAN_ENV}) - unset(UBSAN_ENV) + if(STANDALONE) + string(CONCAT UBSAN_ENV "UBSAN_OPTIONS=" + "print_stacktrace=1:halt_on_error=1:abort_on_error=1") + set(SANITIZER_ENV ${UBSAN_ENV}) + unset(UBSAN_ENV) + endif() endif() option(STATIC_ANALYSIS "Enable compiler static analysis") @@ -345,126 +352,12 @@ else() set(USE_BOOST_STACKTRACE OFF) endif() -add_subdirectory(3rd_party/GSL) - -string(REPLACE ";" " " CXX_FLAGS_FOR_SUBDIR_STR "${SANITIZER_CXX_FLAGS}") -if(MSVC) - string(REPLACE ";" " " MSVC_WARNING_FLAGS_FOR_SUBDIR_STR_TMP "${MSVC_CXX_WARNING_FLAGS}") - string(REPLACE "/Wall" "" MSVC_WARNING_FLAGS_FOR_SUBDIR_STR - "${MSVC_WARNING_FLAGS_FOR_SUBDIR_STR_TMP}") -endif() -string(REPLACE ";" " " LD_FLAGS_FOR_SUBDIR_STR "${SANITIZER_LD_FLAGS}") - -macro(ADD_CXX_FLAGS_FOR_SUBDIR) - set(ORIG_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) - set(ORIG_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) - set(ORIG_CMAKE_MODULE_LINKER_FLAGS ${CMAKE_MODULE_LINKER_FLAGS}) - set(ORIG_CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) - string(APPEND CMAKE_CXX_FLAGS " " "${CXX_FLAGS_FOR_SUBDIR_STR}") - if(MSVC) - string(APPEND CMAKE_CXX_FLAGS " " "${MSVC_WARNING_FLAGS_FOR_SUBDIR_STR}") - endif() - if(is_not_msvc_clang AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) - string(APPEND CMAKE_CXX_FLAGS " " "${CLANG_GE_14_CXX_FLAGS}") - endif() - string(APPEND CMAKE_EXE_LINKER_FLAGS "${LD_FLAGS_FOR_SUBDIR_STR}") - string(APPEND CMAKE_MODULE_LINKER_FLAGS "${LD_FLAGS_FOR_SUBDIR_STR}") - string(APPEND CMAKE_SHARED_LINKER_FLAGS "${LD_FLAGS_FOR_SUBDIR_STR}") -endmacro() - -macro(RESTORE_CXX_FLAGS_FOR_SUBDIR) - set(CMAKE_CXX_FLAGS ${ORIG_CMAKE_CXX_FLAGS}) - set(CMAKE_EXE_LINKER_FLAGS ${ORIG_CMAKE_EXE_LINKER_FLAGS}) - set(CMAKE_MODULE_LINKER_FLAGS ${ORIG_CMAKE_MODULE_LINKER_FLAGS}) - set(CMAKE_SHARED_LINKER_FLAGS ${ORIG_CMAKE_SHARED_LINKER_FLAGS}) -endmacro() - -# For Windows: Prevent overriding the parent project's compiler/linker settings -set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - -ADD_CXX_FLAGS_FOR_SUBDIR() -add_subdirectory(3rd_party/googletest) -RESTORE_CXX_FLAGS_FOR_SUBDIR() - -set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "Suppressing Google Benchmark tests" - FORCE) -set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL - "Suppressing Google Benchmark installation" FORCE) - -if(IPO_SUPPORTED) - if(is_debug) - # It seems that Google Benchmark does not support multi-configuration - # generators if LTO is enabled - message(STATUS "Disabling LTO for Google Benchmark due to debug build") - elseif(is_apple_clang) - message(STATUS - "Disabling LTO for Google Benchmark because Apple clang is not supported") - else() - set(BENCHMARK_ENABLE_LTO ON CACHE BOOL "Enabling LTO for Google Benchmark" - FORCE) - message(STATUS "Enabling LTO for Google Benchmark") - endif() -endif() - -ADD_CXX_FLAGS_FOR_SUBDIR() -add_subdirectory(3rd_party/benchmark) -RESTORE_CXX_FLAGS_FOR_SUBDIR() - -# Do not build DeepState: -# - under Windows as it's not supported -# - on anything else than x86_64 -# - if 32-bit build is not possible -# - with GCC under macOS due to https://github.com/trailofbits/deepstate/issues/374 -# - under macOS with ASan or TSan enabled -if(NOT (is_darwin AND (is_gxx OR SANITIZE_ADDRESS OR SANITIZE_THREAD)) - AND NOT is_windows AND is_x86_64) - CHECK_INCLUDE_FILE(stdio.h CAN_BUILD_32BIT -m32) - - if(CAN_BUILD_32BIT) - message(STATUS "32-bit compilation supported, building DeepState") - - # ThreadSanitizer is not compatible with libfuzzer and LLVM 11 linker crashes - # on libfuzzer release build - if(is_clang AND NOT(is_release) AND NOT(SANITIZE_THREAD)) - set(LIBFUZZER_AVAILABLE TRUE) - set(BUILD_DEEPSTATE_LIBFUZZER "-DDEEPSTATE_LIBFUZZER=ON") - else() - set(LIBFUZZER_AVAILABLE FALSE) - set(BUILD_DEEPSTATE_LIBFUZZER "-DDEEPSTATE_LIBFUZZER=OFF") - endif() - - include(ExternalProject) - ExternalProject_Add(3rd_party_deepstate - SOURCE_DIR "${CMAKE_SOURCE_DIR}/3rd_party/deepstate" - BINARY_DIR "${CMAKE_BINARY_DIR}/3rd_party/deepstate" - CMAKE_ARGS "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DCMAKE_C_FLAGS=-w -Wno-implicit-function-declaration" - "-DCMAKE_CXX_FLAGS=-w" "${BUILD_DEEPSTATE_LIBFUZZER}" - INSTALL_COMMAND "") - ExternalProject_Get_property(3rd_party_deepstate SOURCE_DIR) - ExternalProject_Get_property(3rd_party_deepstate BINARY_DIR) - - add_library(deepstate STATIC IMPORTED) - add_dependencies(deepstate 3rd_party_deepstate) - target_include_directories(deepstate INTERFACE "${SOURCE_DIR}/src/include/") - set_target_properties(deepstate PROPERTIES IMPORTED_LOCATION - "${BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}deepstate${CMAKE_STATIC_LIBRARY_SUFFIX}") - - if(LIBFUZZER_AVAILABLE) - add_library(deepstate_lf STATIC IMPORTED) - add_dependencies(deepstate_lf 3rd_party_deepstate) - target_include_directories(deepstate_lf INTERFACE - "${SOURCE_DIR}/src/include/") - set_target_properties(deepstate_lf PROPERTIES IMPORTED_LOCATION - "${BINARY_DIR}/libdeepstate_LF.a") - endif() - else() - message(STATUS "32-bit compilation not supported, skipping DeepState build") - endif() +if (NOT TARGET Microsoft.GSL::GSL) + add_subdirectory(3rd_party/GSL) endif() # Generator expression helpers +# Platform set(is_release_genex "$") set(is_not_release_genex "$") set(is_not_windows_x86_64 "$") @@ -472,6 +365,7 @@ set(is_windows_x86_64 "$") set(is_windows_genex "$") set(is_not_windows "$") set(is_linux "$") +# Compiler set(is_gxx_genex "$") set(is_clang_genex "$") set(is_any_clang_genex "$") @@ -482,6 +376,7 @@ set(is_clang_cl "$") set(is_any_msvc "$") set(is_x86_64_any_msvc "$") set(is_clang_not_windows "$") +# Compiler version set(cxx_ge_11 "$,11.0>") set(cxx_ge_12 "$,12.0>") set(cxx_lt_13 "$,13.0>") @@ -493,23 +388,152 @@ set(is_clang_ge_14_not_windows "$") set(is_gxx_ge_11 "$") set(is_gxx_ge_12 "$") set(is_gxx_ge_14 "$") +# Configuration set(has_avx2 "$") set(use_boost_stacktrace "$") set(with_stats "$") set(fatal_warnings_on "$") set(coverage_on "$") set(is_standalone "$") +# Checks across above categories set(is_gxx_not_release_standalone $) -target_compile_definitions(benchmark PUBLIC - "$<${is_gxx_not_release_standalone}:_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC>") +if(STANDALONE) + string(REPLACE ";" " " CXX_FLAGS_FOR_SUBDIR_STR "${SANITIZER_CXX_FLAGS}") + if(MSVC) + string(REPLACE ";" " " MSVC_WARNING_FLAGS_FOR_SUBDIR_STR_TMP + "${MSVC_CXX_WARNING_FLAGS}") + string(REPLACE "/Wall" "" MSVC_WARNING_FLAGS_FOR_SUBDIR_STR + "${MSVC_WARNING_FLAGS_FOR_SUBDIR_STR_TMP}") + endif() + string(REPLACE ";" " " LD_FLAGS_FOR_SUBDIR_STR "${SANITIZER_LD_FLAGS}") + + macro(ADD_CXX_FLAGS_FOR_SUBDIR) + set(ORIG_CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS}) + set(ORIG_CMAKE_EXE_LINKER_FLAGS ${CMAKE_EXE_LINKER_FLAGS}) + set(ORIG_CMAKE_MODULE_LINKER_FLAGS ${CMAKE_MODULE_LINKER_FLAGS}) + set(ORIG_CMAKE_SHARED_LINKER_FLAGS ${CMAKE_SHARED_LINKER_FLAGS}) + string(APPEND CMAKE_CXX_FLAGS " " "${CXX_FLAGS_FOR_SUBDIR_STR}") + if(MSVC) + string(APPEND CMAKE_CXX_FLAGS " " "${MSVC_WARNING_FLAGS_FOR_SUBDIR_STR}") + endif() + if(is_not_msvc_clang + AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 14.0) + string(APPEND CMAKE_CXX_FLAGS " " "${CLANG_GE_14_CXX_FLAGS}") + endif() + string(APPEND CMAKE_EXE_LINKER_FLAGS "${LD_FLAGS_FOR_SUBDIR_STR}") + string(APPEND CMAKE_MODULE_LINKER_FLAGS "${LD_FLAGS_FOR_SUBDIR_STR}") + string(APPEND CMAKE_SHARED_LINKER_FLAGS "${LD_FLAGS_FOR_SUBDIR_STR}") + endmacro() + + macro(RESTORE_CXX_FLAGS_FOR_SUBDIR) + set(CMAKE_CXX_FLAGS ${ORIG_CMAKE_CXX_FLAGS}) + set(CMAKE_EXE_LINKER_FLAGS ${ORIG_CMAKE_EXE_LINKER_FLAGS}) + set(CMAKE_MODULE_LINKER_FLAGS ${ORIG_CMAKE_MODULE_LINKER_FLAGS}) + set(CMAKE_SHARED_LINKER_FLAGS ${ORIG_CMAKE_SHARED_LINKER_FLAGS}) + endmacro() + + # For Windows: Prevent overriding the parent project's compiler/linker + # settings + set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) + + ADD_CXX_FLAGS_FOR_SUBDIR() + add_subdirectory(3rd_party/googletest) + RESTORE_CXX_FLAGS_FOR_SUBDIR() + + set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL + "Suppressing Google Benchmark tests" FORCE) + set(BENCHMARK_ENABLE_INSTALL OFF CACHE BOOL + "Suppressing Google Benchmark installation" FORCE) + + if(IPO_SUPPORTED) + if(is_debug) + # It seems that Google Benchmark does not support multi-configuration + # generators if LTO is enabled + message(STATUS "Disabling LTO for Google Benchmark due to debug build") + elseif(is_apple_clang) + message(STATUS + "Disabling LTO for Google Benchmark because Apple clang is not supported") + else() + set(BENCHMARK_ENABLE_LTO ON CACHE BOOL "Enabling LTO for Google Benchmark" + FORCE) + message(STATUS "Enabling LTO for Google Benchmark") + endif() + endif() -# Add benchmark_include_dirs by target_include_directories(... SYSTEM ...) -# before target_link_libraries so that benchmark headers are included through -# -isystem not -I, resulting in build-breaking diagnostics. -get_target_property(benchmark_include_dirs benchmark::benchmark - INTERFACE_INCLUDE_DIRECTORIES) + ADD_CXX_FLAGS_FOR_SUBDIR() + add_subdirectory(3rd_party/benchmark) + RESTORE_CXX_FLAGS_FOR_SUBDIR() + + target_compile_definitions(benchmark PUBLIC + "$<${is_gxx_not_release_standalone}:_GLIBCXX_DEBUG -D_GLIBCXX_DEBUG_PEDANTIC>") + + # Do not build DeepState: + # - if not a standalone configuration + # - under Windows as it's not supported + # - on anything else than x86_64 + # - if 32-bit build is not possible + # - with GCC under macOS due to + # https://github.com/trailofbits/deepstate/issues/374 + # - under macOS with ASan or TSan enabled + if(NOT (is_darwin AND (is_gxx OR SANITIZE_ADDRESS OR SANITIZE_THREAD)) + AND NOT is_windows AND is_x86_64) + INCLUDE(CheckIncludeFile) + ENABLE_LANGUAGE(C) + CHECK_INCLUDE_FILE(stdio.h CAN_BUILD_32BIT -m32) + + if(CAN_BUILD_32BIT) + message(STATUS "32-bit compilation supported, building DeepState") + + # ThreadSanitizer is not compatible with libfuzzer and LLVM 11 linker + # crashes on libfuzzer release build + if(is_clang AND NOT(is_release) AND NOT(SANITIZE_THREAD)) + set(LIBFUZZER_AVAILABLE TRUE) + set(BUILD_DEEPSTATE_LIBFUZZER "-DDEEPSTATE_LIBFUZZER=ON") + else() + set(LIBFUZZER_AVAILABLE FALSE) + set(BUILD_DEEPSTATE_LIBFUZZER "-DDEEPSTATE_LIBFUZZER=OFF") + endif() + + include(ExternalProject) + ExternalProject_Add(3rd_party_deepstate + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rd_party/deepstate" + BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}/3rd_party/deepstate" + CMAKE_ARGS "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DCMAKE_C_FLAGS=-w -Wno-implicit-function-declaration" + "-DCMAKE_CXX_FLAGS=-w" "${BUILD_DEEPSTATE_LIBFUZZER}" + INSTALL_COMMAND "") + ExternalProject_Get_property(3rd_party_deepstate SOURCE_DIR) + ExternalProject_Get_property(3rd_party_deepstate BINARY_DIR) + + add_library(deepstate STATIC IMPORTED) + add_dependencies(deepstate 3rd_party_deepstate) + target_include_directories(deepstate INTERFACE "${SOURCE_DIR}/src/include/") + set_target_properties(deepstate PROPERTIES IMPORTED_LOCATION + "${BINARY_DIR}/${CMAKE_STATIC_LIBRARY_PREFIX}deepstate${CMAKE_STATIC_LIBRARY_SUFFIX}") + + if(LIBFUZZER_AVAILABLE) + add_library(deepstate_lf STATIC IMPORTED) + add_dependencies(deepstate_lf 3rd_party_deepstate) + target_include_directories(deepstate_lf INTERFACE + "${SOURCE_DIR}/src/include/") + set_target_properties(deepstate_lf PROPERTIES IMPORTED_LOCATION + "${BINARY_DIR}/libdeepstate_LF.a") + endif() + else() + message(STATUS + "32-bit compilation not supported, skipping DeepState build") + endif() + endif() + + # Add benchmark_include_dirs by target_include_directories(... SYSTEM ...) + # before target_link_libraries so that benchmark headers are included through + # -isystem not -I, resulting in build-breaking diagnostics. + get_target_property(benchmark_include_dirs benchmark::benchmark + INTERFACE_INCLUDE_DIRECTORIES) +endif() if(NOT is_any_clang) message(STATUS "Not using clang-tidy due to non-clang compiler being used") @@ -608,7 +632,7 @@ function(COMMON_TARGET_PROPERTIES TARGET) cmake_parse_arguments(PARSE_ARGV 1 CTP "SKIP_CHECKS" "" "") target_compile_features(${TARGET} PUBLIC cxx_std_17) set_target_properties(${TARGET} PROPERTIES CXX_EXTENSIONS OFF) - target_compile_definitions(${TARGET} PRIVATE + target_compile_definitions(${TARGET} PUBLIC "$<${is_standalone}:UNODB_DETAIL_STANDALONE>" "$<${use_boost_stacktrace}:UNODB_DETAIL_BOOST_STACKTRACE>" "$<${with_stats}:UNODB_DETAIL_WITH_STATS>" @@ -723,43 +747,45 @@ if(LIBFUZZER_AVAILABLE) target_link_libraries(unodb_lf PUBLIC unodb_util unodb_qsbr_lf) endif() -set(VALGRIND_COMMAND "valgrind" "--error-exitcode=1" "--leak-check=full" - "--trace-children=yes" "-v") +if (STANDALONE) + set(VALGRIND_COMMAND "valgrind" "--error-exitcode=1" "--leak-check=full" + "--trace-children=yes" "-v") -add_custom_target(valgrind - DEPENDS valgrind_tests valgrind_benchmarks valgrind_examples) + add_custom_target(valgrind DEPENDS valgrind_tests valgrind_benchmarks) -enable_testing() + enable_testing() -add_unodb_library(unodb_test test_heap.cpp) -target_link_libraries(unodb_test PUBLIC unodb_util) -target_link_libraries(unodb_test PRIVATE ${Boost_LIBRARIES}) + add_unodb_library(unodb_test test_heap.cpp) + target_link_libraries(unodb_test PUBLIC unodb_util) + target_link_libraries(unodb_test PRIVATE ${Boost_LIBRARIES}) -function(add_sanitized_test) - cmake_parse_arguments(AST "" "NAME" "COMMAND" ${ARGN}) - add_test(NAME "${AST_NAME}" COMMAND ${AST_COMMAND}) - if(SANITIZE_ADDRESS OR SANITIZE_THREAD OR SANITIZE_UB) - set_property(TEST "${AST_NAME}" APPEND PROPERTY ENVIRONMENT - "${SANITIZER_ENV}") + function(add_sanitized_test) + cmake_parse_arguments(AST "" "NAME" "COMMAND" ${ARGN}) + add_test(NAME "${AST_NAME}" COMMAND ${AST_COMMAND}) + if(SANITIZE_ADDRESS OR SANITIZE_THREAD OR SANITIZE_UB) + set_property(TEST "${AST_NAME}" APPEND PROPERTY ENVIRONMENT + "${SANITIZER_ENV}") + endif() + endfunction() + + add_subdirectory(benchmark) + + if(TARGET deepstate) + add_subdirectory(fuzz_deepstate) + add_dependencies(valgrind valgrind_deepstate) endif() -endfunction() -add_subdirectory(benchmark) -add_subdirectory(examples) -if(TARGET deepstate) - add_subdirectory(fuzz_deepstate) - add_dependencies(valgrind valgrind_deepstate) -endif() -add_subdirectory(test) + add_subdirectory(test) -set(BIN_DIR_CDB "${CMAKE_BINARY_DIR}/compile_commands.json") -set(SRC_DIR_CDB "${CMAKE_SOURCE_DIR}/compile_commands.json") + set(BIN_DIR_CDB "${CMAKE_BINARY_DIR}/compile_commands.json") + set(SRC_DIR_CDB "${CMAKE_SOURCE_DIR}/compile_commands.json") -# cmake -E create_symlink has Windows implementation, but additional permissions -# are required. -if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink - ${BIN_DIR_CDB} ${SRC_DIR_CDB}) + # cmake -E create_symlink has Windows implementation, but additional + # permissions are required. + if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink + ${BIN_DIR_CDB} ${SRC_DIR_CDB}) + endif() endif() message(STATUS "User-set CMake options:") diff --git a/README.md b/README.md index e63fdd1f..6062d4f3 100644 --- a/README.md +++ b/README.md @@ -36,20 +36,23 @@ platform-specific features: * Earliest versions of supported compilers: GCC 10, LLVM 11, XCode 16.1, MSVC 2022. Open an issue if you require support for an older version. -* CMake, at least 3.12 +* CMake, at least 3.16 * Boost library. If building with statistics counters, then it is a mandatory dependency for Boost.Accumulator. It is also an optional dependency for Boost.Stacktrace. -### Build dependencies, bundled as git submodules +### Build dependencies, bundled as optional git submodules + +This dependency is bundled as a git submodule but can alternatively be +provided by the parent project: * Guidelines Support Library for `gsl::span`. -* Google Test for tests. -* Google Benchmark for microbenchmarks. -* [DeepState][deepstate] for fuzzing tests. -### Optional dependencies for development +### Optional dependencies for development, testing, & benchmarking +* (vendored) Google Test for tests. +* (vendored) Google Benchmark for microbenchmarks. +* [DeepState][deepstate] for fuzzing tests. * clang-format * lcov * clang-tidy @@ -61,19 +64,14 @@ platform-specific features: ## Building -Out-of-source builds are recommended. Before anything else, do - -``` bash -# --recursive is not strictly required at the moment, but a good habit to have -git submodule update --init --recursive -``` - -There are some CMake options for users: +Out-of-source builds are recommended. There are some CMake options for users: * `-DSTANDALONE=OFF` if you are building this as a part of another project, `ON` - if you work on UnoDB itself. It will enable extra global debug checks that - require entire programs to be compiled with them. Currently, this consists of - the libstdc++ debug mode. The default is `OFF`. + if you work on UnoDB itself. This will build tests and benchmarks, and will + eanble extra global debug checks that require entire programs to be compiled + with them. Currently, this consists of the libstdc++ debug mode. The vendored + development dependencies must be present, i.e. `git submodule update --init + --recursive`. The default is `OFF`. * `-DSPINLOCK_LOOP=PAUSE|EMPTY` to choose the spinlock wait loop body implementation for the optimistic lock. `EMPTY` may benchmark better as long as there are fewer threads than available CPU cores. `PAUSE` will use that diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 6e37fcab..6f1e79ea 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,25 +1,32 @@ -# Copyright 2024 Laurynas Biveinis +# Copyright 2024-2025 Laurynas Biveinis +cmake_minimum_required(VERSION 3.16) + +# This is a top-level CMake script and not an include of ../CMakeLists.txt. In +# fact, this one includes the one in the parent directory to show how another +# project can integrate UnoDB in its build. + +project(unodb-examples VERSION 0.1 + DESCRIPTION "unodb key-value store library examples" + HOMEPAGE_URL "https://github.com/laurynas-biveinis/unodb" LANGUAGES CXX) + +# An example of the parent setting up the required GSL dependency for UnoDB to +# use. Exact minimum supported versions will be added as necessary +find_package(Microsoft.GSL REQUIRED) + +# After the above packages have been found, setup UnoDB itself +add_subdirectory(.. unodb) add_executable(example_art example_art.cpp) -common_target_properties(example_art) target_link_libraries(example_art PRIVATE unodb) add_executable(example_art_stats example_art_stats.cpp) -common_target_properties(example_art_stats) target_link_libraries(example_art_stats PRIVATE unodb) add_executable(example_olc_art example_olc_art.cpp) -common_target_properties(example_olc_art) target_link_libraries(example_olc_art PRIVATE unodb) add_custom_target(examples - env ${SANITIZER_ENV} ./example_art - COMMAND env ${SANITIZER_ENV} ./example_art_stats - COMMAND env ${SANITIZER_ENV} ./example_olc_art - DEPENDS example_art example_art_stats example_olc_art) - -add_custom_target(valgrind_examples - COMMAND ${VALGRIND_COMMAND} ./example_art - COMMAND ${VALGRIND_COMMAND} ./example_art_stats - COMMAND ${VALGRIND_COMMAND} ./example_olc_art + env ./example_art + COMMAND env ./example_art_stats + COMMAND env ./example_olc_art DEPENDS example_art example_art_stats example_olc_art) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 189bbf92..8483419e 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -26,20 +26,22 @@ endfunction() add_library(db_test_utils STATIC db_test_utils.hpp db_test_utils.cpp) common_target_properties(db_test_utils) -target_link_libraries(db_test_utils PUBLIC unodb gtest_main gmock_main) +target_link_libraries(db_test_utils + PUBLIC unodb GTest::gtest_main GTest::gmock_main) set_clang_tidy_options(db_test_utils "${DO_CLANG_TIDY}") add_library(qsbr_test_utils STATIC qsbr_test_utils.hpp qsbr_test_utils.cpp qsbr_gtest_utils.hpp qsbr_gtest_utils.cpp) common_target_properties(qsbr_test_utils) -target_link_libraries(qsbr_test_utils PUBLIC gtest_main) +target_link_libraries(qsbr_test_utils PUBLIC GTest::gtest_main) target_link_libraries(qsbr_test_utils PRIVATE unodb_qsbr) set_clang_tidy_options(qsbr_test_utils "${DO_CLANG_TIDY}") function(ADD_TEST_TARGET TARGET) add_executable("${TARGET}" "${TARGET}.cpp") common_target_properties("${TARGET}") - target_link_libraries("${TARGET}" PRIVATE unodb_qsbr unodb_test gtest_main) + target_link_libraries("${TARGET}" + PRIVATE unodb_qsbr unodb_test GTest::gtest_main) set_clang_tidy_options("${TARGET}" "${DO_CLANG_TIDY}") add_sanitized_test(NAME "${TARGET}" COMMAND "${TARGET}") endfunction()