From fa057bd8767f176eddd29d0d8ff4cfd6dca54a20 Mon Sep 17 00:00:00 2001 From: Ayaz Salikhov Date: Tue, 16 Jun 2026 22:50:43 +0100 Subject: [PATCH] ci: Use Nix-based images for all workflows except pre-commit (#3098) --- .gersemi/definitions.cmake | 3 + .github/actions/build-identifier/action.yml | 53 +++ .github/actions/cache-key/action.yml | 41 -- .github/actions/cmake/action.yml | 15 - .github/actions/conan/action.yml | 11 +- .github/actions/set-compiler-env/action.yml | 34 ++ .github/dependabot.yml | 102 +---- .github/scripts/conan/generate_matrix.py | 13 +- .github/scripts/conan/init.sh | 48 --- .github/scripts/conan/regenerate_lockfile.sh | 2 +- .github/workflows/build.yml | 21 +- .github/workflows/check-libxrpl.yml | 20 +- .github/workflows/clang-tidy.yml | 17 +- .github/workflows/docs.yml | 2 +- .github/workflows/nightly.yml | 39 +- .github/workflows/release.yml | 16 +- .github/workflows/reusable-build-test.yml | 23 +- .github/workflows/reusable-build.yml | 61 +-- .github/workflows/reusable-release.yml | 2 +- .github/workflows/reusable-test.yml | 50 ++- .github/workflows/sanitizers.yml | 10 +- .github/workflows/update-docker-ci.yml | 364 ------------------ .github/workflows/upload-conan-deps.yml | 22 +- CMakeLists.txt | 43 +-- cmake/CheckCompiler.cmake | 27 -- cmake/PatchNixBinary.cmake | 55 +++ cmake/Sanitizers.cmake | 47 +++ cmake/Settings.cmake | 21 +- cmake/deps/libbacktrace.cmake | 2 +- conan.lock | 2 +- conan/global.conf | 7 + conan/init.sh | 30 ++ .../profiles}/apple-clang-17.profile | 0 conan/profiles/ci | 8 + conan/profiles/default | 28 ++ conan/profiles/sanitizers | 120 ++++++ conanfile.py | 5 +- docker/ci/Dockerfile | 132 ------- docker/ci/README.md | 26 -- docker/ci/conan/clang.profile | 12 - docker/ci/conan/gcc.profile | 11 - docker/ci/conan/global.conf | 2 - docker/ci/conan/sanitizer_template.profile | 37 -- docker/compilers/clang/Dockerfile | 32 -- docker/compilers/clang/README.md | 3 - docker/compilers/gcc/Dockerfile | 120 ------ docker/compilers/gcc/README.md | 3 - docker/compilers/gcc/control.m4 | 7 - docker/compilers/gcc/ld.so.conf | 2 - docker/develop/compose.yaml | 2 +- docker/tools/Dockerfile | 112 ------ docs/build-clio.md | 8 +- src/main/CMakeLists.txt | 16 +- src/rpc/CMakeLists.txt | 2 +- src/util/CMakeLists.txt | 2 +- src/util/async/AnyExecutionContext.hpp | 48 +-- src/util/async/AnyOperation.hpp | 1 + src/util/async/AnyStrand.hpp | 32 +- src/util/async/Error.hpp | 8 - src/util/async/impl/Any.hpp | 41 ++ src/util/async/impl/ErasedOperation.hpp | 10 +- src/util/requests/impl/SslContext.cpp | 35 +- tests/common/util/MockExecutionContext.hpp | 27 +- tests/common/util/MockStrand.hpp | 23 +- tests/integration/CMakeLists.txt | 2 + tests/unit/CMakeLists.txt | 2 + .../util/async/AnyExecutionContextTests.cpp | 93 ++--- tests/unit/util/async/AnyOperationTests.cpp | 13 +- tests/unit/util/async/AnyStrandTests.cpp | 49 +-- 69 files changed, 846 insertions(+), 1431 deletions(-) create mode 100644 .github/actions/build-identifier/action.yml delete mode 100644 .github/actions/cache-key/action.yml create mode 100644 .github/actions/set-compiler-env/action.yml delete mode 100755 .github/scripts/conan/init.sh delete mode 100644 .github/workflows/update-docker-ci.yml create mode 100644 cmake/PatchNixBinary.cmake create mode 100644 cmake/Sanitizers.cmake create mode 100644 conan/global.conf create mode 100755 conan/init.sh rename {.github/scripts/conan => conan/profiles}/apple-clang-17.profile (100%) create mode 100644 conan/profiles/ci create mode 100644 conan/profiles/default create mode 100644 conan/profiles/sanitizers delete mode 100644 docker/ci/Dockerfile delete mode 100644 docker/ci/README.md delete mode 100644 docker/ci/conan/clang.profile delete mode 100644 docker/ci/conan/gcc.profile delete mode 100644 docker/ci/conan/global.conf delete mode 100644 docker/ci/conan/sanitizer_template.profile delete mode 100644 docker/compilers/clang/Dockerfile delete mode 100644 docker/compilers/clang/README.md delete mode 100644 docker/compilers/gcc/Dockerfile delete mode 100644 docker/compilers/gcc/README.md delete mode 100644 docker/compilers/gcc/control.m4 delete mode 100644 docker/compilers/gcc/ld.so.conf delete mode 100644 docker/tools/Dockerfile create mode 100644 src/util/async/impl/Any.hpp diff --git a/.gersemi/definitions.cmake b/.gersemi/definitions.cmake index 1e6de7884..ef974a6cc 100644 --- a/.gersemi/definitions.cmake +++ b/.gersemi/definitions.cmake @@ -17,3 +17,6 @@ endfunction() function(append_coverage_compiler_flags_to_target name mode) endfunction() + +function(patch_nix_binary target) +endfunction() diff --git a/.github/actions/build-identifier/action.yml b/.github/actions/build-identifier/action.yml new file mode 100644 index 000000000..7c085883a --- /dev/null +++ b/.github/actions/build-identifier/action.yml @@ -0,0 +1,53 @@ +name: Build identifier +description: Generate a unique build identifier and the ccache key derived from it + +inputs: + build_type: + description: Current build type (e.g. Release, Debug) + required: true + compiler: + description: Compiler used for the build (e.g. gcc, clang) + required: true + code_coverage: + description: Whether code coverage is on + required: true + sanitizers: + description: Sanitizer to enable, read by the 'sanitizers' conan profile (e.g. 'address', 'thread', 'undefinedbehavior') + required: true + +outputs: + build_identifier: + description: Unique identifier for the build configuration (without commit) + value: ${{ steps.build_identifier.outputs.build_identifier }} + cache_key: + description: ccache key, the build identifier suffixed with the common ancestor commit + value: ${{ steps.build_identifier.outputs.build_identifier }}-${{ steps.git_common_ancestor.outputs.commit }} + +runs: + using: composite + steps: + - name: Find common commit + id: git_common_ancestor + uses: ./.github/actions/git-common-ancestor + + - name: Build identifier + id: build_identifier + shell: bash + env: + RUNNER_OS: ${{ runner.os }} + BUILD_TYPE: ${{ inputs.build_type }} + COMPILER: ${{ inputs.compiler }} + CODE_COVERAGE: ${{ inputs.code_coverage }} + SANITIZERS: ${{ inputs.sanitizers }} + run: | + # Keep the legacy "__" layout so standard artifact + # names (e.g. clio_server_Linux_Release_gcc) stay backwards compatible. + # Sanitizer/coverage builds get extra suffixes and may differ from old names. + BUILD_IDENTIFIER="${RUNNER_OS}_${BUILD_TYPE}_${COMPILER}" + if [ "${CODE_COVERAGE}" == "true" ]; then + BUILD_IDENTIFIER+="_code_coverage" + fi + if [ -n "${SANITIZERS}" ]; then + BUILD_IDENTIFIER+="_${SANITIZERS}" + fi + echo "build_identifier=${BUILD_IDENTIFIER}" >>"${GITHUB_OUTPUT}" diff --git a/.github/actions/cache-key/action.yml b/.github/actions/cache-key/action.yml deleted file mode 100644 index a47b05163..000000000 --- a/.github/actions/cache-key/action.yml +++ /dev/null @@ -1,41 +0,0 @@ -name: Cache key -description: Generate cache key for ccache - -inputs: - conan_profile: - description: Conan profile name - required: true - build_type: - description: Current build type (e.g. Release, Debug) - required: true - default: Release - code_coverage: - description: Whether code coverage is on - required: true - default: "false" - -outputs: - key: - description: Generated cache key for ccache - value: ${{ steps.key_without_commit.outputs.key }}-${{ steps.git_common_ancestor.outputs.commit }} - restore_keys: - description: Cache restore keys for fallback - value: ${{ steps.key_without_commit.outputs.key }} - -runs: - using: composite - steps: - - name: Find common commit - id: git_common_ancestor - uses: ./.github/actions/git-common-ancestor - - - name: Set cache key without commit - id: key_without_commit - shell: bash - env: - RUNNER_OS: ${{ runner.os }} - BUILD_TYPE: ${{ inputs.build_type }} - CODE_COVERAGE: ${{ inputs.code_coverage == 'true' && '-code_coverage' || '' }} - CONAN_PROFILE: ${{ inputs.conan_profile }} - run: | - echo "key=clio-ccache-${RUNNER_OS}-${BUILD_TYPE}${CODE_COVERAGE}-${CONAN_PROFILE}-develop" >>"${GITHUB_OUTPUT}" diff --git a/.github/actions/cmake/action.yml b/.github/actions/cmake/action.yml index 3e5f925d3..d3e7dabca 100644 --- a/.github/actions/cmake/action.yml +++ b/.github/actions/cmake/action.yml @@ -6,9 +6,6 @@ inputs: description: Build directory required: false default: "build" - conan_profile: - description: Conan profile name - required: true build_type: description: Build type for third-party libraries and clio. Could be 'Release', 'Debug' required: true @@ -25,10 +22,6 @@ inputs: description: Whether to enable code coverage required: true default: "false" - static: - description: Whether Clio is to be statically linked - required: true - default: "false" time_trace: description: Whether to enable compiler trace reports required: true @@ -50,15 +43,9 @@ runs: env: BUILD_DIR: "${{ inputs.build_dir }}" BUILD_TYPE: "${{ inputs.build_type }}" - SANITIZER_OPTION: |- - ${{ endsWith(inputs.conan_profile, '.asan') && '-Dsan=address' || - endsWith(inputs.conan_profile, '.tsan') && '-Dsan=thread' || - endsWith(inputs.conan_profile, '.ubsan') && '-Dsan=undefined' || - '' }} INTEGRATION_TESTS: "${{ inputs.integration_tests == 'true' && 'ON' || 'OFF' }}" BENCHMARK: "${{ inputs.benchmark == 'true' && 'ON' || 'OFF' }}" COVERAGE: "${{ inputs.code_coverage == 'true' && 'ON' || 'OFF' }}" - STATIC: "${{ inputs.static == 'true' && 'ON' || 'OFF' }}" TIME_TRACE: "${{ inputs.time_trace == 'true' && 'ON' || 'OFF' }}" PACKAGE: "${{ inputs.package == 'true' && 'ON' || 'OFF' }}" # GitHub creates a merge commit for a PR @@ -81,11 +68,9 @@ runs: -G Ninja \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ - "${SANITIZER_OPTION}" \ -Dtests=ON \ -Dintegration_tests="${INTEGRATION_TESTS}" \ -Dbenchmark="${BENCHMARK}" \ -Dcoverage="${COVERAGE}" \ - -Dstatic="${STATIC}" \ -Dtime_trace="${TIME_TRACE}" \ -Dpackage="${PACKAGE}" diff --git a/.github/actions/conan/action.yml b/.github/actions/conan/action.yml index 1acf95e75..4c930a59d 100644 --- a/.github/actions/conan/action.yml +++ b/.github/actions/conan/action.yml @@ -6,9 +6,6 @@ inputs: description: Build directory required: false default: "build" - conan_profile: - description: Conan profile name - required: true force_conan_source_build: description: Whether conan should build all dependencies from source required: true @@ -17,6 +14,10 @@ inputs: description: Build type for third-party libraries and clio. Could be 'Release', 'Debug' required: true default: "Release" + sanitizers: + description: Sanitizer to enable, read by the 'sanitizers' conan profile (e.g. 'address', 'thread', 'undefinedbehavior') + required: false + default: "" runs: using: composite @@ -27,11 +28,11 @@ runs: BUILD_DIR: "${{ inputs.build_dir }}" CONAN_BUILD_OPTION: "${{ inputs.force_conan_source_build == 'true' && '*' || 'missing' }}" BUILD_TYPE: "${{ inputs.build_type }}" - CONAN_PROFILE: "${{ inputs.conan_profile }}" + SANITIZERS: "${{ inputs.sanitizers }}" run: | conan \ install . \ -of "${BUILD_DIR}" \ -b "${CONAN_BUILD_OPTION}" \ -s "build_type=${BUILD_TYPE}" \ - --profile:all "${CONAN_PROFILE}" + --profile:all ci diff --git a/.github/actions/set-compiler-env/action.yml b/.github/actions/set-compiler-env/action.yml new file mode 100644 index 000000000..a16dde2b3 --- /dev/null +++ b/.github/actions/set-compiler-env/action.yml @@ -0,0 +1,34 @@ +name: Set compiler environment +description: "Set CC and CXX environment variables for the given compiler." + +inputs: + compiler: + description: 'The compiler to use ("gcc" or "clang").' + required: true + +runs: + using: composite + + steps: + - name: Set CC and CXX for gcc + if: ${{ inputs.compiler == 'gcc' }} + shell: bash + run: | + echo "CC=gcc" >>"${GITHUB_ENV}" + echo "CXX=g++" >>"${GITHUB_ENV}" + + - name: Set CC and CXX for clang + if: ${{ inputs.compiler == 'clang' }} + shell: bash + run: | + echo "CC=clang" >>"${GITHUB_ENV}" + echo "CXX=clang++" >>"${GITHUB_ENV}" + + - name: Fail on unknown compiler + if: ${{ inputs.compiler != 'gcc' && inputs.compiler != 'clang' }} + shell: bash + env: + COMPILER: ${{ inputs.compiler }} + run: | + echo "Unknown compiler: $COMPILER" >&2 + exit 1 diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 6f8e2ae1c..cef6f5a24 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,98 +1,16 @@ version: 2 updates: - package-ecosystem: github-actions - directory: / - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/build-clio/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/build-docker-image/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/cmake/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/code-coverage/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/conan/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/git-common-ancestor/ - schedule: - interval: weekly - day: monday - time: "04:00" - timezone: Etc/GMT - reviewers: - - XRPLF/clio-dev-team - commit-message: - prefix: "ci: [DEPENDABOT] " - target-branch: develop - - - package-ecosystem: github-actions - directory: .github/actions/cache-key/ + directories: + - / + - .github/actions/build-clio/ + - .github/actions/build-docker-image/ + - .github/actions/build-identifier/ + - .github/actions/cmake/ + - .github/actions/code-coverage/ + - .github/actions/conan/ + - .github/actions/git-common-ancestor/ + - .github/actions/set-compiler-env/ schedule: interval: weekly day: monday diff --git a/.github/scripts/conan/generate_matrix.py b/.github/scripts/conan/generate_matrix.py index 213ea23d3..fe9ba32a9 100755 --- a/.github/scripts/conan/generate_matrix.py +++ b/.github/scripts/conan/generate_matrix.py @@ -3,9 +3,7 @@ import itertools import json LINUX_OS = ["heavy", "heavy-arm64"] -LINUX_CONTAINERS = [ - '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' -] +LINUX_CONTAINERS = ['{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }'] LINUX_COMPILERS = ["gcc", "clang"] MACOS_OS = ["macos15"] @@ -13,7 +11,10 @@ MACOS_CONTAINERS = [""] MACOS_COMPILERS = ["apple-clang"] BUILD_TYPES = ["Release", "Debug"] -SANITIZER_EXT = [".asan", ".tsan", ".ubsan", ""] + +# Values of the `SANITIZERS` environment variable read by the `sanitizers` conan +# profile. An empty string builds without any sanitizers. +SANITIZERS = ["address", "thread", "undefinedbehavior", ""] def generate_matrix(): @@ -23,13 +24,13 @@ def generate_matrix(): itertools.product(LINUX_OS, LINUX_CONTAINERS, LINUX_COMPILERS), itertools.product(MACOS_OS, MACOS_CONTAINERS, MACOS_COMPILERS), ): - for sanitizer_ext, build_type in itertools.product(SANITIZER_EXT, BUILD_TYPES): + for sanitizers, build_type in itertools.product(SANITIZERS, BUILD_TYPES): configurations.append( { "os": os, "container": container, "compiler": compiler, - "sanitizer_ext": sanitizer_ext, + "sanitizers": sanitizers, "build_type": build_type, } ) diff --git a/.github/scripts/conan/init.sh b/.github/scripts/conan/init.sh deleted file mode 100755 index dba73a6ec..000000000 --- a/.github/scripts/conan/init.sh +++ /dev/null @@ -1,48 +0,0 @@ -#!/bin/bash - -set -ex - -CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" -REPO_DIR="$(cd "$CURRENT_DIR/../../../" && pwd)" - -CONAN_DIR="${CONAN_HOME:-$HOME/.conan2}" -PROFILES_DIR="$CONAN_DIR/profiles" - -# When developers' compilers are updated, these profiles might be different -if [[ -z "$CI" ]]; then - APPLE_CLANG_PROFILE="$CURRENT_DIR/apple-clang-17.profile" -else - APPLE_CLANG_PROFILE="$CURRENT_DIR/apple-clang-17.profile" -fi - -GCC_PROFILE="$REPO_DIR/docker/ci/conan/gcc.profile" -CLANG_PROFILE="$REPO_DIR/docker/ci/conan/clang.profile" - -SANITIZER_TEMPLATE_FILE="$REPO_DIR/docker/ci/conan/sanitizer_template.profile" - -rm -rf "$CONAN_DIR" - -conan remote add --index 0 xrplf https://conan.ripplex.io - -cp "$REPO_DIR/docker/ci/conan/global.conf" "$CONAN_DIR/global.conf" - -create_profile_with_sanitizers() { - profile_name="$1" - profile_source="$2" - - cp "$profile_source" "$PROFILES_DIR/$profile_name" - cp "$SANITIZER_TEMPLATE_FILE" "$PROFILES_DIR/$profile_name.asan" - cp "$SANITIZER_TEMPLATE_FILE" "$PROFILES_DIR/$profile_name.tsan" - cp "$SANITIZER_TEMPLATE_FILE" "$PROFILES_DIR/$profile_name.ubsan" -} - -mkdir -p "$PROFILES_DIR" - -if [[ "$(uname)" == "Darwin" ]]; then - create_profile_with_sanitizers "apple-clang" "$APPLE_CLANG_PROFILE" - echo "include(apple-clang)" >"$PROFILES_DIR/default" -else - create_profile_with_sanitizers "clang" "$CLANG_PROFILE" - create_profile_with_sanitizers "gcc" "$GCC_PROFILE" - echo "include(gcc)" >"$PROFILES_DIR/default" -fi diff --git a/.github/scripts/conan/regenerate_lockfile.sh b/.github/scripts/conan/regenerate_lockfile.sh index 2024c80d6..e237bbfdd 100755 --- a/.github/scripts/conan/regenerate_lockfile.sh +++ b/.github/scripts/conan/regenerate_lockfile.sh @@ -22,4 +22,4 @@ rm -f conan.lock # Create a new lockfile that is compatible with macOS. # It should also work on Linux. conan lock create . \ - --profile:all=.github/scripts/conan/apple-clang-17.profile + --profile:all=./conan/profiles/apple-clang-17.profile diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index c309d3a11..573058345 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,30 +45,24 @@ jobs: fail-fast: false matrix: os: [heavy] - conan_profile: [gcc, clang] + compiler: [gcc, clang] build_type: [Release, Debug] - container: - [ - '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }', - ] - static: [true] + container: ['{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }'] include: - os: macos15 - conan_profile: apple-clang + compiler: apple-clang build_type: Release container: "" - static: false uses: ./.github/workflows/reusable-build-test.yml with: runs_on: ${{ matrix.os }} container: ${{ matrix.container }} - conan_profile: ${{ matrix.conan_profile }} + compiler: ${{ matrix.compiler }} build_type: ${{ matrix.build_type }} download_ccache: true upload_ccache: true - static: ${{ matrix.static }} run_unit_tests: true run_integration_tests: false upload_clio_server: true @@ -79,13 +73,12 @@ jobs: uses: ./.github/workflows/reusable-build.yml with: runs_on: heavy - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' - conan_profile: gcc + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' + compiler: gcc build_type: Debug download_ccache: true upload_ccache: true code_coverage: true - static: true upload_clio_server: false targets: all analyze_build_time: false @@ -97,7 +90,7 @@ jobs: needs: build-and-test runs-on: heavy container: - image: ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 diff --git a/.github/workflows/check-libxrpl.yml b/.github/workflows/check-libxrpl.yml index 26f8dda38..f3d2b298a 100644 --- a/.github/workflows/check-libxrpl.yml +++ b/.github/workflows/check-libxrpl.yml @@ -10,7 +10,7 @@ concurrency: cancel-in-progress: true env: - CONAN_PROFILE: gcc + COMPILER: gcc defaults: run: @@ -21,7 +21,7 @@ jobs: name: Build Clio / `libXRPL ${{ github.event.client_payload.version }}` runs-on: heavy container: - image: ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 @@ -36,6 +36,14 @@ jobs: - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 + - name: Set compiler environment + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ env.COMPILER }} + + - name: Setup conan + run: conan/init.sh + - name: Update libXRPL version requirement run: | sed -i.bak -E "s|'xrpl/[a-zA-Z0-9\\.\\-]+'|'xrpl/${{ github.event.client_payload.conan_ref }}'|g" conanfile.py @@ -43,17 +51,13 @@ jobs: - name: Update conan lockfile run: | - conan lock create . --profile:all ${{ env.CONAN_PROFILE }} + conan lock create . --profile:all ci - name: Run conan uses: ./.github/actions/conan - with: - conan_profile: ${{ env.CONAN_PROFILE }} - name: Run CMake uses: ./.github/actions/cmake - with: - conan_profile: ${{ env.CONAN_PROFILE }} - name: Build Clio uses: ./.github/actions/build-clio @@ -72,7 +76,7 @@ jobs: needs: build runs-on: heavy container: - image: ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 steps: - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 diff --git a/.github/workflows/clang-tidy.yml b/.github/workflows/clang-tidy.yml index 311f7b1f0..6e40c4805 100644 --- a/.github/workflows/clang-tidy.yml +++ b/.github/workflows/clang-tidy.yml @@ -32,8 +32,7 @@ env: DIFF_FILE: clang-tidy-git-diff.txt ISSUE_FILE: clang-tidy-issue.md - CONAN_PROFILE: clang - LLVM_TOOLS_VERSION: 21 + COMPILER: clang defaults: run: @@ -52,7 +51,7 @@ jobs: if: ${{ always() && !cancelled() && (github.event_name != 'pull_request' || needs.determine-files.outputs.cpp_changed_files != '' || needs.determine-files.outputs.clang_tidy_config_changed == 'true') }} runs-on: heavy container: - image: ghcr.io/xrplf/clio-ci:f174b47f4909ae41b80406d836ab52adc39eacc6 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 permissions: contents: write @@ -70,18 +69,24 @@ jobs: - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 + - name: Set compiler environment + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ env.COMPILER }} + + - name: Setup conan + run: conan/init.sh + - name: Run conan uses: ./.github/actions/conan with: build_dir: ${{ env.BUILD_DIR }} - conan_profile: ${{ env.CONAN_PROFILE }} build_type: ${{ env.BUILD_TYPE }} - name: Run CMake uses: ./.github/actions/cmake with: build_dir: ${{ env.BUILD_DIR }} - conan_profile: ${{ env.CONAN_PROFILE }} build_type: ${{ env.BUILD_TYPE }} - name: Get number of processors @@ -95,7 +100,7 @@ jobs: TARGETS: ${{ (needs.determine-files.outputs.clang_tidy_config_changed != 'true' && github.event_name == 'pull_request') && needs.determine-files.outputs.cpp_changed_files || 'benchmarks src tests' }} run: | set -o pipefail - run-clang-tidy-${{ env.LLVM_TOOLS_VERSION }} -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}" + run-clang-tidy -j ${{ steps.nproc.outputs.nproc }} -p "${BUILD_DIR}" -quiet -fix -allow-no-checks ${TARGETS} 2>&1 | tee "${OUTPUT_FILE}" - name: Print errors if: ${{ steps.run_clang_tidy.outcome != 'success' }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 4bb885794..e5e73eccc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -18,7 +18,7 @@ jobs: build: runs-on: ubuntu-latest container: - image: ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 steps: - name: Checkout diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 2cbad9218..7f4f5ed6a 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -48,32 +48,29 @@ jobs: matrix: include: - os: macos15 - conan_profile: apple-clang + compiler: apple-clang build_type: Release - static: false - os: heavy - conan_profile: gcc + compiler: gcc build_type: Release - static: true - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' - os: heavy - conan_profile: gcc + compiler: gcc build_type: Debug - static: true - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' - os: heavy - conan_profile: gcc.ubsan + compiler: gcc + sanitizers: undefinedbehavior build_type: Release - static: false - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' uses: ./.github/workflows/reusable-build-test.yml with: runs_on: ${{ matrix.os }} container: ${{ matrix.container }} - conan_profile: ${{ matrix.conan_profile }} + compiler: ${{ matrix.compiler }} + sanitizers: ${{ matrix.sanitizers }} build_type: ${{ matrix.build_type }} - static: ${{ matrix.static }} run_unit_tests: true run_integration_tests: true upload_clio_server: true @@ -88,13 +85,12 @@ jobs: uses: ./.github/workflows/reusable-build.yml with: runs_on: heavy - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' - conan_profile: gcc + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' + compiler: gcc build_type: Release download_ccache: false upload_ccache: false code_coverage: false - static: true upload_clio_server: false package: true version: nightly-${{ needs.get_date.outputs.date }} @@ -110,23 +106,20 @@ jobs: matrix: include: - os: heavy - conan_profile: clang - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' - static: true + compiler: clang + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' - os: macos15 - conan_profile: apple-clang + compiler: apple-clang container: "" - static: false uses: ./.github/workflows/reusable-build.yml with: runs_on: ${{ matrix.os }} container: ${{ matrix.container }} - conan_profile: ${{ matrix.conan_profile }} + compiler: ${{ matrix.compiler }} build_type: Release download_ccache: false upload_ccache: false code_coverage: false - static: ${{ matrix.static }} upload_clio_server: false targets: all analyze_build_time: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99bd26534..77067ef97 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,22 +22,19 @@ jobs: matrix: include: - os: macos15 - conan_profile: apple-clang + compiler: apple-clang build_type: Release - static: false - os: heavy - conan_profile: gcc + compiler: gcc build_type: Release - static: true - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' uses: ./.github/workflows/reusable-build-test.yml with: runs_on: ${{ matrix.os }} container: ${{ matrix.container }} - conan_profile: ${{ matrix.conan_profile }} + compiler: ${{ matrix.compiler }} build_type: ${{ matrix.build_type }} - static: ${{ matrix.static }} run_unit_tests: true run_integration_tests: true upload_clio_server: true @@ -51,13 +48,12 @@ jobs: uses: ./.github/workflows/reusable-build.yml with: runs_on: heavy - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' - conan_profile: gcc + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' + compiler: gcc build_type: Release download_ccache: false upload_ccache: false code_coverage: false - static: true upload_clio_server: false package: true version: ${{ github.event_name == 'push' && github.ref_name || '' }} diff --git a/.github/workflows/reusable-build-test.yml b/.github/workflows/reusable-build-test.yml index d3cbe543d..41fb06b38 100644 --- a/.github/workflows/reusable-build-test.yml +++ b/.github/workflows/reusable-build-test.yml @@ -13,11 +13,17 @@ on: required: true type: string - conan_profile: - description: Conan profile to use + compiler: + description: 'Compiler to build with ("gcc", "clang" or "apple-clang")' required: true type: string + sanitizers: + description: 'Sanitizers to enable ("address", "thread", "undefinedbehavior" or empty)' + required: false + type: string + default: "" + build_type: description: Build type required: true @@ -35,12 +41,6 @@ on: type: boolean default: false - static: - description: Whether to build static binaries - required: true - type: boolean - default: true - run_unit_tests: description: Whether to run unit tests required: true @@ -81,12 +81,12 @@ jobs: with: runs_on: ${{ inputs.runs_on }} container: ${{ inputs.container }} - conan_profile: ${{ inputs.conan_profile }} + compiler: ${{ inputs.compiler }} + sanitizers: ${{ inputs.sanitizers }} build_type: ${{ inputs.build_type }} download_ccache: ${{ inputs.download_ccache }} upload_ccache: ${{ inputs.upload_ccache }} code_coverage: false - static: ${{ inputs.static }} upload_clio_server: ${{ inputs.upload_clio_server }} targets: ${{ inputs.targets }} analyze_build_time: false @@ -99,7 +99,8 @@ jobs: with: runs_on: ${{ inputs.runs_on }} container: ${{ inputs.container }} - conan_profile: ${{ inputs.conan_profile }} + compiler: ${{ inputs.compiler }} + sanitizers: ${{ inputs.sanitizers }} build_type: ${{ inputs.build_type }} run_unit_tests: ${{ inputs.run_unit_tests }} run_integration_tests: ${{ inputs.run_integration_tests }} diff --git a/.github/workflows/reusable-build.yml b/.github/workflows/reusable-build.yml index 998c26713..93f04528f 100644 --- a/.github/workflows/reusable-build.yml +++ b/.github/workflows/reusable-build.yml @@ -13,11 +13,17 @@ on: required: true type: string - conan_profile: - description: Conan profile to use + compiler: + description: 'Compiler to build with ("gcc", "clang" or "apple-clang")' required: true type: string + sanitizers: + description: 'Sanitizers to enable ("address", "thread", "undefinedbehavior" or empty)' + required: false + type: string + default: "" + build_type: description: Build type required: true @@ -40,11 +46,6 @@ on: required: true type: boolean - static: - description: Whether to build static binaries - required: true - type: boolean - upload_clio_server: description: Whether to upload clio_server required: true @@ -102,40 +103,44 @@ jobs: - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 - - name: Setup conan on macOS - if: ${{ runner.os == 'macOS' }} - run: ./.github/scripts/conan/init.sh - - - name: Generate cache key - uses: ./.github/actions/cache-key - id: cache_key + - name: Set compiler environment + if: ${{ runner.os == 'Linux' }} + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ inputs.compiler }} + + - name: Setup conan + run: conan/init.sh + + - name: Generate build identifier + uses: ./.github/actions/build-identifier + id: build_identifier with: - conan_profile: ${{ inputs.conan_profile }} build_type: ${{ inputs.build_type }} + compiler: ${{ inputs.compiler }} code_coverage: ${{ inputs.code_coverage }} + sanitizers: ${{ inputs.sanitizers }} - name: Restore ccache cache if: ${{ inputs.download_ccache && github.ref != 'refs/heads/develop' }} uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ env.CCACHE_DIR }} - key: ${{ steps.cache_key.outputs.key }} + key: ${{ steps.build_identifier.outputs.cache_key }} restore-keys: | - ${{ steps.cache_key.outputs.restore_keys }} + ${{ steps.build_identifier.outputs.build_identifier }} - name: Run conan uses: ./.github/actions/conan with: - conan_profile: ${{ inputs.conan_profile }} build_type: ${{ inputs.build_type }} + sanitizers: ${{ inputs.sanitizers }} - name: Run CMake uses: ./.github/actions/cmake with: - conan_profile: ${{ inputs.conan_profile }} build_type: ${{ inputs.build_type }} code_coverage: ${{ inputs.code_coverage }} - static: ${{ inputs.static }} time_trace: ${{ inputs.analyze_build_time }} package: ${{ inputs.package }} version: ${{ inputs.version }} @@ -156,7 +161,7 @@ jobs: if: ${{ inputs.analyze_build_time }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: build_time_report_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: build_time_report_${{ steps.build_identifier.outputs.build_identifier }} path: build_time_report.txt - name: Show ccache's statistics and zero it @@ -170,42 +175,42 @@ jobs: uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5 with: path: ${{ env.CCACHE_DIR }} - key: ${{ steps.cache_key.outputs.key }} + key: ${{ steps.build_identifier.outputs.cache_key }} - name: Strip unit_tests - if: ${{ !endsWith(inputs.conan_profile, 'san') && !inputs.code_coverage && !inputs.analyze_build_time }} + if: ${{ inputs.sanitizers == '' && !inputs.code_coverage && !inputs.analyze_build_time }} run: strip build/clio_tests - name: Strip integration_tests - if: ${{ !endsWith(inputs.conan_profile, 'san') && !inputs.code_coverage && !inputs.analyze_build_time }} + if: ${{ inputs.sanitizers == '' && !inputs.code_coverage && !inputs.analyze_build_time }} run: strip build/clio_integration_tests - name: Upload clio_server if: ${{ inputs.upload_clio_server && !inputs.code_coverage && !inputs.analyze_build_time }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: clio_server_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: clio_server_${{ steps.build_identifier.outputs.build_identifier }} path: build/clio_server - name: Upload clio_tests if: ${{ !inputs.code_coverage && !inputs.analyze_build_time && !inputs.package }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: clio_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: clio_tests_${{ steps.build_identifier.outputs.build_identifier }} path: build/clio_tests - name: Upload clio_integration_tests if: ${{ !inputs.code_coverage && !inputs.analyze_build_time && !inputs.package }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: clio_integration_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: clio_integration_tests_${{ steps.build_identifier.outputs.build_identifier }} path: build/clio_integration_tests - name: Upload Clio Linux package if: ${{ inputs.package }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: - name: clio_deb_package_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: clio_deb_package_${{ steps.build_identifier.outputs.build_identifier }} path: build/*.deb # This is run as part of the build job, because it requires the following: diff --git a/.github/workflows/reusable-release.yml b/.github/workflows/reusable-release.yml index 50119212e..0ba24524b 100644 --- a/.github/workflows/reusable-release.yml +++ b/.github/workflows/reusable-release.yml @@ -46,7 +46,7 @@ jobs: release: runs-on: heavy container: - image: ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 env: GH_REPO: ${{ github.repository }} GH_TOKEN: ${{ github.token }} diff --git a/.github/workflows/reusable-test.yml b/.github/workflows/reusable-test.yml index 73feb2dca..b4708489a 100644 --- a/.github/workflows/reusable-test.yml +++ b/.github/workflows/reusable-test.yml @@ -13,11 +13,17 @@ on: required: true type: string - conan_profile: - description: Conan profile to use + compiler: + description: 'Compiler the binaries were built with ("gcc", "clang" or "apple-clang")' required: true type: string + sanitizers: + description: 'Sanitizers the binaries were built with ("address", "thread", "undefinedbehavior" or empty)' + required: false + type: string + default: "" + build_type: description: Build type required: true @@ -54,9 +60,19 @@ jobs: with: fetch-depth: 0 + - name: Generate build identifier + uses: ./.github/actions/build-identifier + id: build_identifier + with: + build_type: ${{ inputs.build_type }} + compiler: ${{ inputs.compiler }} + # code_coverage is run inside build environment + code_coverage: false + sanitizers: ${{ inputs.sanitizers }} + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: clio_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: clio_tests_${{ steps.build_identifier.outputs.build_identifier }} - name: Make clio_tests executable run: chmod +x ./clio_tests @@ -67,11 +83,11 @@ jobs: run: ./clio_tests - name: Create an issue - if: ${{ steps.run_clio_tests.outcome == 'failure' && endsWith(inputs.conan_profile, 'san') }} + if: ${{ steps.run_clio_tests.outcome == 'failure' && inputs.sanitizers != '' }} uses: XRPLF/actions/create-issue@2b8bc36af85b88bca0dd7bfac2e2dc05f94ad712 with: - title: "[${{ inputs.conan_profile }}] reported issues" - body: "Clio tests failed one or more sanitizer checks when built with `${{ inputs.conan_profile }}`." + title: "[${{ inputs.compiler }} ${{ inputs.sanitizers }}] reported issues" + body: "Clio tests failed one or more sanitizers checks when built with `${{ inputs.compiler }}` and the `${{ inputs.sanitizers }}` sanitizers." labels: "bug" assignees: "godexsoft,kuznetsss,mathbunnyru" @@ -100,6 +116,20 @@ jobs: if: ${{ runner.os == 'macOS' }} uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 + - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 + with: + fetch-depth: 0 + + - name: Generate build identifier + uses: ./.github/actions/build-identifier + id: build_identifier + with: + build_type: ${{ inputs.build_type }} + compiler: ${{ inputs.compiler }} + # code_coverage is run inside build environment + code_coverage: false + sanitizers: ${{ inputs.sanitizers }} + - name: Delete and start colima (macOS) # This is a temporary workaround for colima issues on macOS runners if: ${{ runner.os == 'macOS' }} @@ -107,6 +137,12 @@ jobs: colima delete --force colima start + - name: Remove leftover scylladb container (macOS) + # A previous run that didn't clean up (e.g. a cancelled job) can leave the + # container behind, otherwise the name is still taken on the persistent runner. + if: ${{ runner.os == 'macOS' }} + run: docker rm --force scylladb || true + - name: Spin up scylladb (macOS) if: ${{ runner.os == 'macOS' }} timeout-minutes: 1 @@ -132,7 +168,7 @@ jobs: - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: - name: clio_integration_tests_${{ runner.os }}_${{ inputs.build_type }}_${{ inputs.conan_profile }} + name: clio_integration_tests_${{ steps.build_identifier.outputs.build_identifier }} - name: Run clio_integration_tests run: | diff --git a/.github/workflows/sanitizers.yml b/.github/workflows/sanitizers.yml index 2e9a05216..f57c5d787 100644 --- a/.github/workflows/sanitizers.yml +++ b/.github/workflows/sanitizers.yml @@ -19,7 +19,7 @@ on: - conanfile.py - conan.lock - "cmake/**" - # We don't run sanitizer on code change, because it takes too long + # We don't run sanitizers on code change, because it takes too long # - "src/**" # - "tests/**" @@ -36,18 +36,18 @@ jobs: fail-fast: false matrix: compiler: [gcc, clang] - sanitizer_ext: [.asan, .tsan, .ubsan] + sanitizers: [address, thread, undefinedbehavior] build_type: [Release, Debug] uses: ./.github/workflows/reusable-build-test.yml with: runs_on: heavy - container: '{ "image": "ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696" }' + container: '{ "image": "ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553" }' download_ccache: false upload_ccache: false - conan_profile: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }} + compiler: ${{ matrix.compiler }} + sanitizers: ${{ matrix.sanitizers }} build_type: ${{ matrix.build_type }} - static: false run_unit_tests: true run_integration_tests: false upload_clio_server: false diff --git a/.github/workflows/update-docker-ci.yml b/.github/workflows/update-docker-ci.yml deleted file mode 100644 index 86cc61e8e..000000000 --- a/.github/workflows/update-docker-ci.yml +++ /dev/null @@ -1,364 +0,0 @@ -name: Update CI docker image - -on: - pull_request: - paths: - - .github/workflows/update-docker-ci.yml - - - ".github/actions/build-docker-image/**" - - - "docker/**" - - "!docker/clio/**" - - "!docker/develop/**" - push: - branches: [develop] - paths: - - .github/workflows/update-docker-ci.yml - - - ".github/actions/build-docker-image/**" - - - "docker/**" - - "!docker/clio/**" - - "!docker/develop/**" - workflow_dispatch: - -concurrency: - # Only matches runs for the current workflow - matches against branch & tags - group: ${{ github.workflow }}-${{ github.ref }} - # We want to execute all builds sequentially in develop - cancel-in-progress: false - -env: - CLANG_MAJOR_VERSION: 19 - GCC_MAJOR_VERSION: 15 - GCC_VERSION: 15.2.0 - -defaults: - run: - shell: bash - -jobs: - repo: - name: Calculate repo name - runs-on: ubuntu-latest - outputs: - GHCR_REPO: ${{ steps.set-ghcr-repo.outputs.GHCR_REPO }} - - steps: - - name: Set GHCR_REPO - id: set-ghcr-repo - run: | - echo "GHCR_REPO=$(echo ghcr.io/${{ github.repository_owner }} | tr '[:upper:]' '[:lower:]')" >>${GITHUB_OUTPUT} - - gcc-amd64: - name: Build and push GCC docker image (amd64) - runs-on: heavy - needs: repo - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/compilers/gcc/**" - - - uses: ./.github/actions/build-docker-image - if: ${{ steps.changed-files.outputs.any_changed == 'true' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} - DOCKERHUB_PW: ${{ secrets.DOCKERHUB_PW }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-gcc - ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_gcc' || '' }} - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/compilers/gcc - tags: | - type=raw,value=amd64-latest - type=raw,value=amd64-${{ env.GCC_MAJOR_VERSION }} - type=raw,value=amd64-${{ env.GCC_VERSION }} - type=raw,value=amd64-${{ github.sha }} - platforms: linux/amd64 - build_args: | - GCC_MAJOR_VERSION=${{ env.GCC_MAJOR_VERSION }} - GCC_VERSION=${{ env.GCC_VERSION }} - dockerhub_repo: ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_gcc' || '' }} - dockerhub_description: GCC compiler for XRPLF/clio. - - gcc-arm64: - name: Build and push GCC docker image (arm64) - runs-on: heavy-arm64 - needs: repo - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/compilers/gcc/**" - - - uses: ./.github/actions/build-docker-image - if: ${{ steps.changed-files.outputs.any_changed == 'true' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} - DOCKERHUB_PW: ${{ secrets.DOCKERHUB_PW }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-gcc - ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_gcc' || '' }} - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/compilers/gcc - tags: | - type=raw,value=arm64-latest - type=raw,value=arm64-${{ env.GCC_MAJOR_VERSION }} - type=raw,value=arm64-${{ env.GCC_VERSION }} - type=raw,value=arm64-${{ github.sha }} - platforms: linux/arm64 - build_args: | - GCC_MAJOR_VERSION=${{ env.GCC_MAJOR_VERSION }} - GCC_VERSION=${{ env.GCC_VERSION }} - dockerhub_repo: ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_gcc' || '' }} - dockerhub_description: GCC compiler for XRPLF/clio. - - gcc-merge: - name: Merge and push multi-arch GCC docker image - runs-on: heavy - needs: [repo, gcc-amd64, gcc-arm64] - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/compilers/gcc/**" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Login to GitHub Container Registry - if: ${{ github.event_name != 'pull_request' }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Login to DockerHub - if: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - username: ${{ secrets.DOCKERHUB_USER }} - password: ${{ secrets.DOCKERHUB_PW }} - - - name: Create and push multi-arch manifest - if: ${{ github.event_name != 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }} - run: | - push_image() { - image=$1 - - docker buildx imagetools create \ - -t $image:latest \ - -t $image:${{ env.GCC_MAJOR_VERSION }} \ - -t $image:${{ env.GCC_VERSION }} \ - -t $image:${{ github.sha }} \ - $image:arm64-latest \ - $image:amd64-latest - } - push_image ${{ needs.repo.outputs.GHCR_REPO }}/clio-gcc - if [[ ${{ github.repository }} == 'XRPLF/clio' ]]; then - push_image rippleci/clio_clang - fi - - clang: - name: Build and push Clang docker image - runs-on: heavy - needs: repo - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/compilers/clang/**" - - - uses: ./.github/actions/build-docker-image - if: ${{ steps.changed-files.outputs.any_changed == 'true' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} - DOCKERHUB_PW: ${{ secrets.DOCKERHUB_PW }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-clang - ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_clang' || '' }} - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/compilers/clang - tags: | - type=raw,value=latest - type=raw,value=${{ env.CLANG_MAJOR_VERSION }} - type=raw,value=${{ github.sha }} - platforms: linux/amd64,linux/arm64 - build_args: | - CLANG_MAJOR_VERSION=${{ env.CLANG_MAJOR_VERSION }} - dockerhub_repo: ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_clang' || '' }} - dockerhub_description: Clang compiler for XRPLF/clio. - - tools-amd64: - name: Build and push tools docker image (amd64) - runs-on: heavy - needs: [repo, gcc-merge] - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/tools/**" - - - uses: ./.github/actions/build-docker-image - if: ${{ steps.changed-files.outputs.any_changed == 'true' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-tools - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/tools - tags: | - type=raw,value=amd64-latest - type=raw,value=amd64-${{ github.sha }} - platforms: linux/amd64 - build_args: | - GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }} - GCC_VERSION=${{ env.GCC_VERSION }} - - tools-arm64: - name: Build and push tools docker image (arm64) - runs-on: heavy-arm64 - needs: [repo, gcc-merge] - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/tools/**" - - - uses: ./.github/actions/build-docker-image - if: ${{ steps.changed-files.outputs.any_changed == 'true' }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-tools - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/tools - tags: | - type=raw,value=arm64-latest - type=raw,value=arm64-${{ github.sha }} - platforms: linux/arm64 - build_args: | - GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }} - GCC_VERSION=${{ env.GCC_VERSION }} - - tools-merge: - name: Merge and push multi-arch tools docker image - runs-on: heavy - needs: [repo, tools-amd64, tools-arm64] - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Get changed files - id: changed-files - uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 - with: - files: "docker/tools/**" - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0 - - - name: Login to GitHub Container Registry - if: ${{ github.event_name != 'pull_request' }} - uses: docker/login-action@650006c6eb7dba73a995cc03b0b2d7f5ca915bee # v4.2.0 - with: - registry: ghcr.io - username: ${{ github.repository_owner }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Create and push multi-arch manifest - if: ${{ github.event_name != 'pull_request' && steps.changed-files.outputs.any_changed == 'true' }} - run: | - image=${{ needs.repo.outputs.GHCR_REPO }}/clio-tools - docker buildx imagetools create \ - -t $image:latest \ - -t $image:${{ github.sha }} \ - $image:arm64-latest \ - $image:amd64-latest - - pre-commit: - name: Build and push pre-commit docker image - runs-on: heavy - needs: [repo, tools-merge] - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - uses: ./.github/actions/build-docker-image - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-pre-commit - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/pre-commit - tags: | - type=raw,value=latest - type=raw,value=${{ github.sha }} - platforms: linux/amd64,linux/arm64 - build_args: | - GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }} - - ci: - name: Build and push CI docker image - runs-on: heavy - needs: [repo, gcc-merge, clang, tools-merge] - - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - uses: ./.github/actions/build-docker-image - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DOCKERHUB_USER: ${{ secrets.DOCKERHUB_USER }} - DOCKERHUB_PW: ${{ secrets.DOCKERHUB_PW }} - with: - images: | - ${{ needs.repo.outputs.GHCR_REPO }}/clio-ci - ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_ci' || '' }} - push_image: ${{ github.repository == 'XRPLF/clio' && github.event_name != 'pull_request' }} - directory: docker/ci - tags: | - type=raw,value=latest - type=raw,value=gcc_${{ env.GCC_MAJOR_VERSION }}_clang_${{ env.CLANG_MAJOR_VERSION }} - type=raw,value=${{ github.sha }} - platforms: linux/amd64,linux/arm64 - build_args: | - GHCR_REPO=${{ needs.repo.outputs.GHCR_REPO }} - CLANG_MAJOR_VERSION=${{ env.CLANG_MAJOR_VERSION }} - GCC_MAJOR_VERSION=${{ env.GCC_MAJOR_VERSION }} - GCC_VERSION=${{ env.GCC_VERSION }} - dockerhub_repo: ${{ github.repository_owner == 'XRPLF' && 'rippleci/clio_ci' || '' }} - dockerhub_description: CI image for XRPLF/clio. diff --git a/.github/workflows/upload-conan-deps.yml b/.github/workflows/upload-conan-deps.yml index d4752af76..2bf079cd8 100644 --- a/.github/workflows/upload-conan-deps.yml +++ b/.github/workflows/upload-conan-deps.yml @@ -59,7 +59,7 @@ jobs: run: .github/scripts/conan/generate_matrix.py >>"${GITHUB_OUTPUT}" upload-conan-deps: - name: Build ${{ matrix.compiler }}${{ matrix.sanitizer_ext }} ${{ matrix.build_type }} + name: Build ${{ matrix.compiler }} ${{ matrix.sanitizers }} ${{ matrix.build_type }} needs: generate-matrix @@ -71,9 +71,6 @@ jobs: runs-on: ${{ matrix.os }} container: ${{ matrix.container != '' && fromJson(matrix.container) || null }} - env: - CONAN_PROFILE: ${{ matrix.compiler }}${{ matrix.sanitizer_ext }} - steps: - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 @@ -85,17 +82,24 @@ jobs: - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 - - name: Setup conan on macOS - if: ${{ runner.os == 'macOS' }} - run: ./.github/scripts/conan/init.sh + - name: Set compiler environment + if: ${{ runner.os == 'Linux' }} + uses: ./.github/actions/set-compiler-env + with: + compiler: ${{ matrix.compiler }} + + - name: Setup conan + run: conan/init.sh - name: Show conan profile - run: conan profile show --profile:all ${{ env.CONAN_PROFILE }} + env: + SANITIZERS: ${{ matrix.sanitizers }} + run: conan profile show --profile:all ci - name: Run conan uses: ./.github/actions/conan with: - conan_profile: ${{ env.CONAN_PROFILE }} + sanitizers: ${{ matrix.sanitizers }} # We check that everything builds fine from source on scheduled runs # But we do build and upload packages with build=missing by default force_conan_source_build: ${{ github.event_name == 'schedule' || github.event.inputs.force_source_build == 'true' }} diff --git a/CMakeLists.txt b/CMakeLists.txt index d6f725353..ffcec30b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,6 @@ option(docs "Generate doxygen docs" FALSE) option(coverage "Build test coverage report" FALSE) option(package "Create distribution packages" FALSE) option(lint "Run clang-tidy checks during compilation" FALSE) -option(static "Statically linked Clio" FALSE) option(snapshot "Build snapshot tool" FALSE) option( time_trace @@ -25,10 +24,7 @@ option( ) # ========================================================================== # -set(san "" CACHE STRING "Add sanitizer instrumentation") set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) -set_property(CACHE san PROPERTY STRINGS ";undefined;memory;address;thread") -# ========================================================================== # set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) @@ -42,6 +38,21 @@ add_library(clio_options INTERFACE) target_compile_features(clio_options INTERFACE cxx_std_23) # Clio needs c++23 but deps can remain c++20 for now target_include_directories(clio_options INTERFACE ${CMAKE_SOURCE_DIR}/src) +if( + CMAKE_CXX_COMPILER_ID STREQUAL "Clang" + OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" +) + # Note: -static-libstdc++ can statically link both libstdc++ and libc++ + target_link_libraries( + clio_options + INTERFACE -static-libstdc++ -static-libgcc + ) +endif() + +# Apply sanitizer instrumentation if requested and define SANITIZERS_ENABLED. +# Must come before the modules and subdirectories below that key off it. +include(Sanitizers) + if(verbose) set(CMAKE_VERBOSE_MAKEFILE TRUE) endif() @@ -50,6 +61,7 @@ endif() include(CheckCompiler) include(Settings) include(SourceLocation) +include(PatchNixBinary) # Clio deps include(deps/libxrpl) @@ -68,29 +80,6 @@ if(benchmark) add_subdirectory(benchmarks) endif() -# Enable selected sanitizer if enabled via `san` -if(san) - set(SUPPORTED_SANITIZERS "address" "thread" "memory" "undefined") - if(NOT san IN_LIST SUPPORTED_SANITIZERS) - message( - FATAL_ERROR - "Error: Unsupported sanitizer '${san}'. Supported values are: ${SUPPORTED_SANITIZERS}." - ) - endif() - - # Sanitizers recommend minimum of -O1 for reasonable performance so we enable it for debug builds - set(SAN_OPTIMIZATION_FLAG "") - if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(SAN_OPTIMIZATION_FLAG -O1) - endif() - target_compile_options( - clio_options - INTERFACE ${SAN_OPTIMIZATION_FLAG} ${SAN_FLAG} -fno-omit-frame-pointer - ) - - target_link_libraries(clio_options INTERFACE ${SAN_FLAG} ${SAN_LIB}) -endif() - # Generate `docs` target for doxygen documentation if enabled Note: use `make docs` to generate the documentation if(docs) add_subdirectory(docs) diff --git a/cmake/CheckCompiler.cmake b/cmake/CheckCompiler.cmake index f0ce4e534..c333093ee 100644 --- a/cmake/CheckCompiler.cmake +++ b/cmake/CheckCompiler.cmake @@ -19,30 +19,3 @@ else() "Supported compilers: AppleClang 15+, Clang 16+, GCC 12+" ) endif() - -if(san) - string(TOLOWER ${san} san) - set(SAN_FLAG "-fsanitize=${san}") - set(SAN_LIB "") - if(is_gcc) - if(san STREQUAL "address") - set(SAN_LIB "asan") - elseif(san STREQUAL "thread") - set(SAN_LIB "tsan") - elseif(san STREQUAL "memory") - set(SAN_LIB "msan") - elseif(san STREQUAL "undefined") - set(SAN_LIB "ubsan") - endif() - endif() - set(_saved_CRL ${CMAKE_REQUIRED_LIBRARIES}) - set(CMAKE_REQUIRED_LIBRARIES "${SAN_FLAG};${SAN_LIB}") - check_cxx_compiler_flag(${SAN_FLAG} COMPILER_SUPPORTS_SAN) - set(CMAKE_REQUIRED_LIBRARIES ${_saved_CRL}) - if(NOT COMPILER_SUPPORTS_SAN) - message( - FATAL_ERROR - "${san} sanitizer does not seem to be supported by your compiler" - ) - endif() -endif() diff --git a/cmake/PatchNixBinary.cmake b/cmake/PatchNixBinary.cmake new file mode 100644 index 000000000..c6355fefc --- /dev/null +++ b/cmake/PatchNixBinary.cmake @@ -0,0 +1,55 @@ +#[===================================================================[ + Patch executables to run in non-Nix environments. + + The Nix-based CI image links binaries against an ELF interpreter (loader) + that lives in the Nix store, so the resulting binaries don't run elsewhere + (including once installed from the .deb package). `patch_nix_binary` adds a + POST_BUILD step that resets the interpreter to the system default loader and + drops the rpath. + + This is only active inside the Nix-based image, detected by the presence of + /tmp/loader-path.sh (shipped by that image, resolves the default loader). It + is skipped for sanitizer builds, whose runtime libraries are resolved through + the rpath. Everywhere else `patch_nix_binary` is a no-op. +#]===================================================================] + +include_guard(GLOBAL) + +# Provided by the Nix-based CI image; prints the system default ELF loader path. +set(_loader_path_script "/tmp/loader-path.sh") + +if( + CMAKE_SYSTEM_NAME STREQUAL "Linux" + AND NOT SANITIZERS_ENABLED + AND EXISTS "${_loader_path_script}" +) + execute_process( + COMMAND "${_loader_path_script}" + OUTPUT_VARIABLE DEFAULT_LOADER_PATH + OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY + ) + find_program(PATCHELF_COMMAND patchelf REQUIRED) + set(PATCH_NIX_BINARIES TRUE) + message( + STATUS + "Binaries will be patched to use loader '${DEFAULT_LOADER_PATH}'" + ) +else() + set(PATCH_NIX_BINARIES FALSE) +endif() + +function(patch_nix_binary target) + if(NOT PATCH_NIX_BINARIES) + return() + endif() + add_custom_command( + TARGET ${target} + POST_BUILD + COMMAND + "${PATCHELF_COMMAND}" --set-interpreter "${DEFAULT_LOADER_PATH}" + --remove-rpath "$" + COMMENT "Patching ${target}: set default loader, remove rpath" + VERBATIM + ) +endfunction() diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake new file mode 100644 index 000000000..4d1f76710 --- /dev/null +++ b/cmake/Sanitizers.cmake @@ -0,0 +1,47 @@ +#[===================================================================[ + Apply sanitizer flags built by the Conan profile. + + Parsing, validation, and flag construction are performed in + conan/profiles/sanitizers. This module reads the following CMake variables + injected by the Conan toolchain via extra_variables: + + - SANITIZERS: The active sanitizers (e.g. "address"). + - SANITIZERS_COMPILER_FLAGS: Space-separated compiler flags. + - SANITIZERS_LINKER_FLAGS: Space-separated linker flags. + + It defines SANITIZERS_ENABLED for the rest of the build to key off, and + applies the flags to the 'clio_options' interface library. +#]===================================================================] + +include_guard(GLOBAL) + +if(NOT DEFINED SANITIZERS) + set(SANITIZERS_ENABLED FALSE) + return() +endif() +set(SANITIZERS_ENABLED TRUE) + +message(STATUS "=== Configuring sanitizers ===") +message(STATUS " SANITIZERS: ${SANITIZERS}") +message(STATUS " Compile flags: ${SANITIZERS_COMPILER_FLAGS}") +message(STATUS " Link flags: ${SANITIZERS_LINKER_FLAGS}") + +# Flags arrive as space-separated strings; split into CMake lists before use +separate_arguments( + sanitizers_compiler_flags + UNIX_COMMAND + "${SANITIZERS_COMPILER_FLAGS}" +) +separate_arguments( + sanitizers_linker_flags + UNIX_COMMAND + "${SANITIZERS_LINKER_FLAGS}" +) + +target_compile_options( + clio_options + INTERFACE + $<$:${sanitizers_compiler_flags}> + $<$:${sanitizers_compiler_flags}> +) +target_link_options(clio_options INTERFACE ${sanitizers_linker_flags}) diff --git a/cmake/Settings.cmake b/cmake/Settings.cmake index b31db9c21..e0311ad4f 100644 --- a/cmake/Settings.cmake +++ b/cmake/Settings.cmake @@ -40,7 +40,7 @@ if(is_appleclang) list(APPEND COMPILER_FLAGS -Wreorder-init-list) endif() -if(san) +if(SANITIZERS_ENABLED) # When building with sanitizers some compilers will actually produce extra warnings/errors. We don't want this yet, # at least not until we have fixed all runtime issues reported by the sanitizers. Once that is done we can start # removing some of these and trying to fix it in our codebase. We can never remove all of below because most of them @@ -85,3 +85,22 @@ target_compile_options(clio_options INTERFACE ${COMPILER_FLAGS}) # Add debug symbols for all builds, including Release. This is needed to get useful stack traces in production. target_compile_options(clio_options INTERFACE -g) + +# Keep -stdlib=libstdc++ off the compile commands, but preserve it for linking. +# +# Conan turns `compiler.libcxx=libstdc++` into `-stdlib=libstdc++` and puts it in CMAKE_CXX_FLAGS, which CMake passes to +# BOTH compile and link steps. On a normal Clang the compile step consumes it while choosing the C++ stdlib include +# paths. The Nixpkgs Clang wrapper (used by our CI image) supplies those paths itself (via -nostdinc++), so at compile +# time the flag is unused -> Clang errors under our -Werror. At link time the flag IS consumed (it selects the C++ +# runtime), so we move it there instead of dropping it entirely. +get_filename_component(_cxx_real "${CMAKE_CXX_COMPILER}" REALPATH) +if( + _cxx_real MATCHES "^/nix/store/" + AND CMAKE_SYSTEM_NAME STREQUAL "Linux" + AND is_clang + AND CMAKE_CXX_FLAGS MATCHES "stdlib=libstdc" +) + string(REPLACE "-stdlib=libstdc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(STRIP "${CMAKE_CXX_FLAGS}" CMAKE_CXX_FLAGS) + add_link_options($<$:-stdlib=libstdc++>) +endif() diff --git a/cmake/deps/libbacktrace.cmake b/cmake/deps/libbacktrace.cmake index 3015d5781..0e81bd89a 100644 --- a/cmake/deps/libbacktrace.cmake +++ b/cmake/deps/libbacktrace.cmake @@ -1,4 +1,4 @@ -if("${san}" STREQUAL "") +if(NOT SANITIZERS_ENABLED) target_compile_definitions(clio_options INTERFACE BOOST_STACKTRACE_LINK) target_compile_definitions( clio_options diff --git a/conan.lock b/conan.lock index b256432b2..47cfe3533 100644 --- a/conan.lock +++ b/conan.lock @@ -28,7 +28,7 @@ "c-ares/1.34.6#545240bb1c40e2cacd4362d6b8967650%1774439234.681", "bzip2/1.0.8#c470882369c2d95c5c77e970c0c7e321%1765850143.837", "boost/1.83.0#91d8b1572534d2c334d6790e3c34d0c1%1764175359.61", - "benchmark/1.9.4#ce4403f7a24d3e1f907cd9da4b678be4%1754578869.672", + "benchmark/1.9.5#b885dc73ad67b40a55d45684d1c88ad1%1774363287.434", "abseil/20230802.1#90ba607d4ee8fb5fb157c3db540671fc%1764175359.429" ], "build_requires": [ diff --git a/conan/global.conf b/conan/global.conf new file mode 100644 index 000000000..cc803dc80 --- /dev/null +++ b/conan/global.conf @@ -0,0 +1,7 @@ +# Global configuration for Conan. This is used to set the number of parallel +# downloads and uploads. +core:non_interactive=True +core.download:parallel={{ os.cpu_count() }} +core.upload:parallel={{ os.cpu_count() }} +tools.files.download:retry=5 +tools.files.download:retry_wait=10 diff --git a/conan/init.sh b/conan/init.sh new file mode 100755 index 000000000..3c79f12ee --- /dev/null +++ b/conan/init.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +set -ex + +CURRENT_DIR="$(cd "$(dirname "$0")" && pwd)" +PROFILES_SRC_DIR="$CURRENT_DIR/profiles" + +CONAN_DIR="${CONAN_HOME:-$HOME/.conan2}" +PROFILES_DIR="$CONAN_DIR/profiles" + +rm -rf "$CONAN_DIR" + +conan remote add --index 0 --force xrplf https://conan.ripplex.io + +cp "$CURRENT_DIR/global.conf" "$CONAN_DIR/global.conf" + +mkdir -p "$PROFILES_DIR" + +# The compiler is selected via the `CC`/`CXX` environment variables (see +# `.github/actions/set-compiler-env`) and the sanitizers via the `SANITIZERS` +# environment variable. Builds always use the `ci` profile, which includes +# `sanitizers` and `default`. +cp "$PROFILES_SRC_DIR/ci" "$PROFILES_DIR/ci" +cp "$PROFILES_SRC_DIR/sanitizers" "$PROFILES_DIR/sanitizers" + +if [[ "$(uname)" == "Darwin" ]]; then + cp "$PROFILES_SRC_DIR/apple-clang-17.profile" "$PROFILES_DIR/default" +else + cp "$PROFILES_SRC_DIR/default" "$PROFILES_DIR/default" +fi diff --git a/.github/scripts/conan/apple-clang-17.profile b/conan/profiles/apple-clang-17.profile similarity index 100% rename from .github/scripts/conan/apple-clang-17.profile rename to conan/profiles/apple-clang-17.profile diff --git a/conan/profiles/ci b/conan/profiles/ci new file mode 100644 index 000000000..9422addfe --- /dev/null +++ b/conan/profiles/ci @@ -0,0 +1,8 @@ +{% set os = detect_api.detect_os() %} +include(sanitizers) + +[conf] +{% if os == "Linux" %} +user.package:libc_version=2.31 +tools.info.package_id:confs+=["user.package:libc_version"] +{% endif %} diff --git a/conan/profiles/default b/conan/profiles/default new file mode 100644 index 000000000..4aee5b3e0 --- /dev/null +++ b/conan/profiles/default @@ -0,0 +1,28 @@ +{% set os = detect_api.detect_os() %} +{% set arch = detect_api.detect_arch() %} +{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %} +{% set compiler_version = version %} +{% if os == "Linux" %} +{% set compiler_version = detect_api.default_compiler_version(compiler, version) %} +{% endif %} + +[settings] +os={{ os }} +arch={{ arch }} +build_type=Debug +compiler={{compiler}} +compiler.version={{ compiler_version }} +compiler.cppstd=20 +{% if os == "Windows" %} +compiler.runtime=static +{% else %} +compiler.libcxx={{detect_api.detect_libcxx(compiler, version, compiler_exe)}} +{% endif %} + +[conf] +{% if compiler == "gcc" and compiler_version < 13 %} +tools.build:cxxflags+=['-Wno-restrict'] +{% endif %} +{% if compiler == "clang" %} +grpc/1.50.1:tools.build:cxxflags+=['-Wno-missing-template-arg-list-after-template-kw'] +{% endif %} diff --git a/conan/profiles/sanitizers b/conan/profiles/sanitizers new file mode 100644 index 000000000..083807ea9 --- /dev/null +++ b/conan/profiles/sanitizers @@ -0,0 +1,120 @@ +include(default) +{% set compiler, version, compiler_exe = detect_api.detect_default_compiler() %} +{% set arch = detect_api.detect_arch() %} +{% set sanitizers = os.getenv("SANITIZERS") %} + +{% if not sanitizers %} +{# Sanitizers not configured; no additional settings needed #} +{% else %} + +{% if compiler == "msvc" %} + {{ "Sanitizers are not supported on Windows/MSVC. Please unset the SANITIZERS environment variable." }} +{% endif %} + +{% set known_sanitizers = ["address", "thread", "undefinedbehavior"] %} +{% set provided_sanitizers = [] %} +{% for san in sanitizers.split(",") %} + {% set san = san.strip() %} + {% if san not in known_sanitizers %} + {{ "Unknown sanitizer in SANITIZERS: " ~ san }} + {% endif %} + {% set _ = provided_sanitizers.append(san) %} +{% endfor %} + +{% set enable_asan = "address" in provided_sanitizers %} +{% set enable_tsan = "thread" in provided_sanitizers %} +{% set enable_ubsan = "undefinedbehavior" in provided_sanitizers %} + +{% if enable_asan and enable_tsan %} + {{ "AddressSanitizer and ThreadSanitizer are incompatible and cannot be enabled simultaneously." }} +{% endif %} + +{% set sanitizer_types = [] %} +{% set defines = [] %} + +{% if enable_asan %} + {% set _ = sanitizer_types.append("address") %} + {% set _ = defines.append("BOOST_USE_ASAN") %} + {% set _ = defines.append("BOOST_USE_UCONTEXT") %} +{% elif enable_tsan %} + {% set _ = sanitizer_types.append("thread") %} + {% set _ = defines.append("BOOST_USE_TSAN") %} + {% set _ = defines.append("BOOST_USE_UCONTEXT") %} +{% endif %} + +{% if enable_ubsan %} + {% set _ = sanitizer_types.append("undefined") %} + {% set _ = sanitizer_types.append("float-divide-by-zero") %} + {# Clang supports additional UB checks beyond the GCC baseline #} + {% if compiler == "clang" or compiler == "apple-clang" %} + {% set _ = sanitizer_types.append("unsigned-integer-overflow") %} + {% endif %} +{% endif %} + +{# Frame pointer required for meaningful stack traces; -O1 for reasonable performance #} +{% set sanitizer_compiler_flags = ["-fno-omit-frame-pointer", "-O1"] %} + +{% if compiler == "gcc" %} + {# Suppress false positive warnings with GCC #} + {% set _ = sanitizer_compiler_flags.append("-Wno-stringop-overflow") %} + + {% set relocation_flags = [] %} + + {% if arch == "x86_64" and enable_asan %} + {# Large code model prevents relocation errors in instrumented ASAN binaries #} + {% set _ = sanitizer_compiler_flags.append("-mcmodel=large") %} + {% set _ = relocation_flags.append("-mcmodel=large") %} + {% elif enable_tsan %} + {# GCC doesn't support atomic_thread_fence with TSAN; suppress warnings #} + {% set _ = sanitizer_compiler_flags.append("-Wno-tsan") %} + {% if arch == "x86_64" %} + {# Medium code model for TSAN; large is incompatible #} + {% set _ = sanitizer_compiler_flags.append("-mcmodel=medium") %} + {% set _ = relocation_flags.append("-mcmodel=medium") %} + {% endif %} + {% endif %} + + {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} + {% set _ = sanitizer_compiler_flags.append(fsanitize) %} + {% set _ = relocation_flags.append(fsanitize) %} + + {% set sanitizer_linker_flags = relocation_flags %} +{% elif compiler == "clang" or compiler == "apple-clang" %} + {% set fsanitize = "-fsanitize=" ~ ",".join(sanitizer_types) %} + {% set _ = sanitizer_compiler_flags.append(fsanitize) %} + + {% set sanitizer_linker_flags = [fsanitize] %} +{% endif %} + +[conf] +tools.build:defines+={{defines}} +tools.build:cxxflags+={{sanitizer_compiler_flags}} +tools.build:sharedlinkflags+={{sanitizer_linker_flags}} +tools.build:exelinkflags+={{sanitizer_linker_flags}} + +tools.info.package_id:confs+=["tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"] + +# &: means "apply only to the consumer/root package" +&:tools.cmake.cmaketoolchain:extra_variables={"SANITIZERS": "{{sanitizers}}", "SANITIZERS_COMPILER_FLAGS": "{{sanitizer_compiler_flags | join(' ')}}", "SANITIZERS_LINKER_FLAGS": "{{sanitizer_linker_flags | join(' ')}}"} + +[options] +{% if enable_asan %} + # Build Boost.Context with ucontext backend (not fcontext) so that + # ASAN fiber-switching annotations (__sanitizer_start/finish_switch_fiber) + # are compiled into the library. fcontext (assembly) has no ASAN support. + # define=BOOST_USE_ASAN=1 is critical: it must be defined when building + # Boost.Context itself so the ucontext backend compiles in the ASAN annotations. + boost/*:extra_b2_flags=context-impl=ucontext address-sanitizer=on define=BOOST_USE_ASAN=1 + boost/*:without_context=False + # Boost stacktrace fails to build with some sanitizers + boost/*:without_stacktrace=True +{% elif enable_tsan %} + # Build Boost.Context with ucontext backend for TSAN. fcontext (assembly) + # has no TSAN annotations, so without this the BOOST_USE_TSAN/BOOST_USE_UCONTEXT + # defines in [conf] would be ineffective. + boost/*:extra_b2_flags=context-impl=ucontext thread-sanitizer=on define=BOOST_USE_TSAN=1 + boost/*:without_context=False + boost/*:without_stacktrace=True +{% endif %} + +{% endif %} diff --git a/conanfile.py b/conanfile.py index ddfdeec4e..1d716eb96 100644 --- a/conanfile.py +++ b/conanfile.py @@ -1,6 +1,7 @@ -from conan import ConanFile from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout +from conan import ConanFile + class ClioConan(ConanFile): name = "clio" @@ -44,7 +45,7 @@ class ClioConan(ConanFile): def requirements(self): self.requires("boost/1.83.0", force=True) self.requires("gtest/1.17.0") - self.requires("benchmark/1.9.4") + self.requires("benchmark/1.9.5") def configure(self): if self.settings.compiler == "apple-clang": diff --git a/docker/ci/Dockerfile b/docker/ci/Dockerfile deleted file mode 100644 index 72ba46b0f..000000000 --- a/docker/ci/Dockerfile +++ /dev/null @@ -1,132 +0,0 @@ -ARG GHCR_REPO=invalid -ARG CLANG_MAJOR_VERSION=invalid -ARG GCC_VERSION=invalid - -FROM ${GHCR_REPO}/clio-gcc:${GCC_VERSION} AS clio-gcc -FROM ${GHCR_REPO}/clio-tools:latest AS clio-tools - -FROM ${GHCR_REPO}/clio-clang:${CLANG_MAJOR_VERSION} - -ARG DEBIAN_FRONTEND=noninteractive - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# Using root by default is not very secure but github checkout action doesn't work with any other user -# https://github.com/actions/checkout/issues/956 -# And Github Actions doc recommends using root -# https://docs.github.com/en/actions/sharing-automations/creating-actions/dockerfile-support-for-github-actions#user - -# hadolint ignore=DL3002 -USER root -WORKDIR /root - -# Install common tools and dependencies -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - curl \ - dpkg-dev \ - file \ - git \ - git-lfs \ - gnupg \ - graphviz \ - jq \ - # libgmp, libmpfr and libncurses are gdb dependencies - libgmp-dev \ - libmpfr-dev \ - libncurses-dev \ - make \ - wget \ - zip \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Install Python tools -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - python3 \ - python3-pip \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -RUN pip install -q --no-cache-dir \ - # TODO: Remove this once we switch to newer Ubuntu base image - # lxml 6.0.0 is not compatible with our image - 'lxml<6.0.0' \ - cmake \ - conan==2.24.0 \ - gcovr \ - # We're adding pre-commit to this image as well, - # because clang-tidy workflow requires it - pre-commit - -# Install LLVM tools -ARG LLVM_TOOLS_VERSION=21 - -RUN echo "deb http://apt.llvm.org/focal/ llvm-toolchain-focal-${LLVM_TOOLS_VERSION} main" >> /etc/apt/sources.list \ - && wget --progress=dot:giga -O - https://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add - - -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - clang-tidy-${LLVM_TOOLS_VERSION} \ - clang-tools-${LLVM_TOOLS_VERSION} \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -ARG GCC_MAJOR_VERSION=invalid - -# Install custom-built gcc and make ldconfig aware of the new libstdc++ location (for gcc) -# Note: Clang is using libc++ instead -COPY --from=clio-gcc /gcc${GCC_MAJOR_VERSION}.deb / -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - binutils \ - libc6-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && dpkg -i /gcc${GCC_MAJOR_VERSION}.deb \ - && rm -rf /gcc${GCC_MAJOR_VERSION}.deb \ - && ldconfig - -# Rewire to use our custom-built gcc as default compiler -RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-${GCC_MAJOR_VERSION} 100 - -COPY --from=clio-tools \ - /usr/local/bin/mold \ - /usr/local/bin/ld.mold \ - /usr/local/bin/ccache \ - /usr/local/bin/doxygen \ - /usr/local/bin/ClangBuildAnalyzer \ - /usr/local/bin/git-cliff \ - /usr/local/bin/gh \ - /usr/local/bin/gdb \ - /usr/local/bin/ninja \ - /usr/local/bin/ - -WORKDIR /root - -# Setup conan -RUN conan remote add --index 0 xrplf https://conan.ripplex.io - -WORKDIR /root/.conan2 -COPY conan/global.conf ./global.conf - -WORKDIR /root/.conan2/profiles - -COPY conan/clang.profile ./clang -COPY conan/sanitizer_template.profile ./clang.asan -COPY conan/sanitizer_template.profile ./clang.tsan -COPY conan/sanitizer_template.profile ./clang.ubsan - -COPY conan/gcc.profile ./gcc -COPY conan/sanitizer_template.profile ./gcc.asan -COPY conan/sanitizer_template.profile ./gcc.tsan -COPY conan/sanitizer_template.profile ./gcc.ubsan - -WORKDIR /root diff --git a/docker/ci/README.md b/docker/ci/README.md deleted file mode 100644 index b8a37a575..000000000 --- a/docker/ci/README.md +++ /dev/null @@ -1,26 +0,0 @@ -# CI image for XRPLF/clio - -This image contains an environment to build [Clio](https://github.com/XRPLF/clio), check code and documentation. -It is used in [Clio Github Actions](https://github.com/XRPLF/clio/actions) but can also be used to compile Clio locally. - -The image is based on Ubuntu 20.04 and contains: - -- ccache 4.12.2 -- Clang 19 -- ClangBuildAnalyzer 1.6.0 -- Conan 2.24.0 -- Doxygen 1.16.1 -- GCC 15.2.0 -- GDB 17.1 -- gh 2.83.2 -- git-cliff 2.11.0 -- LLVM Tools 21 -- mold 2.40.4 -- Ninja 1.13.2 -- Python 3.8 -- and some other useful tools - -Conan is set up to build Clio without any additional steps. -There are two preset conan profiles: `clang` and `gcc` to use corresponding compiler. -`ASan`, `TSan` and `UBSan` sanitizer builds are enabled via conan profiles for each of the supported compilers. -These can be selected using the following pattern (all lowercase): `[compiler].[sanitizer]` (e.g. `--profile:all gcc.tsan`). diff --git a/docker/ci/conan/clang.profile b/docker/ci/conan/clang.profile deleted file mode 100644 index d142469c3..000000000 --- a/docker/ci/conan/clang.profile +++ /dev/null @@ -1,12 +0,0 @@ -[settings] -arch={{detect_api.detect_arch()}} -build_type=Release -compiler=clang -compiler.cppstd=20 -compiler.libcxx=libc++ -compiler.version=19 -os=Linux - -[conf] -tools.build:compiler_executables={"c": "/usr/bin/clang-19", "cpp": "/usr/bin/clang++-19"} -grpc/1.50.1:tools.build:cxxflags+=["-Wno-missing-template-arg-list-after-template-kw"] diff --git a/docker/ci/conan/gcc.profile b/docker/ci/conan/gcc.profile deleted file mode 100644 index 7224fc86b..000000000 --- a/docker/ci/conan/gcc.profile +++ /dev/null @@ -1,11 +0,0 @@ -[settings] -arch={{detect_api.detect_arch()}} -build_type=Release -compiler=gcc -compiler.cppstd=20 -compiler.libcxx=libstdc++11 -compiler.version=15 -os=Linux - -[conf] -tools.build:compiler_executables={"c": "/usr/bin/gcc-15", "cpp": "/usr/bin/g++-15"} diff --git a/docker/ci/conan/global.conf b/docker/ci/conan/global.conf deleted file mode 100644 index 8efc9de20..000000000 --- a/docker/ci/conan/global.conf +++ /dev/null @@ -1,2 +0,0 @@ -core.download:parallel={{os.cpu_count()}} -core.upload:parallel={{os.cpu_count()}} diff --git a/docker/ci/conan/sanitizer_template.profile b/docker/ci/conan/sanitizer_template.profile deleted file mode 100644 index 0df3aa04e..000000000 --- a/docker/ci/conan/sanitizer_template.profile +++ /dev/null @@ -1,37 +0,0 @@ -{% set compiler, sani = profile_name.split('.') %} - -{% set sanitizer_opt_map = {"asan": "address", "tsan": "thread", "ubsan": "undefined"} %} -{% set sanitizer = sanitizer_opt_map[sani] %} - -{% set sanitizer_b2_flags_map = { - "address": "context-impl=ucontext address-sanitizer=norecover", - "thread": "context-impl=ucontext thread-sanitizer=norecover", - "undefined": "undefined-sanitizer=norecover" -} %} -{% set sanitizer_b2_flags_str = sanitizer_b2_flags_map[sanitizer] %} - -{% set sanitizer_build_flags_str = "-fsanitize=" ~ sanitizer ~ " -g -O1 -fno-omit-frame-pointer" %} -{% set sanitizer_build_flags = sanitizer_build_flags_str.split(' ') %} -{% set sanitizer_link_flags_str = "-fsanitize=" ~ sanitizer %} -{% set sanitizer_link_flags = sanitizer_link_flags_str.split(' ') %} - -include({{ compiler }}) - -[options] -boost/*:extra_b2_flags="{{ sanitizer_b2_flags_str }}" -boost/*:without_context=False -boost/*:without_stacktrace=True - -[conf] -tools.build:cflags+={{ sanitizer_build_flags }} -tools.build:cxxflags+={{ sanitizer_build_flags }} -tools.build:exelinkflags+={{ sanitizer_link_flags }} -tools.build:sharedlinkflags+={{ sanitizer_link_flags }} - -{% if sanitizer == "address" %} - tools.build:defines+=["BOOST_USE_ASAN", "BOOST_USE_UCONTEXT"] -{% elif sanitizer == "thread" %} - tools.build:defines+=["BOOST_USE_TSAN", "BOOST_USE_UCONTEXT"] -{% endif %} - -tools.info.package_id:confs+=["tools.build:cflags", "tools.build:cxxflags", "tools.build:exelinkflags", "tools.build:sharedlinkflags", "tools.build:defines"] diff --git a/docker/compilers/clang/Dockerfile b/docker/compilers/clang/Dockerfile deleted file mode 100644 index e9782c49f..000000000 --- a/docker/compilers/clang/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM ubuntu:20.04 - -ARG DEBIAN_FRONTEND=noninteractive - -SHELL ["/bin/bash", "-c"] - -# hadolint ignore=DL3002 -USER root -WORKDIR /root - -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - wget \ - software-properties-common \ - gnupg \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -ARG CLANG_MAJOR_VERSION=invalid -# Bump this version to force rebuild of the image -ARG BUILD_VERSION=1 - -RUN wget --progress=dot:giga https://apt.llvm.org/llvm.sh \ - && chmod +x llvm.sh \ - && ./llvm.sh ${CLANG_MAJOR_VERSION} \ - && rm -rf llvm.sh \ - && apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - libc++-${CLANG_MAJOR_VERSION}-dev \ - libc++abi-${CLANG_MAJOR_VERSION}-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* diff --git a/docker/compilers/clang/README.md b/docker/compilers/clang/README.md deleted file mode 100644 index 6479791cb..000000000 --- a/docker/compilers/clang/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# Clang compiler - -This image contains clang compiler to build . diff --git a/docker/compilers/gcc/Dockerfile b/docker/compilers/gcc/Dockerfile deleted file mode 100644 index ecaa22da9..000000000 --- a/docker/compilers/gcc/Dockerfile +++ /dev/null @@ -1,120 +0,0 @@ -ARG UBUNTU_VERSION=20.04 - -ARG GCC_MAJOR_VERSION=invalid - -FROM ubuntu:$UBUNTU_VERSION AS build - -ARG UBUNTU_VERSION - -ARG GCC_MAJOR_VERSION - -ARG BUILD_VERSION=0 - -ARG DEBIAN_FRONTEND=noninteractive -ARG TARGETARCH - -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - build-essential \ - file \ - flex \ - libz-dev \ - libzstd-dev \ - software-properties-common \ - wget \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -ARG GCC_VERSION - -WORKDIR / -RUN wget --progress=dot:giga https://gcc.gnu.org/pub/gcc/releases/gcc-$GCC_VERSION/gcc-$GCC_VERSION.tar.gz \ - && tar xf gcc-$GCC_VERSION.tar.gz - -WORKDIR /gcc-$GCC_VERSION -RUN ./contrib/download_prerequisites - -# hadolint ignore=DL3059 -RUN mkdir /gcc-build -WORKDIR /gcc-build -RUN /gcc-$GCC_VERSION/configure \ - --with-pkgversion="clio-build-$BUILD_VERSION https://github.com/XRPLF/clio" \ - --enable-languages=c,c++ \ - --prefix=/usr \ - --with-gcc-major-version-only \ - --program-suffix=-${GCC_MAJOR_VERSION} \ - --enable-shared \ - --enable-linker-build-id \ - --libexecdir=/usr/lib \ - --without-included-gettext \ - --enable-threads=posix \ - --libdir=/usr/lib \ - --disable-nls \ - --enable-clocale=gnu \ - --enable-libstdcxx-backtrace=yes \ - --enable-libstdcxx-debug \ - --enable-libstdcxx-time=yes \ - --with-default-libstdcxx-abi=new \ - --enable-gnu-unique-object \ - --disable-vtable-verify \ - --enable-plugin \ - --enable-default-pie \ - --with-system-zlib \ - --enable-libphobos-checking=release \ - --with-target-system-zlib=auto \ - --disable-werror \ - --enable-cet \ - --disable-multilib \ - --without-cuda-driver \ - --enable-checking=release - -RUN make -j "$(nproc)" -RUN make install-strip DESTDIR=/gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION - -RUN export GDB_AUTOLOAD_DIR="/gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION/usr/share/gdb/auto-load/usr/lib64" \ - && mkdir -p "$GDB_AUTOLOAD_DIR" \ - && mv \ - /gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION/usr/lib64/libstdc++.so.*-gdb.py \ - $GDB_AUTOLOAD_DIR/ - -# Generate deb -WORKDIR / -COPY control.m4 / -COPY ld.so.conf /gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION/etc/ld.so.conf.d/1-gcc-${GCC_MAJOR_VERSION}.conf - -RUN mkdir /gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION/DEBIAN \ - && m4 \ - -P \ - -DUBUNTU_VERSION=$UBUNTU_VERSION \ - -DVERSION=$GCC_VERSION-$BUILD_VERSION \ - -DTARGETARCH=$TARGETARCH \ - control.m4 > /gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION/DEBIAN/control \ - && dpkg-deb \ - --build \ - --root-owner-group \ - /gcc-$GCC_VERSION-$BUILD_VERSION-ubuntu-$UBUNTU_VERSION \ - /gcc${GCC_MAJOR_VERSION}.deb - -# Create final image -FROM ubuntu:$UBUNTU_VERSION - -ARG GCC_MAJOR_VERSION - -COPY --from=build /gcc${GCC_MAJOR_VERSION}.deb / - -# Install gcc-${GCC_MAJOR_VERSION}, but also leave gcc${GCC_MAJOR_VERSION}.deb for others to copy if needed -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - binutils \ - libc6-dev \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* \ - && dpkg -i /gcc${GCC_MAJOR_VERSION}.deb - -RUN update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/cc cc /usr/bin/gcc-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcov gcov /usr/bin/gcov-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcov-dump gcov-dump /usr/bin/gcov-dump-${GCC_MAJOR_VERSION} 100 \ - && update-alternatives --install /usr/bin/gcov-tool gcov-tool /usr/bin/gcov-tool-${GCC_MAJOR_VERSION} 100 diff --git a/docker/compilers/gcc/README.md b/docker/compilers/gcc/README.md deleted file mode 100644 index 5cc3a9cbf..000000000 --- a/docker/compilers/gcc/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# GCC compiler - -This image contains GCC compiler to build . diff --git a/docker/compilers/gcc/control.m4 b/docker/compilers/gcc/control.m4 deleted file mode 100644 index 896a735d0..000000000 --- a/docker/compilers/gcc/control.m4 +++ /dev/null @@ -1,7 +0,0 @@ -Package: gcc-15-ubuntu-UBUNTUVERSION -Version: VERSION -Architecture: TARGETARCH -Maintainer: Alex Kremer -Uploaders: Ayaz Salikhov -Description: GCC VERSION build for ubuntu UBUNTUVERSION -Depends: binutils, libc6-dev diff --git a/docker/compilers/gcc/ld.so.conf b/docker/compilers/gcc/ld.so.conf deleted file mode 100644 index 063e964a7..000000000 --- a/docker/compilers/gcc/ld.so.conf +++ /dev/null @@ -1,2 +0,0 @@ -# Path to the directory containing libstdc++.so.6 -/usr/lib64 diff --git a/docker/develop/compose.yaml b/docker/develop/compose.yaml index bd18473d8..664f9b140 100644 --- a/docker/develop/compose.yaml +++ b/docker/develop/compose.yaml @@ -1,6 +1,6 @@ services: clio_develop: - image: ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 + image: ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 volumes: - clio_develop_conan_data:/root/.conan2/p - clio_develop_ccache:/root/.ccache diff --git a/docker/tools/Dockerfile b/docker/tools/Dockerfile deleted file mode 100644 index 16196ea27..000000000 --- a/docker/tools/Dockerfile +++ /dev/null @@ -1,112 +0,0 @@ -ARG GHCR_REPO=invalid -ARG GCC_VERSION=invalid - -FROM ${GHCR_REPO}/clio-gcc:${GCC_VERSION} - -ARG DEBIAN_FRONTEND=noninteractive -ARG TARGETARCH - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -ARG BUILD_VERSION=0 - -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - python3 \ - python3-pip \ - software-properties-common \ - wget \ - && pip3 install -q --no-cache-dir \ - cmake \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -WORKDIR /tmp - -ARG NINJA_VERSION=1.13.2 - -RUN wget --progress=dot:giga "https://github.com/ninja-build/ninja/archive/refs/tags/v${NINJA_VERSION}.tar.gz" \ - && tar xf "v${NINJA_VERSION}.tar.gz" \ - && cd "ninja-${NINJA_VERSION}" \ - && ./configure.py --bootstrap \ - && mv ninja /usr/local/bin/ninja \ - && rm -rf /tmp/* /var/tmp/* - -ARG MOLD_VERSION=2.40.4 -RUN wget --progress=dot:giga "https://github.com/rui314/mold/archive/refs/tags/v${MOLD_VERSION}.tar.gz" \ - && tar xf "v${MOLD_VERSION}.tar.gz" \ - && cd "mold-${MOLD_VERSION}" \ - && mkdir build \ - && cd build \ - && cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. \ - && ninja install \ - && rm -rf /tmp/* /var/tmp/* - -ARG CCACHE_VERSION=4.12.2 -RUN wget --progress=dot:giga "https://github.com/ccache/ccache/releases/download/v${CCACHE_VERSION}/ccache-${CCACHE_VERSION}.tar.gz" \ - && tar xf "ccache-${CCACHE_VERSION}.tar.gz" \ - && cd "ccache-${CCACHE_VERSION}" \ - && mkdir build \ - && cd build \ - && cmake -GNinja -DCMAKE_BUILD_TYPE=Release -DENABLE_TESTING=False .. \ - && ninja install \ - && rm -rf /tmp/* /var/tmp/* - -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - bison \ - flex \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -ARG DOXYGEN_VERSION=1.16.1 -RUN wget --progress=dot:giga "https://github.com/doxygen/doxygen/releases/download/Release_${DOXYGEN_VERSION//./_}/doxygen-${DOXYGEN_VERSION}.src.tar.gz" \ - && tar xf "doxygen-${DOXYGEN_VERSION}.src.tar.gz" \ - && cd "doxygen-${DOXYGEN_VERSION}" \ - && mkdir build \ - && cd build \ - && cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. \ - && ninja install \ - && rm -rf /tmp/* /var/tmp/* - -ARG CLANG_BUILD_ANALYZER_VERSION=1.6.0 -RUN wget --progress=dot:giga "https://github.com/aras-p/ClangBuildAnalyzer/archive/refs/tags/v${CLANG_BUILD_ANALYZER_VERSION}.tar.gz" \ - && tar xf "v${CLANG_BUILD_ANALYZER_VERSION}.tar.gz" \ - && cd "ClangBuildAnalyzer-${CLANG_BUILD_ANALYZER_VERSION}" \ - && mkdir build \ - && cd build \ - && cmake -GNinja -DCMAKE_BUILD_TYPE=Release .. \ - && ninja install \ - && rm -rf /tmp/* /var/tmp/* - -ARG GIT_CLIFF_VERSION=2.11.0 -RUN wget --progress=dot:giga "https://github.com/orhun/git-cliff/releases/download/v${GIT_CLIFF_VERSION}/git-cliff-${GIT_CLIFF_VERSION}-x86_64-unknown-linux-musl.tar.gz" \ - && tar xf git-cliff-${GIT_CLIFF_VERSION}-x86_64-unknown-linux-musl.tar.gz \ - && mv git-cliff-${GIT_CLIFF_VERSION}/git-cliff /usr/local/bin/git-cliff \ - && rm -rf /tmp/* /var/tmp/* - -ARG GH_VERSION=2.83.2 -RUN wget --progress=dot:giga "https://github.com/cli/cli/releases/download/v${GH_VERSION}/gh_${GH_VERSION}_linux_${TARGETARCH}.tar.gz" \ - && tar xf gh_${GH_VERSION}_linux_${TARGETARCH}.tar.gz \ - && mv gh_${GH_VERSION}_linux_${TARGETARCH}/bin/gh /usr/local/bin/gh \ - && rm -rf /tmp/* /var/tmp/* - -RUN apt-get update \ - && apt-get install -y --no-install-recommends --no-install-suggests \ - libgmp-dev \ - libmpfr-dev \ - libncurses-dev \ - make \ - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -ARG GDB_VERSION=17.1 -RUN wget --progress=dot:giga "https://sourceware.org/pub/gdb/releases/gdb-${GDB_VERSION}.tar.gz" \ - && tar xf "gdb-${GDB_VERSION}.tar.gz" \ - && cd "gdb-${GDB_VERSION}" \ - && ./configure --prefix=/usr/local \ - && make -j "$(nproc)" \ - && make install-gdb \ - && rm -rf /tmp/* /var/tmp/* - -WORKDIR /root diff --git a/docs/build-clio.md b/docs/build-clio.md index 81a43e006..0ef3f082a 100644 --- a/docs/build-clio.md +++ b/docs/build-clio.md @@ -11,8 +11,8 @@ - [**Optional**] [GCovr](https://gcc.gnu.org/onlinedocs/gcc/Gcov.html): needed for code coverage generation - [**Optional**] [CCache](https://ccache.dev/): speeds up compilation if you are going to compile Clio often -We use our Docker image `ghcr.io/XRPLF/clio-ci` to build `Clio`, see [Building Clio with Docker](#building-clio-with-docker). -You can find information about exact compiler versions and tools in the [image's README](https://github.com/XRPLF/clio/blob/develop/docker/ci/README.md). +We use the Nix-based Docker image `ghcr.io/xrplf/xrpld/nix-ubuntu` to build `Clio`, see [Building Clio with Docker](#building-clio-with-docker). +This image is produced by [rippled](https://github.com/XRPLF/rippled) and ships the compilers and tools listed below. The following compiler version are guaranteed to work. Any compiler with lower version may not be able to build Clio: @@ -30,7 +30,7 @@ You can change it by using `$CONAN_HOME` env variable. [More info about Conan home](https://docs.conan.io/2/reference/environment.html#conan-home). > [!TIP] -> To setup Conan automatically, you can run `.github/scripts/conan/init.sh`. +> To setup Conan automatically, you can run `conan/init.sh`. > This will delete Conan home directory (if it exists), set up profiles and add Artifactory remote. The instruction below assumes that `$CONAN_HOME` is not set. @@ -175,7 +175,7 @@ Open the `index.html` file in your browser to see the documentation pages. It is also possible to build Clio using [Docker](https://www.docker.com/) if you don't want to install all the dependencies on your machine. ```sh -docker run -it ghcr.io/xrplf/clio-ci:14342e087ceb8b593027198bf9ef06a43833c696 +docker run -it ghcr.io/xrplf/xrpld/nix-ubuntu:sha-7b9d553 git clone https://github.com/XRPLF/clio cd clio ``` diff --git a/src/main/CMakeLists.txt b/src/main/CMakeLists.txt index 6ef5b0bc0..202dd2428 100644 --- a/src/main/CMakeLists.txt +++ b/src/main/CMakeLists.txt @@ -3,21 +3,9 @@ add_executable(clio_server) target_sources(clio_server PRIVATE Main.cpp) target_link_libraries(clio_server PRIVATE clio_app) -if(static) - if(san) - message(FATAL_ERROR "Static linkage not allowed when using sanitizers") - elseif(is_appleclang) - message(FATAL_ERROR "Static linkage not supported on AppleClang") - else() - target_link_options( - # Note: -static-libstdc++ can statically link both libstdc++ and libc++ - clio_server - PRIVATE -static-libstdc++ -static-libgcc - ) - endif() -endif() - set_target_properties( clio_server PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) + +patch_nix_binary(clio_server) diff --git a/src/rpc/CMakeLists.txt b/src/rpc/CMakeLists.txt index a23ed71af..9691de44d 100644 --- a/src/rpc/CMakeLists.txt +++ b/src/rpc/CMakeLists.txt @@ -58,4 +58,4 @@ target_sources( handlers/VaultInfo.cpp ) -target_link_libraries(clio_rpc PRIVATE clio_util) +target_link_libraries(clio_rpc PUBLIC clio_util clio_data) diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index ee7031565..2344d328a 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -45,7 +45,7 @@ target_sources( ) # This must be above the target_link_libraries call otherwise backtrace doesn't work -if("${san}" STREQUAL "") +if(NOT SANITIZERS_ENABLED) target_link_libraries(clio_util PUBLIC Boost::stacktrace_backtrace) endif() diff --git a/src/util/async/AnyExecutionContext.hpp b/src/util/async/AnyExecutionContext.hpp index 1d9241e37..57bf9b58c 100644 --- a/src/util/async/AnyExecutionContext.hpp +++ b/src/util/async/AnyExecutionContext.hpp @@ -4,6 +4,7 @@ #include "util/async/AnyStopToken.hpp" #include "util/async/AnyStrand.hpp" #include "util/async/Concepts.hpp" +#include "util/async/impl/Any.hpp" #include "util/async/impl/ErasedOperation.hpp" #include @@ -68,10 +69,10 @@ public: execute(SomeHandlerWithoutStopToken auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); return AnyOperation( - pimpl_->execute([fn = std::forward(fn)] mutable -> std::any { + pimpl_->execute([fn = std::forward(fn)] mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn)); return {}; @@ -94,10 +95,10 @@ public: execute(SomeHandlerWith auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); return AnyOperation(pimpl_->execute( - [fn = std::forward(fn)](auto stopToken) mutable -> std::any { + [fn = std::forward(fn)](auto stopToken) mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn), std::move(stopToken)); return {}; @@ -123,10 +124,10 @@ public: execute(SomeHandlerWith auto&& fn, SomeStdDuration auto timeout) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); return AnyOperation(pimpl_->execute( - [fn = std::forward(fn)](auto stopToken) mutable -> std::any { + [fn = std::forward(fn)](auto stopToken) mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn), std::move(stopToken)); return {}; @@ -153,11 +154,11 @@ public: scheduleAfter(SomeStdDuration auto delay, SomeHandlerWith auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); auto const millis = std::chrono::duration_cast(delay); return AnyOperation(pimpl_->scheduleAfter( - millis, [fn = std::forward(fn)](auto stopToken) mutable -> std::any { + millis, [fn = std::forward(fn)](auto stopToken) mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn), std::move(stopToken)); return {}; @@ -184,13 +185,13 @@ public: scheduleAfter(SomeStdDuration auto delay, SomeHandlerWith auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); auto const millis = std::chrono::duration_cast(delay); return AnyOperation(pimpl_->scheduleAfter( millis, [fn = std::forward(fn)](auto stopToken, auto cancelled) mutable - -> std::any { + -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn), std::move(stopToken), cancelled); return {}; @@ -214,12 +215,12 @@ public: executeRepeatedly(SomeStdDuration auto interval, SomeHandlerWithoutStopToken auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); auto const millis = std::chrono::duration_cast(interval); return AnyOperation( // pimpl_->executeRepeatedly( - millis, [fn = std::forward(fn)] mutable -> std::any { + millis, [fn = std::forward(fn)] mutable -> impl::Any { std::invoke(std::forward(fn)); return {}; } @@ -277,18 +278,18 @@ private: virtual impl::ErasedOperation execute( - std::function, + std::function, std::optional timeout = std::nullopt ) = 0; - virtual impl::ErasedOperation execute(std::function) = 0; + virtual impl::ErasedOperation execute(std::function) = 0; virtual impl::ErasedOperation - scheduleAfter(std::chrono::milliseconds, std::function) = 0; + scheduleAfter(std::chrono::milliseconds, std::function) = 0; virtual impl::ErasedOperation scheduleAfter( std::chrono::milliseconds, - std::function + std::function ) = 0; virtual impl::ErasedOperation - executeRepeatedly(std::chrono::milliseconds, std::function) = 0; + executeRepeatedly(std::chrono::milliseconds, std::function) = 0; virtual void submit(std::function) = 0; virtual AnyStrand makeStrand() = 0; @@ -309,7 +310,7 @@ private: impl::ErasedOperation execute( - std::function fn, + std::function fn, std::optional timeout ) override { @@ -317,7 +318,7 @@ private: } impl::ErasedOperation - execute(std::function fn) override + execute(std::function fn) override { return ctx.execute(std::move(fn)); } @@ -325,7 +326,7 @@ private: impl::ErasedOperation scheduleAfter( std::chrono::milliseconds delay, - std::function fn + std::function fn ) override { return ctx.scheduleAfter(delay, std::move(fn)); @@ -334,14 +335,17 @@ private: impl::ErasedOperation scheduleAfter( std::chrono::milliseconds delay, - std::function fn + std::function fn ) override { return ctx.scheduleAfter(delay, std::move(fn)); } impl::ErasedOperation - executeRepeatedly(std::chrono::milliseconds interval, std::function fn) override + executeRepeatedly( + std::chrono::milliseconds interval, + std::function fn + ) override { return ctx.executeRepeatedly(interval, std::move(fn)); } diff --git a/src/util/async/AnyOperation.hpp b/src/util/async/AnyOperation.hpp index b6b8846cb..8bdf40dd9 100644 --- a/src/util/async/AnyOperation.hpp +++ b/src/util/async/AnyOperation.hpp @@ -1,6 +1,7 @@ #pragma once #include "util/async/Error.hpp" +#include "util/async/impl/Any.hpp" #include "util/async/impl/ErasedOperation.hpp" #include diff --git a/src/util/async/AnyStrand.hpp b/src/util/async/AnyStrand.hpp index 5e0d53edc..acf3b59c9 100644 --- a/src/util/async/AnyStrand.hpp +++ b/src/util/async/AnyStrand.hpp @@ -3,6 +3,7 @@ #include "util/async/AnyOperation.hpp" #include "util/async/AnyStopToken.hpp" #include "util/async/Concepts.hpp" +#include "util/async/impl/Any.hpp" #include "util/async/impl/ErasedOperation.hpp" #include @@ -46,10 +47,10 @@ public: execute(SomeHandlerWithoutStopToken auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); return AnyOperation( // - pimpl_->execute([fn = std::forward(fn)] mutable -> std::any { + pimpl_->execute([fn = std::forward(fn)] mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn)); return {}; @@ -70,11 +71,11 @@ public: execute(SomeHandlerWith auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); return AnyOperation( // pimpl_->execute( - [fn = std::forward(fn)](auto stopToken) mutable -> std::any { + [fn = std::forward(fn)](auto stopToken) mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn), std::move(stopToken)); return {}; @@ -99,11 +100,11 @@ public: execute(SomeHandlerWith auto&& fn, SomeStdDuration auto timeout) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); return AnyOperation( // pimpl_->execute( - [fn = std::forward(fn)](auto stopToken) mutable -> std::any { + [fn = std::forward(fn)](auto stopToken) mutable -> impl::Any { if constexpr (std::is_void_v) { std::invoke(std::forward(fn), std::move(stopToken)); return {}; @@ -129,12 +130,12 @@ public: executeRepeatedly(SomeStdDuration auto interval, SomeHandlerWithoutStopToken auto&& fn) { using RetType = std::decay_t>; - static_assert(not std::is_same_v); + static_assert(not std::is_same_v); auto const millis = std::chrono::duration_cast(interval); return AnyOperation( // pimpl_->executeRepeatedly( - millis, [fn = std::forward(fn)] mutable -> std::any { + millis, [fn = std::forward(fn)] mutable -> impl::Any { std::invoke(std::forward(fn)); return {}; } @@ -160,12 +161,12 @@ private: [[nodiscard]] virtual impl::ErasedOperation execute( - std::function, + std::function, std::optional timeout = std::nullopt ) = 0; - [[nodiscard]] virtual impl::ErasedOperation execute(std::function) = 0; + [[nodiscard]] virtual impl::ErasedOperation execute(std::function) = 0; [[nodiscard]] virtual impl::ErasedOperation - executeRepeatedly(std::chrono::milliseconds, std::function) = 0; + executeRepeatedly(std::chrono::milliseconds, std::function) = 0; virtual void submit(std::function) = 0; }; @@ -181,7 +182,7 @@ private: [[nodiscard]] impl::ErasedOperation execute( - std::function fn, + std::function fn, std::optional timeout ) override { @@ -189,13 +190,16 @@ private: } [[nodiscard]] impl::ErasedOperation - execute(std::function fn) override + execute(std::function fn) override { return strand.execute(std::move(fn)); } impl::ErasedOperation - executeRepeatedly(std::chrono::milliseconds interval, std::function fn) override + executeRepeatedly( + std::chrono::milliseconds interval, + std::function fn + ) override { return strand.executeRepeatedly(interval, std::move(fn)); } diff --git a/src/util/async/Error.hpp b/src/util/async/Error.hpp index 716f18c9f..273ea223a 100644 --- a/src/util/async/Error.hpp +++ b/src/util/async/Error.hpp @@ -6,11 +6,6 @@ #include #include -// for the static_assert at the bottom which fixes clang compilation: -// see: https://godbolt.org/z/fzTjMd7G1 vs https://godbolt.org/z/jhKG7deen -#include -#include - namespace util::async { /** @@ -49,7 +44,4 @@ struct ExecutionError { std::string message; }; -// these are not the droids you are looking for... -static_assert(std::is_copy_constructible_v>); - } // namespace util::async diff --git a/src/util/async/impl/Any.hpp b/src/util/async/impl/Any.hpp new file mode 100644 index 000000000..18089399f --- /dev/null +++ b/src/util/async/impl/Any.hpp @@ -0,0 +1,41 @@ +#pragma once + +#include +#include + +namespace util::async::impl { + +/** + * @brief A wrapper for std::any used as the type-erased transport for async operation results. + * + * It exists to work around a recursive-constraint failure in libstdc++'s `` when the + * value type is a raw `std::any`: `std::any`'s greedy templated constructor makes + * `std::expected` ask whether `std::any` is constructible from + * `std::expected`, which re-enters the same constraint. Newer Clang rejects this as + * self-referential. + * + * `Any` only allows construction from `std::any` (never from an `expected`), which breaks the cycle + * while still allowing transparent unwrapping back to `std::any&`. + */ +class Any { + std::any value_; + +public: + Any() = default; + Any(Any const&) = default; + + Any(Any&&) = default; + // note: this needs to be `auto` instead of `std::any` because of a bug in gcc 11.4 + Any(auto&& v) + requires(std::is_same_v, std::any>) + : value_{std::forward(v)} + { + } + + operator std::any&() noexcept + { + return value_; + } +}; + +} // namespace util::async::impl diff --git a/src/util/async/impl/ErasedOperation.hpp b/src/util/async/impl/ErasedOperation.hpp index 4ac97a661..8fbb9292d 100644 --- a/src/util/async/impl/ErasedOperation.hpp +++ b/src/util/async/impl/ErasedOperation.hpp @@ -3,8 +3,8 @@ #include "util/Assert.hpp" #include "util/async/Concepts.hpp" #include "util/async/Error.hpp" +#include "util/async/impl/Any.hpp" -#include #include #include #include @@ -37,7 +37,7 @@ public: pimpl_->wait(); } - std::expected + std::expected get() { return pimpl_->get(); @@ -64,7 +64,7 @@ private: virtual void wait() noexcept = 0; - virtual std::expected + virtual std::expected get() = 0; virtual void abort() = 0; @@ -93,14 +93,14 @@ private: } } - std::expected + std::expected get() override { if constexpr (not SomeOperationWithData) { ASSERT(false, "Called get() on an operation that does not support it"); std::unreachable(); } else { - // Note: return type of the operation was already wrapped to std::any by + // Note: return type of the operation was already wrapped to impl::Any by // AnyExecutionContext return operation.get(); } diff --git a/src/util/requests/impl/SslContext.cpp b/src/util/requests/impl/SslContext.cpp index b433f9f0e..c0c59996f 100644 --- a/src/util/requests/impl/SslContext.cpp +++ b/src/util/requests/impl/SslContext.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -43,18 +44,36 @@ constexpr std::array kCertFilePaths{ "/system/etc/security/cacerts", // Android }; +std::optional +readCertificateFile(std::filesystem::path const& path) +{ + if (not std::filesystem::exists(path)) { + return std::nullopt; + } + std::ifstream const fileStream{path, std::ios::in}; + if (not fileStream.is_open()) { + return std::nullopt; + } + std::stringstream buffer; + buffer << fileStream.rdbuf(); + return std::move(buffer).str(); +} + std::expected getRootCertificate() { + // Honor the OpenSSL-standard SSL_CERT_FILE environment variable first. Some + // environments (e.g. the Nix-based CI/runtime image) point it at their CA + // bundle instead of installing certificates at the well-known system paths. + if (char const* const certFile = std::getenv("SSL_CERT_FILE"); certFile != nullptr) { + if (auto contents = readCertificateFile(certFile); contents.has_value()) { + return *std::move(contents); + } + } + for (auto const& path : kCertFilePaths) { - if (std::filesystem::exists(path)) { - std::ifstream const fileStream{path, std::ios::in}; - if (not fileStream.is_open()) { - continue; - } - std::stringstream buffer; - buffer << fileStream.rdbuf(); - return std::move(buffer).str(); + if (auto contents = readCertificateFile(path); contents.has_value()) { + return *std::move(contents); } } return std::unexpected{RequestError{"SSL setup failed: could not find root certificate"}}; diff --git a/tests/common/util/MockExecutionContext.hpp b/tests/common/util/MockExecutionContext.hpp index 377c57c08..d9381ee5a 100644 --- a/tests/common/util/MockExecutionContext.hpp +++ b/tests/common/util/MockExecutionContext.hpp @@ -5,10 +5,10 @@ #include "util/MockStrand.hpp" #include "util/async/AnyStopToken.hpp" #include "util/async/Error.hpp" +#include "util/async/impl/Any.hpp" #include -#include #include #include #include @@ -18,6 +18,8 @@ struct MockExecutionContext { template using ValueType = std::expected; + using Any = util::async::impl::Any; + using StopSource = MockStopSource; using StopToken = MockStopToken; using Strand = MockStrand; @@ -34,36 +36,35 @@ struct MockExecutionContext { template using RepeatingOperation = MockRepeatingOperation; - MOCK_METHOD(Operation const&, execute, (std::function), ()); + MOCK_METHOD(Operation const&, execute, (std::function), ()); MOCK_METHOD( - Operation const&, + Operation const&, execute, - (std::function, std::optional), + (std::function, std::optional), () ); MOCK_METHOD( - StoppableOperation const&, + StoppableOperation const&, execute, - (std::function, - std::optional), + (std::function, std::optional), () ); MOCK_METHOD( - ScheduledOperation const&, + ScheduledOperation const&, scheduleAfter, - (std::chrono::milliseconds, std::function), + (std::chrono::milliseconds, std::function), () ); MOCK_METHOD( - ScheduledOperation const&, + ScheduledOperation const&, scheduleAfter, - (std::chrono::milliseconds, std::function), + (std::chrono::milliseconds, std::function), () ); MOCK_METHOD( - RepeatingOperation const&, + RepeatingOperation const&, executeRepeatedly, - (std::chrono::milliseconds, std::function), + (std::chrono::milliseconds, std::function), () ); MOCK_METHOD(void, submit, (std::function), ()); diff --git a/tests/common/util/MockStrand.hpp b/tests/common/util/MockStrand.hpp index 000c5f266..a45334c31 100644 --- a/tests/common/util/MockStrand.hpp +++ b/tests/common/util/MockStrand.hpp @@ -3,10 +3,10 @@ #include "util/MockOperation.hpp" #include "util/async/AnyStopToken.hpp" #include "util/async/Error.hpp" +#include "util/async/impl/Any.hpp" #include -#include #include #include #include @@ -16,6 +16,8 @@ struct MockStrand { template using ValueType = std::expected; + using Any = util::async::impl::Any; + template using Operation = MockOperation; @@ -25,30 +27,29 @@ struct MockStrand { template using RepeatingOperation = MockRepeatingOperation; - MOCK_METHOD(Operation const&, execute, (std::function), (const)); + MOCK_METHOD(Operation const&, execute, (std::function), (const)); MOCK_METHOD( - Operation const&, + Operation const&, execute, - (std::function, std::optional), + (std::function, std::optional), (const) ); MOCK_METHOD( - StoppableOperation const&, + StoppableOperation const&, execute, - (std::function), + (std::function), (const) ); MOCK_METHOD( - StoppableOperation const&, + StoppableOperation const&, execute, - (std::function, - std::optional), + (std::function, std::optional), (const) ); MOCK_METHOD( - RepeatingOperation const&, + RepeatingOperation const&, executeRepeatedly, - (std::chrono::milliseconds, std::function), + (std::chrono::milliseconds, std::function), (const) ); MOCK_METHOD(void, submit, (std::function), (const)); diff --git a/tests/integration/CMakeLists.txt b/tests/integration/CMakeLists.txt index b9312eed1..03b72f750 100644 --- a/tests/integration/CMakeLists.txt +++ b/tests/integration/CMakeLists.txt @@ -28,3 +28,5 @@ set_target_properties( clio_integration_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) + +patch_nix_binary(clio_integration_tests) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index df88b91b9..291f7300f 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -239,6 +239,8 @@ set_target_properties( PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR} ) +patch_nix_binary(clio_tests) + # Generate `coverage_report` target if coverage is enabled if(coverage) if(DEFINED CODE_COVERAGE_REPORT_FORMAT) diff --git a/tests/unit/util/async/AnyExecutionContextTests.cpp b/tests/unit/util/async/AnyExecutionContextTests.cpp index da28002f9..704390c15 100644 --- a/tests/unit/util/async/AnyExecutionContextTests.cpp +++ b/tests/unit/util/async/AnyExecutionContextTests.cpp @@ -10,6 +10,7 @@ #include "util/async/Outcome.hpp" #include "util/async/context/SyncExecutionContext.hpp" #include "util/async/context/impl/Cancellation.hpp" +#include "util/async/impl/Any.hpp" #include "util/async/impl/ErasedOperation.hpp" #include @@ -105,8 +106,8 @@ struct AnyExecutionContextTests : Test { TEST_F(AnyExecutionContextTests, Move) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockExecutionContext, execute(A>())) + auto mockOp = OperationType{}; + EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); @@ -116,8 +117,8 @@ TEST_F(AnyExecutionContextTests, Move) TEST_F(AnyExecutionContextTests, CopyIsRefCounted) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockExecutionContext, execute(A>())) + auto mockOp = OperationType{}; + EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); @@ -127,8 +128,8 @@ TEST_F(AnyExecutionContextTests, CopyIsRefCounted) TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoid) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockExecutionContext, execute(A>())) + auto mockOp = OperationType{}; + EXPECT_CALL(mockExecutionContext, execute(A>())) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); @@ -140,17 +141,17 @@ TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoid) TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoidThrowsException) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockExecutionContext, execute(A>())) - .WillOnce([](auto&&) -> OperationType const& { throw 0; }); + auto mockOp = OperationType{}; + EXPECT_CALL(mockExecutionContext, execute(A>())) + .WillOnce([](auto&&) -> OperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([] { throw 0; })); } TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoid) { - auto mockOp = StoppableOperationType{}; - EXPECT_CALL(mockExecutionContext, execute(A>(), _)) + auto mockOp = StoppableOperationType{}; + EXPECT_CALL(mockExecutionContext, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); @@ -162,17 +163,17 @@ TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoid) TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoidThrowsException) { - EXPECT_CALL(mockExecutionContext, execute(A>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockExecutionContext, execute(A>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([](auto) { throw 0; })); } TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndReturnValue) { - auto mockOp = StoppableOperationType{}; + auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); - EXPECT_CALL(mockExecutionContext, execute(A>(), _)) + EXPECT_CALL(mockExecutionContext, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = ctx.execute([](auto) -> int { throw 0; }); @@ -183,19 +184,19 @@ TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndReturnValue) TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndReturnValueThrowsException) { - EXPECT_CALL(mockExecutionContext, execute(A>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockExecutionContext, execute(A>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([](auto) -> int { throw 0; })); } TEST_F(AnyExecutionContextTests, TimerCancellation) { - auto mockScheduledOp = ScheduledOperationType{}; + auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, cancel()); EXPECT_CALL( mockExecutionContext, - scheduleAfter(std::chrono::milliseconds{12}, A>()) + scheduleAfter(std::chrono::milliseconds{12}, A>()) ) .WillOnce(ReturnRef(mockScheduledOp)); @@ -207,13 +208,13 @@ TEST_F(AnyExecutionContextTests, TimerCancellation) TEST_F(AnyExecutionContextTests, TimerExecuted) { - auto mockScheduledOp = ScheduledOperationType{}; + auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL( mockExecutionContext, - scheduleAfter(std::chrono::milliseconds{12}, A>()) + scheduleAfter(std::chrono::milliseconds{12}, A>()) ) - .WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType const& { + .WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType const& { return mockScheduledOp; }); @@ -225,12 +226,12 @@ TEST_F(AnyExecutionContextTests, TimerExecuted) TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerCancellation) { - auto mockScheduledOp = ScheduledOperationType{}; + auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, cancel()); EXPECT_CALL( mockExecutionContext, scheduleAfter( - std::chrono::milliseconds{12}, A>() + std::chrono::milliseconds{12}, A>() ) ) .WillOnce(ReturnRef(mockScheduledOp)); @@ -243,15 +244,15 @@ TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerCancellation) TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerExecuted) { - auto mockScheduledOp = ScheduledOperationType{}; + auto mockScheduledOp = ScheduledOperationType{}; EXPECT_CALL(mockScheduledOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL( mockExecutionContext, scheduleAfter( - std::chrono::milliseconds{12}, A>() + std::chrono::milliseconds{12}, A>() ) ) - .WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType const& { + .WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType const& { return mockScheduledOp; }); @@ -264,13 +265,13 @@ TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerExecuted) TEST_F(AnyExecutionContextTests, RepeatingOperation) { - auto mockRepeatingOp = RepeatingOperationType{}; + auto mockRepeatingOp = RepeatingOperationType{}; EXPECT_CALL(mockRepeatingOp, wait()); EXPECT_CALL( mockExecutionContext, - executeRepeatedly(std::chrono::milliseconds{1}, A>()) + executeRepeatedly(std::chrono::milliseconds{1}, A>()) ) - .WillOnce([&mockRepeatingOp] -> RepeatingOperationType const& { + .WillOnce([&mockRepeatingOp] -> RepeatingOperationType const& { return mockRepeatingOp; }); @@ -281,11 +282,11 @@ TEST_F(AnyExecutionContextTests, RepeatingOperation) TEST_F(AnyExecutionContextTests, StrandExecuteWithVoid) { - auto mockOp = OperationType{}; + auto mockOp = OperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>())).WillOnce(ReturnRef(mockOp)); + EXPECT_CALL(mockStrand, execute(A>())).WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); @@ -300,8 +301,8 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithVoidThrowsException) { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>())) - .WillOnce([](auto&&) -> OperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(A>())) + .WillOnce([](auto&&) -> OperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); @@ -311,11 +312,11 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithVoidThrowsException) TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValue) { - auto mockOp = OperationType{}; + auto mockOp = OperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>())).WillOnce(ReturnRef(mockOp)); + EXPECT_CALL(mockStrand, execute(A>())).WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); @@ -330,8 +331,8 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValueThrowsException) { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>())) - .WillOnce([](auto&&) -> OperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(A>())) + .WillOnce([](auto&&) -> OperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); @@ -341,11 +342,11 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValueThrowsException) TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoid) { - auto mockOp = StoppableOperationType{}; + auto mockOp = StoppableOperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>(), _)) + EXPECT_CALL(mockStrand, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); @@ -361,8 +362,8 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoidThrowsExceptio { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(A>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); @@ -372,11 +373,11 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoidThrowsExceptio TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndReturnValue) { - auto mockOp = StoppableOperationType{}; + auto mockOp = StoppableOperationType{}; auto mockStrand = StrandType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>(), _)) + EXPECT_CALL(mockStrand, execute(A>(), _)) .WillOnce(ReturnRef(mockOp)); auto strand = ctx.makeStrand(); @@ -392,8 +393,8 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndReturnValueThrowsE { auto mockStrand = StrandType{}; EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand)); - EXPECT_CALL(mockStrand, execute(A>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(A>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); auto strand = ctx.makeStrand(); static_assert(std::is_same_v); diff --git a/tests/unit/util/async/AnyOperationTests.cpp b/tests/unit/util/async/AnyOperationTests.cpp index df4605d87..ce62dec4d 100644 --- a/tests/unit/util/async/AnyOperationTests.cpp +++ b/tests/unit/util/async/AnyOperationTests.cpp @@ -2,6 +2,7 @@ #include "util/MockOperation.hpp" #include "util/async/AnyOperation.hpp" #include "util/async/Error.hpp" +#include "util/async/impl/Any.hpp" #include #include @@ -15,10 +16,10 @@ using namespace util::async; using namespace ::testing; struct AnyOperationTests : virtual Test { - using OperationType = MockOperation>; - using StoppableOperationType = MockStoppableOperation>; - using ScheduledOperationType = MockScheduledOperation>; - using RepeatingOperationType = MockRepeatingOperation>; + using OperationType = MockOperation>; + using StoppableOperationType = MockStoppableOperation>; + using ScheduledOperationType = MockScheduledOperation>; + using RepeatingOperationType = MockRepeatingOperation>; NaggyMock mockOp; NaggyMock mockStoppableOp; @@ -40,7 +41,7 @@ struct AnyOperationTests : virtual Test { TEST_F(AnyOperationTests, Move) { - EXPECT_CALL(mockOp, get()).WillOnce(Return(std::any{})); + EXPECT_CALL(mockOp, get()).WillOnce(Return(impl::Any{})); auto yoink = std::move(voidOp); auto res = yoink.get(); ASSERT_TRUE(res); @@ -48,7 +49,7 @@ TEST_F(AnyOperationTests, Move) TEST_F(AnyOperationTests, VoidDataYieldsNoError) { - EXPECT_CALL(mockOp, get()).WillOnce(Return(std::any{})); + EXPECT_CALL(mockOp, get()).WillOnce(Return(impl::Any{})); auto res = voidOp.get(); ASSERT_TRUE(res); } diff --git a/tests/unit/util/async/AnyStrandTests.cpp b/tests/unit/util/async/AnyStrandTests.cpp index c0a87c31d..b60cb600f 100644 --- a/tests/unit/util/async/AnyStrandTests.cpp +++ b/tests/unit/util/async/AnyStrandTests.cpp @@ -3,6 +3,7 @@ #include "util/async/AnyOperation.hpp" #include "util/async/AnyStopToken.hpp" #include "util/async/AnyStrand.hpp" +#include "util/async/impl/Any.hpp" #include #include @@ -33,8 +34,8 @@ struct AnyStrandTests : ::testing::Test { TEST_F(AnyStrandTests, Move) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); + auto mockOp = OperationType{}; + EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); EXPECT_CALL(mockOp, get()); auto mineNow = std::move(strand); @@ -43,8 +44,8 @@ TEST_F(AnyStrandTests, Move) TEST_F(AnyStrandTests, CopyIsRefCounted) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); + auto mockOp = OperationType{}; + EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); auto yoink = strand; ASSERT_TRUE(yoink.execute([] { throw 0; }).get()); @@ -52,8 +53,8 @@ TEST_F(AnyStrandTests, CopyIsRefCounted) TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoid) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); + auto mockOp = OperationType{}; + EXPECT_CALL(mockStrand, execute(An>())).WillOnce(ReturnRef(mockOp)); auto op = strand.execute([] {}); static_assert(std::is_same_v>); @@ -63,17 +64,17 @@ TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoid) TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoidThrowsException) { - auto mockOp = OperationType{}; - EXPECT_CALL(mockStrand, execute(An>())) - .WillOnce([](auto&&) -> OperationType const& { throw 0; }); + auto mockOp = OperationType{}; + EXPECT_CALL(mockStrand, execute(An>())) + .WillOnce([](auto&&) -> OperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([] {})); } TEST_F(AnyStrandTests, ExecuteWithStopTokenAndVoid) { - auto mockOp = StoppableOperationType{}; - EXPECT_CALL(mockStrand, execute(An>(), _)) + auto mockOp = StoppableOperationType{}; + EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = strand.execute([](auto) {}); @@ -84,17 +85,17 @@ TEST_F(AnyStrandTests, ExecuteWithStopTokenAndVoid) TEST_F(AnyStrandTests, ExecuteWithStopTokenAndVoidThrowsException) { - EXPECT_CALL(mockStrand, execute(An>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(An>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) {})); } TEST_F(AnyStrandTests, ExecuteWithStopTokenAndReturnValue) { - auto mockOp = StoppableOperationType{}; + auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); - EXPECT_CALL(mockStrand, execute(An>(), _)) + EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = strand.execute([](auto) { return 42; }); @@ -105,17 +106,17 @@ TEST_F(AnyStrandTests, ExecuteWithStopTokenAndReturnValue) TEST_F(AnyStrandTests, ExecuteWithStopTokenAndReturnValueThrowsException) { - EXPECT_CALL(mockStrand, execute(An>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(An>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) { return 42; })); } TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValue) { - auto mockOp = StoppableOperationType{}; + auto mockOp = StoppableOperationType{}; EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any(42))); - EXPECT_CALL(mockStrand, execute(An>(), _)) + EXPECT_CALL(mockStrand, execute(An>(), _)) .WillOnce(ReturnRef(mockOp)); auto op = strand.execute([](auto) { return 42; }, std::chrono::milliseconds{1}); @@ -126,8 +127,8 @@ TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValue) TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValueThrowsException) { - EXPECT_CALL(mockStrand, execute(An>(), _)) - .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); + EXPECT_CALL(mockStrand, execute(An>(), _)) + .WillOnce([](auto&&, auto) -> StoppableOperationType const& { throw 0; }); EXPECT_ANY_THROW( [[maybe_unused]] auto unused = @@ -137,12 +138,12 @@ TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValueThrowsExcepti TEST_F(AnyStrandTests, RepeatingOperation) { - auto mockRepeatingOp = RepeatingOperationType{}; + auto mockRepeatingOp = RepeatingOperationType{}; EXPECT_CALL(mockRepeatingOp, wait()); EXPECT_CALL( - mockStrand, executeRepeatedly(std::chrono::milliseconds{1}, A>()) + mockStrand, executeRepeatedly(std::chrono::milliseconds{1}, A>()) ) - .WillOnce([&mockRepeatingOp] -> RepeatingOperationType const& { + .WillOnce([&mockRepeatingOp] -> RepeatingOperationType const& { return mockRepeatingOp; });