ci: Use Nix-based images for all workflows except pre-commit (#3098)

This commit is contained in:
Ayaz Salikhov
2026-06-16 22:50:43 +01:00
committed by GitHub
parent e24216c8a9
commit fa057bd876
69 changed files with 846 additions and 1431 deletions

View File

@@ -17,3 +17,6 @@ endfunction()
function(append_coverage_compiler_flags_to_target name mode)
endfunction()
function(patch_nix_binary target)
endfunction()

View File

@@ -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 "<os>_<build_type>_<compiler>" 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}"

View File

@@ -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}"

View File

@@ -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}"

View File

@@ -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

View File

@@ -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

102
.github/dependabot.yml vendored
View File

@@ -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

View File

@@ -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,
}
)

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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' }}

View File

@@ -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

View File

@@ -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

View File

@@ -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 || '' }}

View File

@@ -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 }}

View File

@@ -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:

View File

@@ -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 }}

View File

@@ -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: |

View File

@@ -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

View File

@@ -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.

View File

@@ -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' }}

View File

@@ -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)

View File

@@ -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()

View File

@@ -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 "$<TARGET_FILE:${target}>"
COMMENT "Patching ${target}: set default loader, remove rpath"
VERBATIM
)
endfunction()

47
cmake/Sanitizers.cmake Normal file
View File

@@ -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
$<$<COMPILE_LANGUAGE:CXX>:${sanitizers_compiler_flags}>
$<$<COMPILE_LANGUAGE:C>:${sanitizers_compiler_flags}>
)
target_link_options(clio_options INTERFACE ${sanitizers_linker_flags})

View File

@@ -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($<$<LINK_LANGUAGE:CXX>:-stdlib=libstdc++>)
endif()

View File

@@ -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

View File

@@ -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": [

7
conan/global.conf Normal file
View File

@@ -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

30
conan/init.sh Executable file
View File

@@ -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

8
conan/profiles/ci Normal file
View File

@@ -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 %}

28
conan/profiles/default Normal file
View File

@@ -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 %}

120
conan/profiles/sanitizers Normal file
View File

@@ -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 %}

View File

@@ -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":

View File

@@ -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

View File

