From fce3c94df102a40674e72d1991f29d847d2384a8 Mon Sep 17 00:00:00 2001 From: Rimuksh Kansal Date: Thu, 10 Sep 2026 18:34:49 +0900 Subject: [PATCH 1/4] cpp coverage --- .../actions/cpp-bazel/pre-merge/action.yml | 11 +- .github/workflows/_test.yml | 12 ++ .github/workflows/coverage-baseline.yml | 55 ++++- .github/workflows/pre-merge.yml | 2 + codecov.yml | 7 + foreign/cpp/.bazelrc | 16 +- foreign/cpp/BUILD.bazel | 49 ++++- scripts/ci/coverage-baseline-affected.sh | 3 +- scripts/ci/cpp-coverage.sh | 193 ++++++++++++++++++ 9 files changed, 340 insertions(+), 8 deletions(-) create mode 100755 scripts/ci/cpp-coverage.sh diff --git a/.github/actions/cpp-bazel/pre-merge/action.yml b/.github/actions/cpp-bazel/pre-merge/action.yml index 0cd50e62f0..7ffe7b53d2 100644 --- a/.github/actions/cpp-bazel/pre-merge/action.yml +++ b/.github/actions/cpp-bazel/pre-merge/action.yml @@ -72,18 +72,25 @@ runs: cd foreign/cpp bazel build --config=ci //:iggy-cpp + - name: Install cargo-llvm-cov + if: inputs.task == 'test' + uses: taiki-e/install-action@v2.87.3 + with: + tool: cargo-llvm-cov + - name: Setup server for tests if: inputs.task == 'test' uses: ./.github/actions/utils/server-start with: cargo-bin: iggy-server - - name: Test + - name: Test with coverage if: inputs.task == 'test' shell: bash run: | cd foreign/cpp - bazel test --config=ci //... + ../../scripts/ci/cpp-coverage.sh + bazel build --config=ci //:bdd_wire_server - name: Stop server after tests if: always() && inputs.task == 'test' diff --git a/.github/workflows/_test.yml b/.github/workflows/_test.yml index 82a70444ba..8d0594c679 100644 --- a/.github/workflows/_test.yml +++ b/.github/workflows/_test.yml @@ -231,6 +231,18 @@ jobs: with: task: ${{ inputs.task }} + - name: Upload C++ coverage to Codecov + if: inputs.component == 'sdk-cpp' && inputs.task == 'test' + uses: codecov/codecov-action@v7.0.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: reports/cpp-coverage.lcov + disable_search: true + flags: cpp + fail_ci_if_error: false + verbose: true + override_pr: ${{ github.event.pull_request.number }} + - name: Upload Java coverage to Codecov if: inputs.component == 'sdk-java' && inputs.task == 'test' uses: codecov/codecov-action@v7.0.0 diff --git a/.github/workflows/coverage-baseline.yml b/.github/workflows/coverage-baseline.yml index 42a90fccf3..2e7b2a805b 100644 --- a/.github/workflows/coverage-baseline.yml +++ b/.github/workflows/coverage-baseline.yml @@ -15,8 +15,8 @@ # specific language governing permissions and limitations # under the License. -# Coverage baseline for all 7 languages (Rust, Java, C#, Python, PHP, -# Node, Go). Runs on push to master so Codecov has data on master SHAs for +# Coverage baseline for all 8 languages (Rust, Java, C#, Python, PHP, +# Node, Go, C++). Runs on push to master so Codecov has data on master SHAs for # carryforward on PR builds where only a subset of SDKs is tested. # # Jobs are path-gated: a language job runs only when the paths feeding its @@ -61,6 +61,7 @@ jobs: php: ${{ steps.affected.outputs.php }} node: ${{ steps.affected.outputs.node }} go: ${{ steps.affected.outputs.go }} + cpp: ${{ steps.affected.outputs.cpp }} steps: - uses: actions/checkout@v7.0.1 with: @@ -581,6 +582,56 @@ jobs: flags: go fail_ci_if_error: false + cpp-coverage: + name: C++ coverage baseline + needs: detect + if: needs.detect.outputs.cpp == 'true' + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v7.0.1 + + - name: Setup Bazel with cache + uses: ./.github/actions/utils/setup-cpp-with-cache + + - name: Setup Rust with cache + uses: ./.github/actions/utils/setup-rust-with-cache + with: + save-cache: "false" + + - name: Install cargo-llvm-cov + uses: taiki-e/install-action@v2.87.3 + with: + tool: cargo-llvm-cov + + # The e2e suite needs a live server; unit tests run standalone. + - name: Start Iggy server + id: iggy + uses: ./.github/actions/utils/server-start + with: + cargo-bin: iggy-server + + - name: Run tests and generate coverage + run: | + cd foreign/cpp + ../../scripts/ci/cpp-coverage.sh + + - name: Stop Iggy server + if: always() + uses: ./.github/actions/utils/server-stop + with: + pid-file: ${{ steps.iggy.outputs.pid_file }} + log-file: ${{ steps.iggy.outputs.log_file }} + + - name: Upload to Codecov + uses: codecov/codecov-action@v7.0.0 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: reports/cpp-coverage.lcov + disable_search: true + flags: cpp + fail_ci_if_error: false + warm-cache-macos: name: Warm macOS cache needs: detect diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml index aae32a00c6..ef9527e6d2 100644 --- a/.github/workflows/pre-merge.yml +++ b/.github/workflows/pre-merge.yml @@ -165,6 +165,8 @@ jobs: with: component: ${{ matrix.component }} task: ${{ matrix.task }} + secrets: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} # Other components test-other: diff --git a/codecov.yml b/codecov.yml index 7c71b59c96..9557471df1 100644 --- a/codecov.yml +++ b/codecov.yml @@ -71,6 +71,9 @@ flag_management: - name: go paths: - foreign/go/ + - name: cpp + paths: + - foreign/cpp/ component_management: individual_components: @@ -102,6 +105,10 @@ component_management: name: "Go SDK" paths: - foreign/go/ + - component_id: cpp + name: "C++ SDK" + paths: + - foreign/cpp/ comment: layout: "header, diff, components, files" diff --git a/foreign/cpp/.bazelrc b/foreign/cpp/.bazelrc index 880712c20b..eaa1055976 100644 --- a/foreign/cpp/.bazelrc +++ b/foreign/cpp/.bazelrc @@ -48,4 +48,18 @@ build:ci --config=release build:ci --lockfile_mode=error test:ci --lockfile_mode=error -test:ci --test_summary=detailed +test:ci --test_summary=terse +test:ci --test_arg=--gtest_brief=1 + +# Self-contained coverage configuration. +common:cpp-coverage --color=no +common:cpp-coverage --curses=no +common:cpp-coverage --announce_rc + +build:cpp-coverage --compilation_mode=dbg +build:cpp-coverage --copt=-O0 +build:cpp-coverage --strip=never +build:cpp-coverage --lockfile_mode=error + +test:cpp-coverage --test_summary=terse +test:cpp-coverage --test_arg=--gtest_brief=1 diff --git a/foreign/cpp/BUILD.bazel b/foreign/cpp/BUILD.bazel index 286cce09e6..9e93fdad7e 100644 --- a/foreign/cpp/BUILD.bazel +++ b/foreign/cpp/BUILD.bazel @@ -17,6 +17,11 @@ load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_import", "cc_library", "cc_test") +config_setting( + name = "coverage_enabled", + values = {"define": "iggy_cpp_coverage=1"}, +) + genrule( name = "cargo_build", srcs = glob(["src/**/*.rs"]) + [ @@ -55,7 +60,7 @@ genrule( env -u PWD \ CARGO_TARGET_DIR="$$CARGO_TARGET_DIR" \ RUSTC="$$RUSTC" \ - "$$CARGO" build $$FLAGS + "$$CARGO" build --quiet $$FLAGS cp "$$CARGO_TARGET_DIR/$$PROFILE/libiggy_cpp.a" "$$OUT_LIB" @@ -88,11 +93,48 @@ genrule( ], ) +genrule( + name = "profiler_builtins", + outs = ["libprofiler_builtins.a"], + cmd = """ + set -euo pipefail + + EXECROOT="$$(pwd)" + RUSTC="$$EXECROOT/$(execpath @rs_host_tools//:rustc)" + RT_OUT="$$EXECROOT/$(location libprofiler_builtins.a)" + RT_SYSROOT="$$("$$RUSTC" --print sysroot)" + RT_HOST="$$("$$RUSTC" -vV | sed -n 's/^host: //p')" + RT_RLIB="$$(find "$$RT_SYSROOT/lib/rustlib/$$RT_HOST/lib" -maxdepth 1 -name 'libprofiler_builtins-*.rlib' -print -quit)" + if [ -z "$$RT_RLIB" ]; then + echo "ERROR: Failed to locate libprofiler_builtins rlib under $$RT_SYSROOT" >&2 + exit 1 + fi + cp "$$RT_RLIB" "$$RT_OUT" + for member in $$(ar t "$$RT_OUT"); do + case "$$member" in + *.o) ;; + *) ar d "$$RT_OUT" "$$member" ;; + esac + done + """, + local = 1, + tools = [ + "@rs_host_tools//:rust_toolchain", + "@rs_host_tools//:rustc", + ], +) + cc_import( name = "iggy_cpp_static", static_library = ":libiggy_cpp.a", ) +cc_library( + name = "profiler_rt", + srcs = ["libprofiler_builtins.a"], + alwayslink = True, +) + cc_library( name = "iggy-cpp", srcs = glob( @@ -139,7 +181,10 @@ cc_library( visibility = ["//visibility:public"], deps = [ ":iggy_cpp_static", - ], + ] + select({ + ":coverage_enabled": [":profiler_rt"], + "//conditions:default": [], + }), ) cc_binary( diff --git a/scripts/ci/coverage-baseline-affected.sh b/scripts/ci/coverage-baseline-affected.sh index 930a6c2db2..e33ecec9cf 100755 --- a/scripts/ci/coverage-baseline-affected.sh +++ b/scripts/ci/coverage-baseline-affected.sh @@ -33,7 +33,7 @@ set -euo pipefail BASE="${1:-}" HEAD="${2:-}" ZERO="0000000000000000000000000000000000000000" -GATES=(rust java csharp python php node go) +GATES=(rust java csharp python php node go cpp) emit_all() { local gate @@ -82,6 +82,7 @@ for gate in "${GATES[@]}"; do node) paths=(foreign/node) ;; # The go job also runs the bdd/go suite with foreign/go in -coverpkg. go) paths=(foreign/go bdd/go) ;; + cpp) paths=(foreign/cpp bdd/cpp) ;; *) echo "coverage-gate: no pathspecs defined for gate '$gate'" >&2 exit 1 diff --git a/scripts/ci/cpp-coverage.sh b/scripts/ci/cpp-coverage.sh new file mode 100755 index 0000000000..42cd867d55 --- /dev/null +++ b/scripts/ci/cpp-coverage.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +set -euo pipefail + +if [[ ! -f MODULE.bazel || ! -f BUILD.bazel ]]; then + echo "cpp-coverage: must run with CWD set to foreign/cpp" >&2 + exit 2 +fi + +for tool in bazel cargo; do + if ! command -v "$tool" >/dev/null 2>&1; then + echo "cpp-coverage: required tool '$tool' not on PATH" >&2 + exit 2 + fi +done + +OUTPUT="${1:-../../reports/cpp-coverage.lcov}" +OUTPUT_TMP="${OUTPUT}.tmp" +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +TMP_DIR="$(pwd)/target/cpp-coverage-tmp" +PROFRAW_DIR="$TMP_DIR/profraw" +BUILD_PROFRAW_DIR="$TMP_DIR/build-profraw" +PROFDATA="$TMP_DIR/cpp-shim.profdata" +CPP_RAW="$(pwd)/bazel-out/_coverage/_coverage_report.dat" +SHIM_RAW="$TMP_DIR/rust-shim-raw.lcov" +CPP_FILTERED="$TMP_DIR/cpp-coverage.lcov" +SHIM_FILTERED="$TMP_DIR/rust-shim.lcov" + +mkdir -p "$PROFRAW_DIR" "$BUILD_PROFRAW_DIR" "$(dirname "$OUTPUT")" +rm -f "$PROFRAW_DIR"/*.profraw "$BUILD_PROFRAW_DIR"/*.profraw \ + "$PROFDATA" "$CPP_FILTERED" "$SHIM_RAW" "$SHIM_FILTERED" "$OUTPUT_TMP" + +show_env_file="$TMP_DIR/show-env.sh" +if ! cargo llvm-cov show-env --no-rustc-wrapper --sh >"$show_env_file"; then + echo "cpp-coverage: 'cargo llvm-cov show-env' failed (see output above)" >&2 + exit 1 +fi +# shellcheck disable=SC1090 +source "$show_env_file" +if [[ -z "${RUSTFLAGS:-}" ]]; then + echo "cpp-coverage: cargo llvm-cov show-env did not export RUSTFLAGS" >&2 + exit 1 +fi +export LLVM_PROFILE_FILE="$PROFRAW_DIR/cpp-%p-%m.profraw" + +llvm_bin_dir="$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n 's/^host: //p')/bin" +LLVM_COV="$llvm_bin_dir/llvm-cov" +LLVM_PROFDATA="$llvm_bin_dir/llvm-profdata" +if [[ ! -x "$LLVM_COV" || ! -x "$LLVM_PROFDATA" ]]; then + echo "cpp-coverage: $LLVM_COV or $LLVM_PROFDATA missing;" >&2 + echo "cpp-coverage: install with 'rustup component add llvm-tools'" >&2 + exit 1 +fi + +BAZEL_FLAGS=(--config=cpp-coverage) +COVERAGE_DEFINE="--define=iggy_cpp_coverage=1" +TEST_QUERY='kind("cc_test rule", //...)' +if ! TEST_TARGET_ROWS="$( + bazel cquery "$TEST_QUERY" \ + "${BAZEL_FLAGS[@]}" \ + "$COVERAGE_DEFINE" \ + --output=starlark \ + '--starlark:expr="//" + target.label.package + ":" + target.label.name + "\t" + providers(target)["DefaultInfo"].files_to_run.executable.path' +)"; then + echo "cpp-coverage: failed to discover C++ test targets" >&2 + exit 1 +fi +if [[ -z "$TEST_TARGET_ROWS" ]]; then + echo "cpp-coverage: no C++ test targets found" >&2 + exit 1 +fi + +TEST_TARGETS=() +TEST_OBJECTS=() +while IFS=$'\t' read -r test_target test_object; do + if [[ -z "$test_target" || -z "$test_object" ]]; then + echo "cpp-coverage: invalid C++ test target entry: $test_target $test_object" >&2 + exit 1 + fi + TEST_TARGETS+=("$test_target") + TEST_OBJECTS+=("$(pwd)/$test_object") +done <<<"$TEST_TARGET_ROWS" + +bazel coverage "${BAZEL_FLAGS[@]}" \ + --combined_report=lcov \ + '--instrumentation_filter=//:iggy-cpp' \ + --nocache_test_results \ + --strategy=CoverageReport=local \ + --strategy=TestRunner=standalone \ + "$COVERAGE_DEFINE" \ + "--action_env=RUSTFLAGS=$RUSTFLAGS" \ + "--action_env=LLVM_PROFILE_FILE=$BUILD_PROFRAW_DIR/build-%p-%m.profraw" \ + "--run_under=/usr/bin/env LLVM_PROFILE_FILE=$LLVM_PROFILE_FILE" \ + "${TEST_TARGETS[@]}" + +if [[ ! -s "$CPP_RAW" ]]; then + echo "cpp-coverage: bazel coverage produced no combined report" >&2 + exit 1 +fi + +shopt -s nullglob +profraws=("$PROFRAW_DIR"/*.profraw) +shopt -u nullglob +if [[ ${#profraws[@]} -eq 0 ]]; then + echo "cpp-coverage: no profraw files in $PROFRAW_DIR; RUSTFLAGS or LLVM_PROFILE_FILE did not reach the test binaries" >&2 + exit 1 +fi + +"$LLVM_PROFDATA" merge -sparse "${profraws[@]}" -o "$PROFDATA" + +LLVM_COV_OBJECT_ARGS=() +for test_object in "${TEST_OBJECTS[@]}"; do + if [[ ! -f "$test_object" ]]; then + echo "cpp-coverage: test executable not found: $test_object" >&2 + exit 1 + fi + LLVM_COV_OBJECT_ARGS+=(--object "$test_object") +done + +"$LLVM_COV" export "${LLVM_COV_OBJECT_ARGS[@]}" \ + -instr-profile="$PROFDATA" \ + -format=lcov \ + -ignore-filename-regex='(\.cargo/|/rustc/|/core/|cxxbridge|/registry/)' >"$SHIM_RAW" + +filter_lcov() { + local input="$1" output="$2" mode="$3" + awk -v mode="$mode" ' + function map_sf(raw, mapped) { + if (mode == "rs") { + if (match(raw, /foreign\/cpp\/src\/[^\/]+\.rs/)) { + return substr(raw, RSTART, RLENGTH) + } + return "" + } + if (match(raw, /foreign\/cpp\/(src\/[^\/]+\.cpp|include\/[^\/]+\.hpp?)/)) { + return substr(raw, RSTART, RLENGTH) + } + if (match(raw, /\/proc\/self\/cwd\/(src\/[^\/]+\.cpp|include\/[^\/]+\.hpp?)/)) { + return "foreign/cpp/" substr(raw, RSTART + 15, RLENGTH - 15) + } + if (raw ~ /^(src\/[^\/]+\.cpp|include\/[^\/]+\.hpp?)$/) { + return "foreign/cpp/" raw + } + return "" + } + /^SF:/ { + mapped = map_sf(substr($0, 4)) + if (mapped != "") { + print "SF:" mapped + in_block = 1 + } else { + in_block = 0 + } + next + } + /^end_of_record/ { + if (in_block) { + print + } + in_block = 0 + next + } + { + if (in_block) { + print + } + } + ' "$input" >"$output" +} + +filter_lcov "$CPP_RAW" "$CPP_FILTERED" "cpp" +filter_lcov "$SHIM_RAW" "$SHIM_FILTERED" "rs" + +cat "$CPP_FILTERED" "$SHIM_FILTERED" >"$OUTPUT_TMP" + +"$REPO_ROOT/scripts/ci/validate-lcov.sh" "$OUTPUT_TMP" +mv "$OUTPUT_TMP" "$OUTPUT" From dfccce86c5d5ea6d28df99e8dc4d0bbd0bfb0a02 Mon Sep 17 00:00:00 2001 From: Rimuksh Kansal Date: Sat, 12 Sep 2026 16:22:56 +0900 Subject: [PATCH 2/4] cleanup --- foreign/cpp/.bazelrc | 16 +- foreign/cpp/BUILD.bazel | 6 +- scripts/ci/coverage-baseline-affected.sh | 2 +- scripts/ci/cpp-coverage.sh | 185 ++++------------------- 4 files changed, 37 insertions(+), 172 deletions(-) diff --git a/foreign/cpp/.bazelrc b/foreign/cpp/.bazelrc index eaa1055976..880712c20b 100644 --- a/foreign/cpp/.bazelrc +++ b/foreign/cpp/.bazelrc @@ -48,18 +48,4 @@ build:ci --config=release build:ci --lockfile_mode=error test:ci --lockfile_mode=error -test:ci --test_summary=terse -test:ci --test_arg=--gtest_brief=1 - -# Self-contained coverage configuration. -common:cpp-coverage --color=no -common:cpp-coverage --curses=no -common:cpp-coverage --announce_rc - -build:cpp-coverage --compilation_mode=dbg -build:cpp-coverage --copt=-O0 -build:cpp-coverage --strip=never -build:cpp-coverage --lockfile_mode=error - -test:cpp-coverage --test_summary=terse -test:cpp-coverage --test_arg=--gtest_brief=1 +test:ci --test_summary=detailed diff --git a/foreign/cpp/BUILD.bazel b/foreign/cpp/BUILD.bazel index 9e93fdad7e..ea6dffedd1 100644 --- a/foreign/cpp/BUILD.bazel +++ b/foreign/cpp/BUILD.bazel @@ -60,7 +60,7 @@ genrule( env -u PWD \ CARGO_TARGET_DIR="$$CARGO_TARGET_DIR" \ RUSTC="$$RUSTC" \ - "$$CARGO" build --quiet $$FLAGS + "$$CARGO" build $$FLAGS cp "$$CARGO_TARGET_DIR/$$PROFILE/libiggy_cpp.a" "$$OUT_LIB" @@ -129,9 +129,9 @@ cc_import( static_library = ":libiggy_cpp.a", ) -cc_library( +cc_import( name = "profiler_rt", - srcs = ["libprofiler_builtins.a"], + static_library = ":libprofiler_builtins.a", alwayslink = True, ) diff --git a/scripts/ci/coverage-baseline-affected.sh b/scripts/ci/coverage-baseline-affected.sh index e33ecec9cf..b3c7b84927 100755 --- a/scripts/ci/coverage-baseline-affected.sh +++ b/scripts/ci/coverage-baseline-affected.sh @@ -82,7 +82,7 @@ for gate in "${GATES[@]}"; do node) paths=(foreign/node) ;; # The go job also runs the bdd/go suite with foreign/go in -coverpkg. go) paths=(foreign/go bdd/go) ;; - cpp) paths=(foreign/cpp bdd/cpp) ;; + cpp) paths=(foreign/cpp) ;; *) echo "coverage-gate: no pathspecs defined for gate '$gate'" >&2 exit 1 diff --git a/scripts/ci/cpp-coverage.sh b/scripts/ci/cpp-coverage.sh index 42cd867d55..780116ca62 100755 --- a/scripts/ci/cpp-coverage.sh +++ b/scripts/ci/cpp-coverage.sh @@ -18,176 +18,55 @@ set -euo pipefail -if [[ ! -f MODULE.bazel || ! -f BUILD.bazel ]]; then - echo "cpp-coverage: must run with CWD set to foreign/cpp" >&2 - exit 2 -fi - -for tool in bazel cargo; do - if ! command -v "$tool" >/dev/null 2>&1; then - echo "cpp-coverage: required tool '$tool' not on PATH" >&2 - exit 2 - fi -done - OUTPUT="${1:-../../reports/cpp-coverage.lcov}" -OUTPUT_TMP="${OUTPUT}.tmp" -REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -TMP_DIR="$(pwd)/target/cpp-coverage-tmp" -PROFRAW_DIR="$TMP_DIR/profraw" -BUILD_PROFRAW_DIR="$TMP_DIR/build-profraw" -PROFDATA="$TMP_DIR/cpp-shim.profdata" +COVERAGE_DIR="$(pwd)/target/cpp-coverage" +BUILD_PROFRAW_DIR="$COVERAGE_DIR/build" +PROFDATA="$COVERAGE_DIR/shim.profdata" CPP_RAW="$(pwd)/bazel-out/_coverage/_coverage_report.dat" -SHIM_RAW="$TMP_DIR/rust-shim-raw.lcov" -CPP_FILTERED="$TMP_DIR/cpp-coverage.lcov" -SHIM_FILTERED="$TMP_DIR/rust-shim.lcov" -mkdir -p "$PROFRAW_DIR" "$BUILD_PROFRAW_DIR" "$(dirname "$OUTPUT")" -rm -f "$PROFRAW_DIR"/*.profraw "$BUILD_PROFRAW_DIR"/*.profraw \ - "$PROFDATA" "$CPP_FILTERED" "$SHIM_RAW" "$SHIM_FILTERED" "$OUTPUT_TMP" +mkdir -p "$COVERAGE_DIR" "$BUILD_PROFRAW_DIR" "$(dirname "$OUTPUT")" +rm -f "$COVERAGE_DIR"/*.profraw "$BUILD_PROFRAW_DIR"/*.profraw "$PROFDATA" "$OUTPUT" -show_env_file="$TMP_DIR/show-env.sh" -if ! cargo llvm-cov show-env --no-rustc-wrapper --sh >"$show_env_file"; then - echo "cpp-coverage: 'cargo llvm-cov show-env' failed (see output above)" >&2 - exit 1 -fi # shellcheck disable=SC1090 -source "$show_env_file" -if [[ -z "${RUSTFLAGS:-}" ]]; then - echo "cpp-coverage: cargo llvm-cov show-env did not export RUSTFLAGS" >&2 - exit 1 -fi -export LLVM_PROFILE_FILE="$PROFRAW_DIR/cpp-%p-%m.profraw" - -llvm_bin_dir="$(rustc --print sysroot)/lib/rustlib/$(rustc -vV | sed -n 's/^host: //p')/bin" -LLVM_COV="$llvm_bin_dir/llvm-cov" -LLVM_PROFDATA="$llvm_bin_dir/llvm-profdata" -if [[ ! -x "$LLVM_COV" || ! -x "$LLVM_PROFDATA" ]]; then - echo "cpp-coverage: $LLVM_COV or $LLVM_PROFDATA missing;" >&2 - echo "cpp-coverage: install with 'rustup component add llvm-tools'" >&2 - exit 1 -fi +source <(cargo llvm-cov show-env --no-rustc-wrapper --sh) +export LLVM_PROFILE_FILE="$COVERAGE_DIR/%p-%m.profraw" -BAZEL_FLAGS=(--config=cpp-coverage) -COVERAGE_DEFINE="--define=iggy_cpp_coverage=1" -TEST_QUERY='kind("cc_test rule", //...)' -if ! TEST_TARGET_ROWS="$( - bazel cquery "$TEST_QUERY" \ - "${BAZEL_FLAGS[@]}" \ - "$COVERAGE_DEFINE" \ - --output=starlark \ - '--starlark:expr="//" + target.label.package + ":" + target.label.name + "\t" + providers(target)["DefaultInfo"].files_to_run.executable.path' -)"; then - echo "cpp-coverage: failed to discover C++ test targets" >&2 - exit 1 -fi -if [[ -z "$TEST_TARGET_ROWS" ]]; then - echo "cpp-coverage: no C++ test targets found" >&2 - exit 1 -fi +LLVM_BIN="$(rustc --print target-libdir)/../bin" +LLVM_COV="$LLVM_BIN/llvm-cov" +LLVM_PROFDATA="$LLVM_BIN/llvm-profdata" -TEST_TARGETS=() -TEST_OBJECTS=() -while IFS=$'\t' read -r test_target test_object; do - if [[ -z "$test_target" || -z "$test_object" ]]; then - echo "cpp-coverage: invalid C++ test target entry: $test_target $test_object" >&2 - exit 1 - fi - TEST_TARGETS+=("$test_target") - TEST_OBJECTS+=("$(pwd)/$test_object") -done <<<"$TEST_TARGET_ROWS" - -bazel coverage "${BAZEL_FLAGS[@]}" \ +bazel coverage \ + --config=debug \ + --lockfile_mode=error \ --combined_report=lcov \ '--instrumentation_filter=//:iggy-cpp' \ - --nocache_test_results \ - --strategy=CoverageReport=local \ --strategy=TestRunner=standalone \ - "$COVERAGE_DEFINE" \ - "--action_env=RUSTFLAGS=$RUSTFLAGS" \ - "--action_env=LLVM_PROFILE_FILE=$BUILD_PROFRAW_DIR/build-%p-%m.profraw" \ + --define=iggy_cpp_coverage=1 \ + --action_env=RUSTFLAGS \ + "--action_env=LLVM_PROFILE_FILE=$BUILD_PROFRAW_DIR/%p-%m.profraw" \ "--run_under=/usr/bin/env LLVM_PROFILE_FILE=$LLVM_PROFILE_FILE" \ - "${TEST_TARGETS[@]}" - -if [[ ! -s "$CPP_RAW" ]]; then - echo "cpp-coverage: bazel coverage produced no combined report" >&2 - exit 1 -fi + //:unit //:e2e -shopt -s nullglob -profraws=("$PROFRAW_DIR"/*.profraw) -shopt -u nullglob -if [[ ${#profraws[@]} -eq 0 ]]; then - echo "cpp-coverage: no profraw files in $PROFRAW_DIR; RUSTFLAGS or LLVM_PROFILE_FILE did not reach the test binaries" >&2 - exit 1 -fi +"$LLVM_PROFDATA" merge -sparse \ + "$COVERAGE_DIR"/*.profraw \ + -o "$PROFDATA" -"$LLVM_PROFDATA" merge -sparse "${profraws[@]}" -o "$PROFDATA" +sed 's|^SF:|SF:foreign/cpp/|' "$CPP_RAW" >"$OUTPUT" -LLVM_COV_OBJECT_ARGS=() -for test_object in "${TEST_OBJECTS[@]}"; do - if [[ ! -f "$test_object" ]]; then - echo "cpp-coverage: test executable not found: $test_object" >&2 - exit 1 - fi - LLVM_COV_OBJECT_ARGS+=(--object "$test_object") -done - -"$LLVM_COV" export "${LLVM_COV_OBJECT_ARGS[@]}" \ +"$LLVM_COV" export \ + --object bazel-bin/unit \ + --object bazel-bin/e2e \ -instr-profile="$PROFDATA" \ -format=lcov \ - -ignore-filename-regex='(\.cargo/|/rustc/|/core/|cxxbridge|/registry/)' >"$SHIM_RAW" - -filter_lcov() { - local input="$1" output="$2" mode="$3" - awk -v mode="$mode" ' - function map_sf(raw, mapped) { - if (mode == "rs") { - if (match(raw, /foreign\/cpp\/src\/[^\/]+\.rs/)) { - return substr(raw, RSTART, RLENGTH) - } - return "" - } - if (match(raw, /foreign\/cpp\/(src\/[^\/]+\.cpp|include\/[^\/]+\.hpp?)/)) { - return substr(raw, RSTART, RLENGTH) - } - if (match(raw, /\/proc\/self\/cwd\/(src\/[^\/]+\.cpp|include\/[^\/]+\.hpp?)/)) { - return "foreign/cpp/" substr(raw, RSTART + 15, RLENGTH - 15) - } - if (raw ~ /^(src\/[^\/]+\.cpp|include\/[^\/]+\.hpp?)$/) { - return "foreign/cpp/" raw - } - return "" - } + -ignore-filename-regex='(\.cargo/|/rustc/|/core/|cxxbridge|/registry/)' | + awk ' /^SF:/ { - mapped = map_sf(substr($0, 4)) - if (mapped != "") { - print "SF:" mapped - in_block = 1 - } else { - in_block = 0 - } - next - } - /^end_of_record/ { - if (in_block) { - print + include = /foreign\/cpp\/src\/[^/]+\.rs$/ + if (include) { + sub(/^SF:.*foreign\/cpp\//, "SF:foreign/cpp/") } - in_block = 0 - next } - { - if (in_block) { - print - } - } - ' "$input" >"$output" -} - -filter_lcov "$CPP_RAW" "$CPP_FILTERED" "cpp" -filter_lcov "$SHIM_RAW" "$SHIM_FILTERED" "rs" - -cat "$CPP_FILTERED" "$SHIM_FILTERED" >"$OUTPUT_TMP" + include + ' >>"$OUTPUT" -"$REPO_ROOT/scripts/ci/validate-lcov.sh" "$OUTPUT_TMP" -mv "$OUTPUT_TMP" "$OUTPUT" +../../scripts/ci/validate-lcov.sh "$OUTPUT" From eaf92b22aace94f98b541bd0bd4cec676d428655 Mon Sep 17 00:00:00 2001 From: Rimuksh Kansal Date: Sat, 12 Sep 2026 16:43:58 +0900 Subject: [PATCH 3/4] clean up --- foreign/cpp/BUILD.bazel | 50 ++++++++++++++------------------------ scripts/ci/cpp-coverage.sh | 2 +- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/foreign/cpp/BUILD.bazel b/foreign/cpp/BUILD.bazel index ea6dffedd1..8c12250e94 100644 --- a/foreign/cpp/BUILD.bazel +++ b/foreign/cpp/BUILD.bazel @@ -30,6 +30,7 @@ genrule( ], outs = [ "libiggy_cpp.a", + "libprofiler_builtins.a", "include/lib.rs.h", "cxxbridge/rust/cxx.h", "cxxbridge/sources/lib.rs.cc", @@ -42,6 +43,7 @@ genrule( RUSTC="$$EXECROOT/$(execpath @rs_host_tools//:rustc)" CARGO_TARGET_DIR="$$EXECROOT/$(@D)/cargo_target" OUT_LIB="$$EXECROOT/$(location libiggy_cpp.a)" + PROFILER_RT_OUT="$$EXECROOT/$(location libprofiler_builtins.a)" OUT_RS="$$EXECROOT/$(location include/lib.rs.h)" OUT_CXX="$$EXECROOT/$(location cxxbridge/rust/cxx.h)" OUT_CC="$$EXECROOT/$(location cxxbridge/sources/lib.rs.cc)" @@ -60,10 +62,25 @@ genrule( env -u PWD \ CARGO_TARGET_DIR="$$CARGO_TARGET_DIR" \ RUSTC="$$RUSTC" \ - "$$CARGO" build $$FLAGS + "$$CARGO" build --quiet $$FLAGS cp "$$CARGO_TARGET_DIR/$$PROFILE/libiggy_cpp.a" "$$OUT_LIB" + RT_SYSROOT="$$("$$RUSTC" --print sysroot)" + RT_HOST="$$("$$RUSTC" -vV | sed -n 's/^host: //p')" + RT_RLIB="$$(find "$$RT_SYSROOT/lib/rustlib/$$RT_HOST/lib" -maxdepth 1 -name 'libprofiler_builtins-*.rlib' -print -quit)" + if [ -z "$$RT_RLIB" ]; then + echo "ERROR: Failed to locate libprofiler_builtins rlib under $$RT_SYSROOT" >&2 + exit 1 + fi + cp "$$RT_RLIB" "$$PROFILER_RT_OUT" + for member in $$(ar t "$$PROFILER_RT_OUT"); do + case "$$member" in + *.o) ;; + *) ar d "$$PROFILER_RT_OUT" "$$member" ;; + esac + done + RS_HDR="$$(find "$$CARGO_TARGET_DIR/$$PROFILE/build" -name lib.rs.h -print -quit)" if [ -z "$$RS_HDR" ]; then echo "ERROR: Failed to locate generated lib.rs.h under $$CARGO_TARGET_DIR/$$PROFILE/build" >&2 @@ -93,37 +110,6 @@ genrule( ], ) -genrule( - name = "profiler_builtins", - outs = ["libprofiler_builtins.a"], - cmd = """ - set -euo pipefail - - EXECROOT="$$(pwd)" - RUSTC="$$EXECROOT/$(execpath @rs_host_tools//:rustc)" - RT_OUT="$$EXECROOT/$(location libprofiler_builtins.a)" - RT_SYSROOT="$$("$$RUSTC" --print sysroot)" - RT_HOST="$$("$$RUSTC" -vV | sed -n 's/^host: //p')" - RT_RLIB="$$(find "$$RT_SYSROOT/lib/rustlib/$$RT_HOST/lib" -maxdepth 1 -name 'libprofiler_builtins-*.rlib' -print -quit)" - if [ -z "$$RT_RLIB" ]; then - echo "ERROR: Failed to locate libprofiler_builtins rlib under $$RT_SYSROOT" >&2 - exit 1 - fi - cp "$$RT_RLIB" "$$RT_OUT" - for member in $$(ar t "$$RT_OUT"); do - case "$$member" in - *.o) ;; - *) ar d "$$RT_OUT" "$$member" ;; - esac - done - """, - local = 1, - tools = [ - "@rs_host_tools//:rust_toolchain", - "@rs_host_tools//:rustc", - ], -) - cc_import( name = "iggy_cpp_static", static_library = ":libiggy_cpp.a", diff --git a/scripts/ci/cpp-coverage.sh b/scripts/ci/cpp-coverage.sh index 780116ca62..aa82be879c 100755 --- a/scripts/ci/cpp-coverage.sh +++ b/scripts/ci/cpp-coverage.sh @@ -18,7 +18,7 @@ set -euo pipefail -OUTPUT="${1:-../../reports/cpp-coverage.lcov}" +OUTPUT="../../reports/cpp-coverage.lcov" COVERAGE_DIR="$(pwd)/target/cpp-coverage" BUILD_PROFRAW_DIR="$COVERAGE_DIR/build" PROFDATA="$COVERAGE_DIR/shim.profdata" From 924cef8ed1f2ded169224c7d33b91212cae9b865 Mon Sep 17 00:00:00 2001 From: Rimuksh Kansal Date: Sat, 12 Sep 2026 19:59:06 +0900 Subject: [PATCH 4/4] fixes --- .../actions/cpp-bazel/pre-merge/action.yml | 3 +-- foreign/cpp/BUILD.bazel | 1 + foreign/cpp/MODULE.bazel | 17 +++++++++++++++ scripts/ci/cpp-coverage.sh | 21 +++++++++++++------ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/.github/actions/cpp-bazel/pre-merge/action.yml b/.github/actions/cpp-bazel/pre-merge/action.yml index 7ffe7b53d2..f46f0f3b50 100644 --- a/.github/actions/cpp-bazel/pre-merge/action.yml +++ b/.github/actions/cpp-bazel/pre-merge/action.yml @@ -63,7 +63,7 @@ runs: shell: bash run: | cd foreign/cpp - cargo clippy --manifest-path Cargo.toml --all-targets --all-features -- -D warnings + cargo clippy --all-targets --all-features -- -D warnings - name: Build if: inputs.task == 'build' @@ -90,7 +90,6 @@ runs: run: | cd foreign/cpp ../../scripts/ci/cpp-coverage.sh - bazel build --config=ci //:bdd_wire_server - name: Stop server after tests if: always() && inputs.task == 'test' diff --git a/foreign/cpp/BUILD.bazel b/foreign/cpp/BUILD.bazel index 8c12250e94..fcc784b6e9 100644 --- a/foreign/cpp/BUILD.bazel +++ b/foreign/cpp/BUILD.bazel @@ -27,6 +27,7 @@ genrule( srcs = glob(["src/**/*.rs"]) + [ "Cargo.toml", "build.rs", + "@iggy_source//:cpp_rust_dependencies", ], outs = [ "libiggy_cpp.a", diff --git a/foreign/cpp/MODULE.bazel b/foreign/cpp/MODULE.bazel index 3401589b32..c53f23adb5 100644 --- a/foreign/cpp/MODULE.bazel +++ b/foreign/cpp/MODULE.bazel @@ -26,6 +26,23 @@ bazel_dep(name = "googletest", version = "1.18.0.bcr.1") bazel_dep(name = "cucumber-cpp", version = "0.8.0.bcr.1") bazel_dep(name = "rules_rust", version = "0.74.0") +iggy_source = use_repo_rule("@bazel_tools//tools/build_defs/repo:local.bzl", "new_local_repository") +iggy_source( + name = "iggy_source", + build_file_content = """ +filegroup( + name = "cpp_rust_dependencies", + srcs = glob([ + "core/binary_protocol/**", + "core/common/**", + "core/sdk/**", + ]) + ["Cargo.toml"], + visibility = ["//visibility:public"], +) +""", + path = "../..", +) + rust_host_tools = use_extension("@rules_rust//rust:extensions.bzl", "rust_host_tools") rust_host_tools.host_tools( name = "rs_host_tools", diff --git a/scripts/ci/cpp-coverage.sh b/scripts/ci/cpp-coverage.sh index aa82be879c..7d193898f0 100755 --- a/scripts/ci/cpp-coverage.sh +++ b/scripts/ci/cpp-coverage.sh @@ -18,17 +18,26 @@ set -euo pipefail -OUTPUT="../../reports/cpp-coverage.lcov" -COVERAGE_DIR="$(pwd)/target/cpp-coverage" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CPP_ROOT="$REPO_ROOT/foreign/cpp" + +cd "$CPP_ROOT" + +OUTPUT="$REPO_ROOT/reports/cpp-coverage.lcov" +COVERAGE_DIR="$CPP_ROOT/target/cpp-coverage" BUILD_PROFRAW_DIR="$COVERAGE_DIR/build" PROFDATA="$COVERAGE_DIR/shim.profdata" -CPP_RAW="$(pwd)/bazel-out/_coverage/_coverage_report.dat" +CPP_RAW="$CPP_ROOT/bazel-out/_coverage/_coverage_report.dat" mkdir -p "$COVERAGE_DIR" "$BUILD_PROFRAW_DIR" "$(dirname "$OUTPUT")" rm -f "$COVERAGE_DIR"/*.profraw "$BUILD_PROFRAW_DIR"/*.profraw "$PROFDATA" "$OUTPUT" -# shellcheck disable=SC1090 -source <(cargo llvm-cov show-env --no-rustc-wrapper --sh) +if ! LLVM_COV_ENV="$(cargo llvm-cov show-env --no-rustc-wrapper --sh)"; then + echo "cpp-coverage: 'cargo llvm-cov show-env' failed" >&2 + exit 1 +fi +eval "$LLVM_COV_ENV" export LLVM_PROFILE_FILE="$COVERAGE_DIR/%p-%m.profraw" LLVM_BIN="$(rustc --print target-libdir)/../bin" @@ -69,4 +78,4 @@ sed 's|^SF:|SF:foreign/cpp/|' "$CPP_RAW" >"$OUTPUT" include ' >>"$OUTPUT" -../../scripts/ci/validate-lcov.sh "$OUTPUT" +"$SCRIPT_DIR/validate-lcov.sh" "$OUTPUT"