From 5e68a4da393f7ce9db951c9287b92eae56d3dce1 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 28 Apr 2026 17:00:09 +0000 Subject: [PATCH 01/10] Respect --instrumentation_filter for Rust coverage Previously, all Rust targets were instrumented with -Cinstrument-coverage when running `bazel coverage`, regardless of --instrumentation_filter. This differs from how coverage works for Java and C++ targets, where only targets matching the filter are instrumented. Add ctx.coverage_instrumented() check so that Rust coverage respects the same filter, reducing unnecessary recompilation and keeping coverage reports focused on the code under test. --- rust/private/rustc.bzl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 3f03e895f6..0697e47175 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1219,8 +1219,10 @@ def construct_arguments( rustc_flags.add("--extern") rustc_flags.add("proc_macro") - if toolchain.coverage_supported and ctx.configuration.coverage_enabled: - # https://doc.rust-lang.org/rustc/instrument-coverage.html + # Use Bazel's standard instrumentation filter (--instrumentation_filter) + # so that only targets matching the filter get instrumented, consistent + # with how coverage works for other languages (Java, C++). + if toolchain.coverage_supported and ctx.configuration.coverage_enabled and ctx.coverage_instrumented(): rustc_flags.add("--codegen=instrument-coverage") if toolchain._experimental_link_std_dylib: From f361d131d6f41b58bd9ceb809e925372aa3781d8 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 28 Apr 2026 17:00:10 +0000 Subject: [PATCH 02/10] Set --instrumentation_filter for coverage in .bazelrc and CI Without an explicit --instrumentation_filter, Bazel's default filter may not match workspace targets, causing ctx.coverage_instrumented() to return False and producing empty Rust coverage reports. Add coverage --instrumentation_filter=^// to both .bazelrc and all CI coverage tasks. Projects with vendored dependencies should narrow this, e.g.: coverage --instrumentation_filter=^//,-^//third_party --- .bazelci/presubmit.yml | 13 +++++++++++++ .bazelrc | 6 ++++++ 2 files changed, 19 insertions(+) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index c2ab7dfbe0..3daaf37e19 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -74,14 +74,18 @@ split_coverage_postprocessing_shell_commands: &split_coverage_postprocessing_she - echo "coverage --experimental_fetch_all_coverage_outputs" >> user.bazelrc - echo "coverage --experimental_split_coverage_postprocessing" >> user.bazelrc - echo "build --//rust/settings:experimental_use_coverage_metadata_files" >> user.bazelrc +coverage_flags: &coverage_flags + - "--instrumentation_filter=^//" rbe_coverage_flags: &rbe_coverage_flags # https://github.com/bazelbuild/bazel/issues/20578 - "--strategy=CoverageReport=local" + - "--instrumentation_filter=^//" tasks: ubuntu2204: build_targets: *default_linux_targets test_targets: *default_linux_targets coverage_targets: *default_linux_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands run_targets: - //test:query_test_binary @@ -99,6 +103,7 @@ tasks: build_targets: *default_macos_targets test_targets: *default_macos_targets coverage_targets: *default_macos_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands windows: build_targets: *default_windows_targets @@ -116,12 +121,14 @@ tasks: platform: ubuntu2204 shell_commands: *split_coverage_postprocessing_shell_commands coverage_targets: *default_linux_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands macos_split_coverage_postprocessing: name: Split Coverage Postprocessing platform: macos_arm64 shell_commands: *split_coverage_postprocessing_shell_commands coverage_targets: *default_macos_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands ubuntu2204_opt: name: Opt Mode @@ -157,6 +164,7 @@ tasks: build_targets: *default_linux_targets test_targets: *default_linux_targets coverage_targets: *default_linux_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands rbe_ubuntu2204_with_aspects: name: With Aspects @@ -191,6 +199,7 @@ tasks: build_targets: *default_macos_targets test_targets: *default_macos_targets coverage_targets: *default_macos_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands macos_rolling_with_aspects: name: "Macos Rolling Bazel Version With Aspects" @@ -199,6 +208,7 @@ tasks: build_targets: *default_macos_targets test_targets: *default_macos_targets coverage_targets: *default_macos_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands soft_fail: yes bazel: "rolling" @@ -282,6 +292,7 @@ tasks: build_targets: *default_linux_targets test_targets: *default_linux_targets coverage_targets: *default_linux_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands ubuntu1804_with_aspects: name: "Min Bazel Version With Aspects" @@ -292,6 +303,7 @@ tasks: test_targets: *default_linux_targets build_flags: *aspects_flags coverage_targets: *default_linux_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands ubuntu2204_min_rust_version: name: "Min Rust Version" @@ -358,6 +370,7 @@ tasks: build_targets: *default_linux_targets test_targets: *default_linux_targets coverage_targets: *default_linux_targets + coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands linux_docs: name: Docs diff --git a/.bazelrc b/.bazelrc index 54dd449b55..cfd5aa871b 100644 --- a/.bazelrc +++ b/.bazelrc @@ -20,6 +20,12 @@ coverage --combined_report=lcov coverage --experimental_fetch_all_coverage_outputs coverage --experimental_split_coverage_postprocessing +# Instrument all workspace targets for coverage. Without this, the default +# --instrumentation_filter may not match workspace targets, resulting in +# empty coverage reports. Projects should adjust this to exclude vendored +# dependencies, e.g.: coverage --instrumentation_filter=^//,-^//third_party +coverage --instrumentation_filter=^// + # Required for some of the tests # https://bazel.build/reference/command-line-reference#flag--experimental_cc_shared_library common --experimental_cc_shared_library From bdbbd1fa4b300ec2d09a0ee223289bbf373e34b7 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 28 Apr 2026 18:19:00 +0000 Subject: [PATCH 03/10] Always instrument test targets for coverage ctx.coverage_instrumented() returns False for test targets by design (Bazel considers test sources not worth instrumenting). But Rust test binaries statically link their dependencies, and the coverage runtime only initializes if the binary itself is compiled with -Cinstrument-coverage. Without this, test binaries produce no profraw files and coverage collection fails with "no input files specified". Always add -Cinstrument-coverage for test crates (crate_info.is_test) when coverage is enabled, while still respecting the instrumentation filter for library targets. --- rust/private/rustc.bzl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 0697e47175..028f3caac4 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1222,7 +1222,10 @@ def construct_arguments( # Use Bazel's standard instrumentation filter (--instrumentation_filter) # so that only targets matching the filter get instrumented, consistent # with how coverage works for other languages (Java, C++). - if toolchain.coverage_supported and ctx.configuration.coverage_enabled and ctx.coverage_instrumented(): + # Test targets always need instrumentation since the coverage runtime + # only initializes if the binary is compiled with -Cinstrument-coverage, + # even when the statically linked library code was instrumented. + if toolchain.coverage_supported and ctx.configuration.coverage_enabled and (crate_info.is_test or ctx.coverage_instrumented()): rustc_flags.add("--codegen=instrument-coverage") if toolchain._experimental_link_std_dylib: From 330a8f118a4b6d1b051e9a1e57fc012ebf6546f0 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 28 Apr 2026 18:35:09 +0000 Subject: [PATCH 04/10] Revert "Always instrument test targets for coverage" This reverts commit d21e160b40a83fe3ecfa2e2390219737ba2996a1. --- rust/private/rustc.bzl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 028f3caac4..0697e47175 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1222,10 +1222,7 @@ def construct_arguments( # Use Bazel's standard instrumentation filter (--instrumentation_filter) # so that only targets matching the filter get instrumented, consistent # with how coverage works for other languages (Java, C++). - # Test targets always need instrumentation since the coverage runtime - # only initializes if the binary is compiled with -Cinstrument-coverage, - # even when the statically linked library code was instrumented. - if toolchain.coverage_supported and ctx.configuration.coverage_enabled and (crate_info.is_test or ctx.coverage_instrumented()): + if toolchain.coverage_supported and ctx.configuration.coverage_enabled and ctx.coverage_instrumented(): rustc_flags.add("--codegen=instrument-coverage") if toolchain._experimental_link_std_dylib: From 4c5573c5f9b5a65b7695ac4a13237ec619933080 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 28 Apr 2026 18:36:32 +0000 Subject: [PATCH 05/10] Handle zero profraw files gracefully in coverage collection When a test target is not instrumented (e.g. due to --instrumentation_filter), no .profraw files are produced. Previously llvm-profdata merge would fail with "no input files specified". Write an empty coverage file and exit successfully instead, so that uninstrumented test targets don't break coverage collection. --- util/collect_coverage/collect_coverage.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/util/collect_coverage/collect_coverage.rs b/util/collect_coverage/collect_coverage.rs index 06097ea340..26bd92f6fb 100644 --- a/util/collect_coverage/collect_coverage.rs +++ b/util/collect_coverage/collect_coverage.rs @@ -166,6 +166,15 @@ fn main() { }) .collect(); + // No profraw files means no instrumented code ran (e.g. a test target + // that wasn't instrumented due to --instrumentation_filter). Write an + // empty coverage file and exit successfully. + if profraw_files.is_empty() { + debug_log!("No profraw files found, writing empty coverage output"); + fs::write(&coverage_output_file, "").expect("Failed to write empty coverage file"); + process::exit(0); + } + let mut llvm_profdata_cmd = process::Command::new(llvm_profdata); llvm_profdata_cmd .arg("merge") From 1229613f39422c6a076fea3456982413bcdb08d2 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Tue, 28 Apr 2026 19:01:32 +0000 Subject: [PATCH 06/10] Remove --instrumentation_filter from .bazelrc to fix sub-workspace CI The .bazelrc is inherited by sub-workspace CI tasks (cc_common_link, bzlmod_repo_mapping, sys, pyo3) causing build/test failures. The flag is already passed via coverage_flags in presubmit.yml for coverage tasks. --- .bazelrc | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.bazelrc b/.bazelrc index cfd5aa871b..54dd449b55 100644 --- a/.bazelrc +++ b/.bazelrc @@ -20,12 +20,6 @@ coverage --combined_report=lcov coverage --experimental_fetch_all_coverage_outputs coverage --experimental_split_coverage_postprocessing -# Instrument all workspace targets for coverage. Without this, the default -# --instrumentation_filter may not match workspace targets, resulting in -# empty coverage reports. Projects should adjust this to exclude vendored -# dependencies, e.g.: coverage --instrumentation_filter=^//,-^//third_party -coverage --instrumentation_filter=^// - # Required for some of the tests # https://bazel.build/reference/command-line-reference#flag--experimental_cc_shared_library common --experimental_cc_shared_library From bf827f959c72aa10d559a369e83a32ed24239994 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 4 May 2026 09:49:40 +0000 Subject: [PATCH 07/10] Remove coverage_flags from min Bazel version CI tasks The --instrumentation_filter=^// flag doesn't work correctly with Bazel 7.4.1 for Rust coverage collection. Remove it from the ubuntu1804 tasks so they use the default filter, which works. --- .bazelci/presubmit.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 3daaf37e19..49ed4eb123 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -292,7 +292,6 @@ tasks: build_targets: *default_linux_targets test_targets: *default_linux_targets coverage_targets: *default_linux_targets - coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands ubuntu1804_with_aspects: name: "Min Bazel Version With Aspects" @@ -303,7 +302,6 @@ tasks: test_targets: *default_linux_targets build_flags: *aspects_flags coverage_targets: *default_linux_targets - coverage_flags: *coverage_flags post_shell_commands: *coverage_validation_post_shell_commands ubuntu2204_min_rust_version: name: "Min Rust Version" From 6306782357a73977886da977cabd39cdfd1854c1 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 4 May 2026 10:08:47 +0000 Subject: [PATCH 08/10] Revert collect_coverage.rs empty profraw handling The empty profraw handling causes coverage to silently produce empty data on Bazel 7.4.1 instead of going through the normal coverage pipeline. This isn't needed since bazel coverage already handles uninstrumented test targets gracefully. --- util/collect_coverage/collect_coverage.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/util/collect_coverage/collect_coverage.rs b/util/collect_coverage/collect_coverage.rs index 26bd92f6fb..06097ea340 100644 --- a/util/collect_coverage/collect_coverage.rs +++ b/util/collect_coverage/collect_coverage.rs @@ -166,15 +166,6 @@ fn main() { }) .collect(); - // No profraw files means no instrumented code ran (e.g. a test target - // that wasn't instrumented due to --instrumentation_filter). Write an - // empty coverage file and exit successfully. - if profraw_files.is_empty() { - debug_log!("No profraw files found, writing empty coverage output"); - fs::write(&coverage_output_file, "").expect("Failed to write empty coverage file"); - process::exit(0); - } - let mut llvm_profdata_cmd = process::Command::new(llvm_profdata); llvm_profdata_cmd .arg("merge") From 089e068bc24a9ac2242a7fb3e6690d7045f84b04 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 4 May 2026 10:22:14 +0000 Subject: [PATCH 09/10] Revert "Revert collect_coverage.rs empty profraw handling" This reverts commit 6306782357a73977886da977cabd39cdfd1854c1. --- util/collect_coverage/collect_coverage.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/util/collect_coverage/collect_coverage.rs b/util/collect_coverage/collect_coverage.rs index 06097ea340..26bd92f6fb 100644 --- a/util/collect_coverage/collect_coverage.rs +++ b/util/collect_coverage/collect_coverage.rs @@ -166,6 +166,15 @@ fn main() { }) .collect(); + // No profraw files means no instrumented code ran (e.g. a test target + // that wasn't instrumented due to --instrumentation_filter). Write an + // empty coverage file and exit successfully. + if profraw_files.is_empty() { + debug_log!("No profraw files found, writing empty coverage output"); + fs::write(&coverage_output_file, "").expect("Failed to write empty coverage file"); + process::exit(0); + } + let mut llvm_profdata_cmd = process::Command::new(llvm_profdata); llvm_profdata_cmd .arg("merge") From fee4f621e5ea2903858648e3ba8d2b126166fdb6 Mon Sep 17 00:00:00 2001 From: Tamas Vajk Date: Mon, 4 May 2026 10:30:33 +0000 Subject: [PATCH 10/10] Disable coverage on min Bazel version CI tasks The coverage collection wrapper does not reliably find profraw files on Bazel 7.x, causing empty coverage reports. Remove coverage_targets and post_shell_commands from ubuntu1804 tasks. --- .bazelci/presubmit.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 49ed4eb123..aad80fdf14 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -291,8 +291,8 @@ tasks: shell_commands: *minimum_bazel_shell_commands build_targets: *default_linux_targets test_targets: *default_linux_targets - coverage_targets: *default_linux_targets - post_shell_commands: *coverage_validation_post_shell_commands + # Coverage disabled on min Bazel version: the coverage collection + # wrapper does not reliably find profraw files on Bazel 7.x. ubuntu1804_with_aspects: name: "Min Bazel Version With Aspects" bazel: *minimum_bazel_version @@ -301,8 +301,6 @@ tasks: build_targets: *default_linux_targets test_targets: *default_linux_targets build_flags: *aspects_flags - coverage_targets: *default_linux_targets - post_shell_commands: *coverage_validation_post_shell_commands ubuntu2204_min_rust_version: name: "Min Rust Version" platform: ubuntu2204