@@ -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`).

View File

@@ -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"]

View File

@@ -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"}

View File

@@ -1,2 +0,0 @@
core.download:parallel={{os.cpu_count()}}
core.upload:parallel={{os.cpu_count()}}

View File

@@ -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"]

View File

@@ -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/*

View File

@@ -1,3 +0,0 @@
# Clang compiler
This image contains clang compiler to build <https://github.com/XRPLF/clio>.

View File

@@ -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

View File

@@ -1,3 +0,0 @@
# GCC compiler
This image contains GCC compiler to build <https://github.com/XRPLF/clio>.

View File

@@ -1,7 +0,0 @@
Package: gcc-15-ubuntu-UBUNTUVERSION
Version: VERSION
Architecture: TARGETARCH
Maintainer: Alex Kremer <akremer@ripple.com>
Uploaders: Ayaz Salikhov <asalikhov@ripple.com>
Description: GCC VERSION build for ubuntu UBUNTUVERSION
Depends: binutils, libc6-dev

View File

@@ -1,2 +0,0 @@
# Path to the directory containing libstdc++.so.6
/usr/lib64

View File

@@ -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

View File

@@ -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

View File

@@ -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
```

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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 <any>
@@ -68,10 +69,10 @@ public:
execute(SomeHandlerWithoutStopToken auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn)>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
return AnyOperation<RetType>(
pimpl_->execute([fn = std::forward<decltype(fn)>(fn)] mutable -> std::any {
pimpl_->execute([fn = std::forward<decltype(fn)>(fn)] mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn));
return {};
@@ -94,10 +95,10 @@ public:
execute(SomeHandlerWith<AnyStopToken> auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn), AnyStopToken>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
return AnyOperation<RetType>(pimpl_->execute(
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> std::any {
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn), std::move(stopToken));
return {};
@@ -123,10 +124,10 @@ public:
execute(SomeHandlerWith<AnyStopToken> auto&& fn, SomeStdDuration auto timeout)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn), AnyStopToken>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
return AnyOperation<RetType>(pimpl_->execute(
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> std::any {
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn), std::move(stopToken));
return {};
@@ -153,11 +154,11 @@ public:
scheduleAfter(SomeStdDuration auto delay, SomeHandlerWith<AnyStopToken> auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn), AnyStopToken>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
auto const millis = std::chrono::duration_cast<std::chrono::milliseconds>(delay);
return AnyOperation<RetType>(pimpl_->scheduleAfter(
millis, [fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> std::any {
millis, [fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn), std::move(stopToken));
return {};
@@ -184,13 +185,13 @@ public:
scheduleAfter(SomeStdDuration auto delay, SomeHandlerWith<AnyStopToken, bool> auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn), AnyStopToken, bool>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
auto const millis = std::chrono::duration_cast<std::chrono::milliseconds>(delay);
return AnyOperation<RetType>(pimpl_->scheduleAfter(
millis,
[fn = std::forward<decltype(fn)>(fn)](auto stopToken, auto cancelled) mutable
-> std::any {
-> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn), std::move(stopToken), cancelled);
return {};
@@ -214,12 +215,12 @@ public:
executeRepeatedly(SomeStdDuration auto interval, SomeHandlerWithoutStopToken auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn)>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
auto const millis = std::chrono::duration_cast<std::chrono::milliseconds>(interval);
return AnyOperation<RetType>( //
pimpl_->executeRepeatedly(
millis, [fn = std::forward<decltype(fn)>(fn)] mutable -> std::any {
millis, [fn = std::forward<decltype(fn)>(fn)] mutable -> impl::Any {
std::invoke(std::forward<decltype(fn)>(fn));
return {};
}
@@ -277,18 +278,18 @@ private:
virtual impl::ErasedOperation
execute(
std::function<std::any(AnyStopToken)>,
std::function<impl::Any(AnyStopToken)>,
std::optional<std::chrono::milliseconds> timeout = std::nullopt
) = 0;
virtual impl::ErasedOperation execute(std::function<std::any()>) = 0;
virtual impl::ErasedOperation execute(std::function<impl::Any()>) = 0;
virtual impl::ErasedOperation
scheduleAfter(std::chrono::milliseconds, std::function<std::any(AnyStopToken)>) = 0;
scheduleAfter(std::chrono::milliseconds, std::function<impl::Any(AnyStopToken)>) = 0;
virtual impl::ErasedOperation scheduleAfter(
std::chrono::milliseconds,
std::function<std::any(AnyStopToken, bool)>
std::function<impl::Any(AnyStopToken, bool)>
) = 0;
virtual impl::ErasedOperation
executeRepeatedly(std::chrono::milliseconds, std::function<std::any()>) = 0;
executeRepeatedly(std::chrono::milliseconds, std::function<impl::Any()>) = 0;
virtual void submit(std::function<void()>) = 0;
virtual AnyStrand
makeStrand() = 0;
@@ -309,7 +310,7 @@ private:
impl::ErasedOperation
execute(
std::function<std::any(AnyStopToken)> fn,
std::function<impl::Any(AnyStopToken)> fn,
std::optional<std::chrono::milliseconds> timeout
) override
{
@@ -317,7 +318,7 @@ private:
}
impl::ErasedOperation
execute(std::function<std::any()> fn) override
execute(std::function<impl::Any()> fn) override
{
return ctx.execute(std::move(fn));
}
@@ -325,7 +326,7 @@ private:
impl::ErasedOperation
scheduleAfter(
std::chrono::milliseconds delay,
std::function<std::any(AnyStopToken)> fn
std::function<impl::Any(AnyStopToken)> fn
) override
{
return ctx.scheduleAfter(delay, std::move(fn));
@@ -334,14 +335,17 @@ private:
impl::ErasedOperation
scheduleAfter(
std::chrono::milliseconds delay,
std::function<std::any(AnyStopToken, bool)> fn
std::function<impl::Any(AnyStopToken, bool)> fn
) override
{
return ctx.scheduleAfter(delay, std::move(fn));
}
impl::ErasedOperation
executeRepeatedly(std::chrono::milliseconds interval, std::function<std::any()> fn) override
executeRepeatedly(
std::chrono::milliseconds interval,
std::function<impl::Any()> fn
) override
{
return ctx.executeRepeatedly(interval, std::move(fn));
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include "util/async/Error.hpp"
#include "util/async/impl/Any.hpp"
#include "util/async/impl/ErasedOperation.hpp"
#include <fmt/format.h>

View File

@@ -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 <any>
@@ -46,10 +47,10 @@ public:
execute(SomeHandlerWithoutStopToken auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn)>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
return AnyOperation<RetType>( //
pimpl_->execute([fn = std::forward<decltype(fn)>(fn)] mutable -> std::any {
pimpl_->execute([fn = std::forward<decltype(fn)>(fn)] mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn));
return {};
@@ -70,11 +71,11 @@ public:
execute(SomeHandlerWith<AnyStopToken> auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn), AnyStopToken>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
return AnyOperation<RetType>( //
pimpl_->execute(
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> std::any {
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn), std::move(stopToken));
return {};
@@ -99,11 +100,11 @@ public:
execute(SomeHandlerWith<AnyStopToken> auto&& fn, SomeStdDuration auto timeout)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn), AnyStopToken>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
return AnyOperation<RetType>( //
pimpl_->execute(
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> std::any {
[fn = std::forward<decltype(fn)>(fn)](auto stopToken) mutable -> impl::Any {
if constexpr (std::is_void_v<RetType>) {
std::invoke(std::forward<decltype(fn)>(fn), std::move(stopToken));
return {};
@@ -129,12 +130,12 @@ public:
executeRepeatedly(SomeStdDuration auto interval, SomeHandlerWithoutStopToken auto&& fn)
{
using RetType = std::decay_t<std::invoke_result_t<decltype(fn)>>;
static_assert(not std::is_same_v<RetType, std::any>);
static_assert(not std::is_same_v<RetType, impl::Any>);
auto const millis = std::chrono::duration_cast<std::chrono::milliseconds>(interval);
return AnyOperation<RetType>( //
pimpl_->executeRepeatedly(
millis, [fn = std::forward<decltype(fn)>(fn)] mutable -> std::any {
millis, [fn = std::forward<decltype(fn)>(fn)] mutable -> impl::Any {
std::invoke(std::forward<decltype(fn)>(fn));
return {};
}
@@ -160,12 +161,12 @@ private:
[[nodiscard]] virtual impl::ErasedOperation
execute(
std::function<std::any(AnyStopToken)>,
std::function<impl::Any(AnyStopToken)>,
std::optional<std::chrono::milliseconds> timeout = std::nullopt
) = 0;
[[nodiscard]] virtual impl::ErasedOperation execute(std::function<std::any()>) = 0;
[[nodiscard]] virtual impl::ErasedOperation execute(std::function<impl::Any()>) = 0;
[[nodiscard]] virtual impl::ErasedOperation
executeRepeatedly(std::chrono::milliseconds, std::function<std::any()>) = 0;
executeRepeatedly(std::chrono::milliseconds, std::function<impl::Any()>) = 0;
virtual void submit(std::function<void()>) = 0;
};
@@ -181,7 +182,7 @@ private:
[[nodiscard]] impl::ErasedOperation
execute(
std::function<std::any(AnyStopToken)> fn,
std::function<impl::Any(AnyStopToken)> fn,
std::optional<std::chrono::milliseconds> timeout
) override
{
@@ -189,13 +190,16 @@ private:
}
[[nodiscard]] impl::ErasedOperation
execute(std::function<std::any()> fn) override
execute(std::function<impl::Any()> fn) override
{
return strand.execute(std::move(fn));
}
impl::ErasedOperation
executeRepeatedly(std::chrono::milliseconds interval, std::function<std::any()> fn) override
executeRepeatedly(
std::chrono::milliseconds interval,
std::function<impl::Any()> fn
) override
{
return strand.executeRepeatedly(interval, std::move(fn));
}

View File

@@ -6,11 +6,6 @@
#include <string>
#include <utility>
// for the static_assert at the bottom which fixes clang compilation:
// see: https://godbolt.org/z/fzTjMd7G1 vs https://godbolt.org/z/jhKG7deen
#include <any>
#include <expected>
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<std::expected<std::any, ExecutionError>>);
} // namespace util::async

View File

@@ -0,0 +1,41 @@
#pragma once
#include <any>
#include <type_traits>
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 `<expected>` when the
* value type is a raw `std::any`: `std::any`'s greedy templated constructor makes
* `std::expected<std::any, E>` ask whether `std::any` is constructible from
* `std::expected<std::any, E>`, 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::decay_t<decltype(v)>, std::any>)
: value_{std::forward<decltype(v)>(v)}
{
}
operator std::any&() noexcept
{
return value_;
}
};
} // namespace util::async::impl

View File

@@ -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 <any>
#include <expected>
#include <memory>
#include <type_traits>
@@ -37,7 +37,7 @@ public:
pimpl_->wait();
}
std::expected<std::any, ExecutionError>
std::expected<Any, ExecutionError>
get()
{
return pimpl_->get();
@@ -64,7 +64,7 @@ private:
virtual void
wait() noexcept = 0;
virtual std::expected<std::any, ExecutionError>
virtual std::expected<Any, ExecutionError>
get() = 0;
virtual void
abort() = 0;
@@ -93,14 +93,14 @@ private:
}
}
std::expected<std::any, ExecutionError>
std::expected<Any, ExecutionError>
get() override
{
if constexpr (not SomeOperationWithData<OpType>) {
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();
}

View File

@@ -13,6 +13,7 @@
#include <array>
#include <cstddef>
#include <cstdlib>
#include <expected>
#include <filesystem>
#include <fstream>
@@ -43,18 +44,36 @@ constexpr std::array kCertFilePaths{
"/system/etc/security/cacerts", // Android
};
std::optional<std::string>
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<std::string, RequestError>
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"}};

View File

@@ -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 <gmock/gmock.h>
#include <any>
#include <chrono>
#include <expected>
#include <functional>
@@ -18,6 +18,8 @@ struct MockExecutionContext {
template <typename T>
using ValueType = std::expected<T, util::async::ExecutionError>;
using Any = util::async::impl::Any;
using StopSource = MockStopSource;
using StopToken = MockStopToken;
using Strand = MockStrand;
@@ -34,36 +36,35 @@ struct MockExecutionContext {
template <typename T>
using RepeatingOperation = MockRepeatingOperation<T>;
MOCK_METHOD(Operation<std::any> const&, execute, (std::function<std::any()>), ());
MOCK_METHOD(Operation<Any> const&, execute, (std::function<Any()>), ());
MOCK_METHOD(
Operation<std::any> const&,
Operation<Any> const&,
execute,
(std::function<std::any()>, std::optional<std::chrono::milliseconds>),
(std::function<Any()>, std::optional<std::chrono::milliseconds>),
()
);
MOCK_METHOD(
StoppableOperation<std::any> const&,
StoppableOperation<Any> const&,
execute,
(std::function<std::any(util::async::AnyStopToken)>,
std::optional<std::chrono::milliseconds>),
(std::function<Any(util::async::AnyStopToken)>, std::optional<std::chrono::milliseconds>),
()
);
MOCK_METHOD(
ScheduledOperation<std::any> const&,
ScheduledOperation<Any> const&,
scheduleAfter,
(std::chrono::milliseconds, std::function<std::any(util::async::AnyStopToken)>),
(std::chrono::milliseconds, std::function<Any(util::async::AnyStopToken)>),
()
);
MOCK_METHOD(
ScheduledOperation<std::any> const&,
ScheduledOperation<Any> const&,
scheduleAfter,
(std::chrono::milliseconds, std::function<std::any(util::async::AnyStopToken, bool)>),
(std::chrono::milliseconds, std::function<Any(util::async::AnyStopToken, bool)>),
()
);
MOCK_METHOD(
RepeatingOperation<std::any> const&,
RepeatingOperation<Any> const&,
executeRepeatedly,
(std::chrono::milliseconds, std::function<std::any()>),
(std::chrono::milliseconds, std::function<Any()>),
()
);
MOCK_METHOD(void, submit, (std::function<void()>), ());

View File

@@ -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 <gmock/gmock.h>
#include <any>
#include <chrono>
#include <expected>
#include <functional>
@@ -16,6 +16,8 @@ struct MockStrand {
template <typename T>
using ValueType = std::expected<T, util::async::ExecutionError>;
using Any = util::async::impl::Any;
template <typename T>
using Operation = MockOperation<T>;
@@ -25,30 +27,29 @@ struct MockStrand {
template <typename T>
using RepeatingOperation = MockRepeatingOperation<T>;
MOCK_METHOD(Operation<std::any> const&, execute, (std::function<std::any()>), (const));
MOCK_METHOD(Operation<Any> const&, execute, (std::function<Any()>), (const));
MOCK_METHOD(
Operation<std::any> const&,
Operation<Any> const&,
execute,
(std::function<std::any()>, std::optional<std::chrono::milliseconds>),
(std::function<Any()>, std::optional<std::chrono::milliseconds>),
(const)
);
MOCK_METHOD(
StoppableOperation<std::any> const&,
StoppableOperation<Any> const&,
execute,
(std::function<std::any(util::async::AnyStopToken)>),
(std::function<Any(util::async::AnyStopToken)>),
(const)
);
MOCK_METHOD(
StoppableOperation<std::any> const&,
StoppableOperation<Any> const&,
execute,
(std::function<std::any(util::async::AnyStopToken)>,
std::optional<std::chrono::milliseconds>),
(std::function<Any(util::async::AnyStopToken)>, std::optional<std::chrono::milliseconds>),
(const)
);
MOCK_METHOD(
RepeatingOperation<std::any> const&,
RepeatingOperation<Any> const&,
executeRepeatedly,
(std::chrono::milliseconds, std::function<std::any()>),
(std::chrono::milliseconds, std::function<Any()>),
(const)
);
MOCK_METHOD(void, submit, (std::function<void()>), (const));

View File

@@ -28,3 +28,5 @@ set_target_properties(
clio_integration_tests
PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
)
patch_nix_binary(clio_integration_tests)

View File

@@ -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)

View File

@@ -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 <gmock/gmock.h>
@@ -105,8 +106,8 @@ struct AnyExecutionContextTests : Test {
TEST_F(AnyExecutionContextTests, Move)
{
auto mockOp = OperationType<std::any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any()>>()))
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any()>>()))
.WillOnce(ReturnRef(mockOp));
EXPECT_CALL(mockOp, get());
@@ -116,8 +117,8 @@ TEST_F(AnyExecutionContextTests, Move)
TEST_F(AnyExecutionContextTests, CopyIsRefCounted)
{
auto mockOp = OperationType<std::any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any()>>()))
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any()>>()))
.WillOnce(ReturnRef(mockOp));
EXPECT_CALL(mockOp, get());
@@ -127,8 +128,8 @@ TEST_F(AnyExecutionContextTests, CopyIsRefCounted)
TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoid)
{
auto mockOp = OperationType<std::any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any()>>()))
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any()>>()))
.WillOnce(ReturnRef(mockOp));
EXPECT_CALL(mockOp, get());
@@ -140,17 +141,17 @@ TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoid)
TEST_F(AnyExecutionContextTests, ExecuteWithoutTokenAndVoidThrowsException)
{
auto mockOp = OperationType<std::any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any()>>()))
.WillOnce([](auto&&) -> OperationType<std::any> const& { throw 0; });
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any()>>()))
.WillOnce([](auto&&) -> OperationType<impl::Any> const& { throw 0; });
EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([] { throw 0; }));
}
TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoid)
{
auto mockOp = StoppableOperationType<std::any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any(AnyStopToken)>>(), _))
auto mockOp = StoppableOperationType<impl::Any>{};
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce(ReturnRef(mockOp));
EXPECT_CALL(mockOp, get());
@@ -162,17 +163,17 @@ TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoid)
TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndVoidThrowsException)
{
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> const& { throw 0; });
EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([](auto) { throw 0; }));
}
TEST_F(AnyExecutionContextTests, ExecuteWithStopTokenAndReturnValue)
{
auto mockOp = StoppableOperationType<std::any>{};
auto mockOp = StoppableOperationType<impl::Any>{};
EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(mockExecutionContext, execute(A<std::function<std::any(AnyStopToken)>>(), _))
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.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<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockExecutionContext, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> const& { throw 0; });
EXPECT_ANY_THROW([[maybe_unused]] auto unused = ctx.execute([](auto) -> int { throw 0; }));
}
TEST_F(AnyExecutionContextTests, TimerCancellation)
{
auto mockScheduledOp = ScheduledOperationType<std::any>{};
auto mockScheduledOp = ScheduledOperationType<impl::Any>{};
EXPECT_CALL(mockScheduledOp, cancel());
EXPECT_CALL(
mockExecutionContext,
scheduleAfter(std::chrono::milliseconds{12}, A<std::function<std::any(AnyStopToken)>>())
scheduleAfter(std::chrono::milliseconds{12}, A<std::function<impl::Any(AnyStopToken)>>())
)
.WillOnce(ReturnRef(mockScheduledOp));
@@ -207,13 +208,13 @@ TEST_F(AnyExecutionContextTests, TimerCancellation)
TEST_F(AnyExecutionContextTests, TimerExecuted)
{
auto mockScheduledOp = ScheduledOperationType<std::any>{};
auto mockScheduledOp = ScheduledOperationType<impl::Any>{};
EXPECT_CALL(mockScheduledOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(
mockExecutionContext,
scheduleAfter(std::chrono::milliseconds{12}, A<std::function<std::any(AnyStopToken)>>())
scheduleAfter(std::chrono::milliseconds{12}, A<std::function<impl::Any(AnyStopToken)>>())
)
.WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType<std::any> const& {
.WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType<impl::Any> const& {
return mockScheduledOp;
});
@@ -225,12 +226,12 @@ TEST_F(AnyExecutionContextTests, TimerExecuted)
TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerCancellation)
{
auto mockScheduledOp = ScheduledOperationType<std::any>{};
auto mockScheduledOp = ScheduledOperationType<impl::Any>{};
EXPECT_CALL(mockScheduledOp, cancel());
EXPECT_CALL(
mockExecutionContext,
scheduleAfter(
std::chrono::milliseconds{12}, A<std::function<std::any(AnyStopToken, bool)>>()
std::chrono::milliseconds{12}, A<std::function<impl::Any(AnyStopToken, bool)>>()
)
)
.WillOnce(ReturnRef(mockScheduledOp));
@@ -243,15 +244,15 @@ TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerCancellation)
TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerExecuted)
{
auto mockScheduledOp = ScheduledOperationType<std::any>{};
auto mockScheduledOp = ScheduledOperationType<impl::Any>{};
EXPECT_CALL(mockScheduledOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(
mockExecutionContext,
scheduleAfter(
std::chrono::milliseconds{12}, A<std::function<std::any(AnyStopToken, bool)>>()
std::chrono::milliseconds{12}, A<std::function<impl::Any(AnyStopToken, bool)>>()
)
)
.WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType<std::any> const& {
.WillOnce([&mockScheduledOp](auto, auto&&) -> ScheduledOperationType<impl::Any> const& {
return mockScheduledOp;
});
@@ -264,13 +265,13 @@ TEST_F(AnyExecutionContextTests, TimerWithBoolHandlerExecuted)
TEST_F(AnyExecutionContextTests, RepeatingOperation)
{
auto mockRepeatingOp = RepeatingOperationType<std::any>{};
auto mockRepeatingOp = RepeatingOperationType<impl::Any>{};
EXPECT_CALL(mockRepeatingOp, wait());
EXPECT_CALL(
mockExecutionContext,
executeRepeatedly(std::chrono::milliseconds{1}, A<std::function<std::any()>>())
executeRepeatedly(std::chrono::milliseconds{1}, A<std::function<impl::Any()>>())
)
.WillOnce([&mockRepeatingOp] -> RepeatingOperationType<std::any> const& {
.WillOnce([&mockRepeatingOp] -> RepeatingOperationType<impl::Any> const& {
return mockRepeatingOp;
});
@@ -281,11 +282,11 @@ TEST_F(AnyExecutionContextTests, RepeatingOperation)
TEST_F(AnyExecutionContextTests, StrandExecuteWithVoid)
{
auto mockOp = OperationType<std::any>{};
auto mockOp = OperationType<impl::Any>{};
auto mockStrand = StrandType{};
EXPECT_CALL(mockOp, get());
EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand));
EXPECT_CALL(mockStrand, execute(A<std::function<std::any()>>())).WillOnce(ReturnRef(mockOp));
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any()>>())).WillOnce(ReturnRef(mockOp));
auto strand = ctx.makeStrand();
static_assert(std::is_same_v<decltype(strand), AnyStrand>);
@@ -300,8 +301,8 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithVoidThrowsException)
{
auto mockStrand = StrandType{};
EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand));
EXPECT_CALL(mockStrand, execute(A<std::function<std::any()>>()))
.WillOnce([](auto&&) -> OperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any()>>()))
.WillOnce([](auto&&) -> OperationType<impl::Any> const& { throw 0; });
auto strand = ctx.makeStrand();
static_assert(std::is_same_v<decltype(strand), AnyStrand>);
@@ -311,11 +312,11 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithVoidThrowsException)
TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValue)
{
auto mockOp = OperationType<std::any>{};
auto mockOp = OperationType<impl::Any>{};
auto mockStrand = StrandType{};
EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand));
EXPECT_CALL(mockStrand, execute(A<std::function<std::any()>>())).WillOnce(ReturnRef(mockOp));
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any()>>())).WillOnce(ReturnRef(mockOp));
auto strand = ctx.makeStrand();
static_assert(std::is_same_v<decltype(strand), AnyStrand>);
@@ -330,8 +331,8 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValueThrowsException)
{
auto mockStrand = StrandType{};
EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand));
EXPECT_CALL(mockStrand, execute(A<std::function<std::any()>>()))
.WillOnce([](auto&&) -> OperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any()>>()))
.WillOnce([](auto&&) -> OperationType<impl::Any> const& { throw 0; });
auto strand = ctx.makeStrand();
static_assert(std::is_same_v<decltype(strand), AnyStrand>);
@@ -341,11 +342,11 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithReturnValueThrowsException)
TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoid)
{
auto mockOp = StoppableOperationType<std::any>{};
auto mockOp = StoppableOperationType<impl::Any>{};
auto mockStrand = StrandType{};
EXPECT_CALL(mockOp, get());
EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand));
EXPECT_CALL(mockStrand, execute(A<std::function<std::any(AnyStopToken)>>(), _))
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.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<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> const& { throw 0; });
auto strand = ctx.makeStrand();
static_assert(std::is_same_v<decltype(strand), AnyStrand>);
@@ -372,11 +373,11 @@ TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndVoidThrowsExceptio
TEST_F(AnyExecutionContextTests, StrandExecuteWithStopTokenAndReturnValue)
{
auto mockOp = StoppableOperationType<std::any>{};
auto mockOp = StoppableOperationType<impl::Any>{};
auto mockStrand = StrandType{};
EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(mockExecutionContext, makeStrand()).WillOnce(ReturnRef(mockStrand));
EXPECT_CALL(mockStrand, execute(A<std::function<std::any(AnyStopToken)>>(), _))
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.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<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(A<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> const& { throw 0; });
auto strand = ctx.makeStrand();
static_assert(std::is_same_v<decltype(strand), AnyStrand>);

View File

@@ -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 <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -15,10 +16,10 @@ using namespace util::async;
using namespace ::testing;
struct AnyOperationTests : virtual Test {
using OperationType = MockOperation<std::expected<std::any, ExecutionError>>;
using StoppableOperationType = MockStoppableOperation<std::expected<std::any, ExecutionError>>;
using ScheduledOperationType = MockScheduledOperation<std::expected<std::any, ExecutionError>>;
using RepeatingOperationType = MockRepeatingOperation<std::expected<std::any, ExecutionError>>;
using OperationType = MockOperation<std::expected<impl::Any, ExecutionError>>;
using StoppableOperationType = MockStoppableOperation<std::expected<impl::Any, ExecutionError>>;
using ScheduledOperationType = MockScheduledOperation<std::expected<impl::Any, ExecutionError>>;
using RepeatingOperationType = MockRepeatingOperation<std::expected<impl::Any, ExecutionError>>;
NaggyMock<OperationType> mockOp;
NaggyMock<StoppableOperationType> 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);
}

