name: Build and test configuration on: workflow_call: inputs: build_only: description: 'Whether to only build or to build and test the code ("true", "false").' required: true type: boolean build_type: description: 'The build type to use ("Debug", "Release").' required: true type: string ccache_enabled: description: "Whether to enable ccache." required: false type: boolean default: false cmake_args: description: "Additional arguments to pass to CMake." required: false type: string default: "" cmake_target: description: "The CMake target to build." required: true type: string runs_on: description: Runner to run the job on as a JSON string required: true type: string image: description: "The image to run in (leave empty to run natively)" required: true type: string config_name: description: "The configuration string (used for naming artifacts and such)." required: true type: string nproc_subtract: description: "The number of processors to subtract when calculating parallelism." required: false type: number default: 2 sanitizers: description: "The sanitizers to enable." required: false type: string default: "" secrets: CODECOV_TOKEN: description: "The Codecov token to use for uploading coverage reports." required: true defaults: run: shell: bash env: # Conan installs the generators in the build/generators directory, see the # layout() method in conanfile.py. We then run CMake from the build directory. BUILD_DIR: build jobs: build-and-test: name: ${{ inputs.config_name }} runs-on: ${{ fromJSON(inputs.runs_on) }} container: ${{ inputs.image != '' && inputs.image || null }} timeout-minutes: ${{ inputs.sanitizers != '' && 360 || 60 }} env: # Use a namespace to keep the objects separate for each configuration. CCACHE_NAMESPACE: ${{ inputs.config_name }} # Ccache supports both Redis and HTTP endpoints. # * For Redis, use the following format: redis://ip:port, see # https://github.com/ccache/ccache/wiki/Redis-storage. Note that TLS is # not directly supported by ccache, and requires use of a proxy. # * For HTTP use the following format: http://ip:port/cache when using # nginx as backend or http://ip:port|layout=bazel when using Bazel # Remote Cache, see https://github.com/ccache/ccache/wiki/HTTP-storage. # Note that HTTPS is not directly supported by ccache. CCACHE_REMOTE_ONLY: true CCACHE_REMOTE_STORAGE: http://cache.dev.ripplex.io:8080|layout=bazel # Ignore the creation and modification timestamps on files, since the # header files are copied into separate directories by CMake, which will # otherwise result in cache misses. CCACHE_SLOPPINESS: include_file_ctime,include_file_mtime # Determine if coverage and voidstar should be enabled. COVERAGE_ENABLED: ${{ contains(inputs.cmake_args, '-Dcoverage=ON') }} VOIDSTAR_ENABLED: ${{ contains(inputs.cmake_args, '-Dvoidstar=ON') }} SANITIZERS_ENABLED: ${{ inputs.sanitizers != '' }} steps: - name: Cleanup workspace (macOS and Windows) if: ${{ runner.os == 'macOS' || runner.os == 'Windows' }} uses: XRPLF/actions/cleanup-workspace@c7d9ce5ebb03c752a354889ecd870cadfc2b1cd4 - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Prepare runner uses: XRPLF/actions/prepare-runner@90f11ee655d1687824fb8793db770477d52afbab with: enable_ccache: ${{ inputs.ccache_enabled }} - name: Set ccache log file if: ${{ inputs.ccache_enabled && runner.debug == '1' }} run: echo "CCACHE_LOGFILE=${{ runner.temp }}/ccache.log" >> "${GITHUB_ENV}" - name: Print build environment uses: XRPLF/actions/print-build-env@59dec886e4afb05a1724443af08baccbc045b574 - name: Get number of processors uses: XRPLF/actions/get-nproc@cf0433aa74563aead044a1e395610c96d65a37cf id: nproc with: subtract: ${{ inputs.nproc_subtract }} - name: Setup Conan env: SANITIZERS: ${{ inputs.sanitizers }} uses: ./.github/actions/setup-conan - name: Build dependencies uses: ./.github/actions/build-deps with: build_nproc: ${{ steps.nproc.outputs.nproc }} build_type: ${{ inputs.build_type }} # Set the verbosity to "quiet" for Windows to avoid an excessive # amount of logs. For other OSes, the "verbose" logs are more useful. log_verbosity: ${{ runner.os == 'Windows' && 'quiet' || 'verbose' }} sanitizers: ${{ inputs.sanitizers }} - name: Configure CMake working-directory: ${{ env.BUILD_DIR }} env: BUILD_TYPE: ${{ inputs.build_type }} CMAKE_ARGS: ${{ inputs.cmake_args }} run: | cmake \ -G '${{ runner.os == 'Windows' && 'Visual Studio 17 2022' || 'Ninja' }}' \ -DCMAKE_TOOLCHAIN_FILE:FILEPATH=build/generators/conan_toolchain.cmake \ -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" \ ${CMAKE_ARGS} \ .. - name: Check protocol autogen files are up-to-date working-directory: ${{ env.BUILD_DIR }} env: MESSAGE: | The generated protocol wrapper classes are out of date. This typically happens when the macro files or generator scripts have changed but the generated files were not regenerated. To fix this: 1. Run: cmake --build . --target setup_code_gen 2. Run: cmake --build . --target code_gen 3. Commit and push the regenerated files run: | set -e cmake --build . --target setup_code_gen cmake --build . --target code_gen DIFF=$(git -C .. status --porcelain -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen) if [ -n "${DIFF}" ]; then echo "::error::Generated protocol files are out of date" git -C .. diff -- include/xrpl/protocol_autogen src/tests/libxrpl/protocol_autogen echo "${MESSAGE}" exit 1 fi - name: Build the binary working-directory: ${{ env.BUILD_DIR }} env: BUILD_NPROC: ${{ runner.os == 'Linux' && '16' || steps.nproc.outputs.nproc }} BUILD_TYPE: ${{ inputs.build_type }} CMAKE_TARGET: ${{ inputs.cmake_target }} run: | cmake \ --build . \ --config "${BUILD_TYPE}" \ --parallel "${BUILD_NPROC}" \ --target "${CMAKE_TARGET}" - name: Show ccache statistics if: ${{ inputs.ccache_enabled }} run: | ccache --show-stats -vv if [ '${{ runner.debug }}' = '1' ]; then cat "${CCACHE_LOGFILE}" curl ${CCACHE_REMOTE_STORAGE%|*}/status || true fi - name: Upload the binary (Linux) if: ${{ github.event.repository.visibility == 'public' && runner.os == 'Linux' }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: xrpld-${{ inputs.config_name }} path: ${{ env.BUILD_DIR }}/xrpld retention-days: 3 if-no-files-found: error - name: Export server definitions if: ${{ runner.os != 'Windows' && !inputs.build_only && env.VOIDSTAR_ENABLED != 'true' }} working-directory: ${{ env.BUILD_DIR }} run: | set -o pipefail ./xrpld --definitions | python3 -m json.tool > server_definitions.json - name: Upload server definitions if: ${{ github.event.repository.visibility == 'public' && inputs.config_name == 'debian-bookworm-gcc-13-amd64-release' }} uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: server-definitions path: ${{ env.BUILD_DIR }}/server_definitions.json retention-days: 3 if-no-files-found: error - name: Check linking (Linux) if: ${{ runner.os == 'Linux' && env.SANITIZERS_ENABLED == 'false' }} working-directory: ${{ env.BUILD_DIR }} run: | ldd ./xrpld if [ "$(ldd ./xrpld | grep -E '(libstdc\+\+|libgcc)' | wc -l)" -eq 0 ]; then echo 'The binary is statically linked.' else echo 'The binary is dynamically linked.' exit 1 fi - name: Verify presence of instrumentation (Linux) if: ${{ runner.os == 'Linux' && env.VOIDSTAR_ENABLED == 'true' }} working-directory: ${{ env.BUILD_DIR }} run: | ./xrpld --version | grep libvoidstar - name: Set sanitizer options if: ${{ !inputs.build_only && env.SANITIZERS_ENABLED == 'true' }} env: CONFIG_NAME: ${{ inputs.config_name }} run: | ASAN_OPTS="include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-asan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/asan.supp" if [[ "${CONFIG_NAME}" == *gcc* ]]; then ASAN_OPTS="${ASAN_OPTS}:alloc_dealloc_mismatch=0" fi echo "ASAN_OPTIONS=${ASAN_OPTS}" >> ${GITHUB_ENV} echo "TSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-tsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/tsan.supp" >> ${GITHUB_ENV} echo "UBSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-ubsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/ubsan.supp" >> ${GITHUB_ENV} echo "LSAN_OPTIONS=include=${GITHUB_WORKSPACE}/sanitizers/suppressions/runtime-lsan-options.txt:suppressions=${GITHUB_WORKSPACE}/sanitizers/suppressions/lsan.supp" >> ${GITHUB_ENV} - name: Run the separate tests if: ${{ !inputs.build_only }} working-directory: ${{ env.BUILD_DIR }} # Windows locks some of the build files while running tests, and parallel jobs can collide env: BUILD_TYPE: ${{ inputs.build_type }} PARALLELISM: ${{ runner.os == 'Windows' && '1' || steps.nproc.outputs.nproc }} run: | ctest \ --output-on-failure \ -C "${BUILD_TYPE}" \ -j "${PARALLELISM}" - name: Run the embedded tests if: ${{ !inputs.build_only }} working-directory: ${{ runner.os == 'Windows' && format('{0}/{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} env: BUILD_NPROC: ${{ steps.nproc.outputs.nproc }} run: | set -o pipefail # Coverage builds are slower due to instrumentation; use fewer parallel jobs to avoid flakiness [ "$COVERAGE_ENABLED" = "true" ] && BUILD_NPROC=$(( BUILD_NPROC - 2 )) ./xrpld --unittest --unittest-jobs "${BUILD_NPROC}" 2>&1 | tee unittest.log - name: Show test failure summary if: ${{ failure() && !inputs.build_only }} env: WORKING_DIR: ${{ runner.os == 'Windows' && format('{0}\{1}', env.BUILD_DIR, inputs.build_type) || env.BUILD_DIR }} run: | if [ ! -d "${WORKING_DIR}" ]; then echo "Working directory '${WORKING_DIR}' does not exist." exit 0 fi cd "${WORKING_DIR}" if [ ! -f unittest.log ]; then echo "unittest.log not found; embedded tests may not have run." exit 0 fi if ! grep -E "failed" unittest.log; then echo "Log present but no failure lines found in unittest.log." fi - name: Debug failure (Linux) if: ${{ failure() && runner.os == 'Linux' && !inputs.build_only }} run: | echo "IPv4 local port range:" cat /proc/sys/net/ipv4/ip_local_port_range echo "Netstat:" netstat -an - name: Prepare coverage report if: ${{ !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} working-directory: ${{ env.BUILD_DIR }} env: BUILD_NPROC: ${{ steps.nproc.outputs.nproc }} BUILD_TYPE: ${{ inputs.build_type }} run: | cmake \ --build . \ --config "${BUILD_TYPE}" \ --parallel "${BUILD_NPROC}" \ --target coverage - name: Upload coverage report if: ${{ github.repository == 'XRPLF/rippled' && !inputs.build_only && env.COVERAGE_ENABLED == 'true' }} uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 with: disable_search: true disable_telem: true fail_ci_if_error: true files: ${{ env.BUILD_DIR }}/coverage.xml plugins: noop token: ${{ secrets.CODECOV_TOKEN }} verbose: true