View File

@@ -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 <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -33,8 +34,8 @@ struct AnyStrandTests : ::testing::Test {
TEST_F(AnyStrandTests, Move)
{
auto mockOp = OperationType<std::any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<std::any()>>())).WillOnce(ReturnRef(mockOp));
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any()>>())).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<std::any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<std::any()>>())).WillOnce(ReturnRef(mockOp));
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any()>>())).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<std::any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<std::any()>>())).WillOnce(ReturnRef(mockOp));
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any()>>())).WillOnce(ReturnRef(mockOp));
auto op = strand.execute([] {});
static_assert(std::is_same_v<decltype(op), AnyOperation<void>>);
@@ -63,17 +64,17 @@ TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoid)
TEST_F(AnyStrandTests, ExecuteWithoutTokenAndVoidThrowsException)
{
auto mockOp = OperationType<std::any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<std::any()>>()))
.WillOnce([](auto&&) -> OperationType<std::any> const& { throw 0; });
auto mockOp = OperationType<impl::Any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any()>>()))
.WillOnce([](auto&&) -> OperationType<impl::Any> const& { throw 0; });
EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([] {}));
}
TEST_F(AnyStrandTests, ExecuteWithStopTokenAndVoid)
{
auto mockOp = StoppableOperationType<std::any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<std::any(AnyStopToken)>>(), _))
auto mockOp = StoppableOperationType<impl::Any>{};
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any(AnyStopToken)>>(), _))
.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<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> const& { throw 0; });
EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) {}));
}
TEST_F(AnyStrandTests, ExecuteWithStopTokenAndReturnValue)
{
auto mockOp = StoppableOperationType<std::any>{};
auto mockOp = StoppableOperationType<impl::Any>{};
EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(mockStrand, execute(An<std::function<std::any(AnyStopToken)>>(), _))
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any(AnyStopToken)>>(), _))
.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<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> const& { throw 0; });
EXPECT_ANY_THROW([[maybe_unused]] auto unused = strand.execute([](auto) { return 42; }));
}
TEST_F(AnyStrandTests, ExecuteWithTimeoutAndStopTokenAndReturnValue)
{
auto mockOp = StoppableOperationType<std::any>{};
auto mockOp = StoppableOperationType<impl::Any>{};
EXPECT_CALL(mockOp, get()).WillOnce(Return(std::make_any<int>(42)));
EXPECT_CALL(mockStrand, execute(An<std::function<std::any(AnyStopToken)>>(), _))
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any(AnyStopToken)>>(), _))
.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<std::function<std::any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<std::any> const& { throw 0; });
EXPECT_CALL(mockStrand, execute(An<std::function<impl::Any(AnyStopToken)>>(), _))
.WillOnce([](auto&&, auto) -> StoppableOperationType<impl::Any> 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<std::any>{};
auto mockRepeatingOp = RepeatingOperationType<impl::Any>{};
EXPECT_CALL(mockRepeatingOp, wait());
EXPECT_CALL(
mockStrand, executeRepeatedly(std::chrono::milliseconds{1}, A<std::function<std::any()>>())
mockStrand, executeRepeatedly(std::chrono::milliseconds{1}, A<std::function<impl::Any()>>())
)
.WillOnce([&mockRepeatingOp] -> RepeatingOperationType<std::any> const& {
.WillOnce([&mockRepeatingOp] -> RepeatingOperationType<impl::Any> const& {
return mockRepeatingOp;
});