Skip to content

Commit 81ca812

Browse files
authored
Update coverage to work with Bazel 9 (bazelbuild#3944)
When `--experimental_split_coverage_postprocessing` is enabled (default in Bazel 9), Bazel no longer sets `RUNFILES_DIR` during coverage collection. The coverage script now treats `RUNFILES_DIR` as optional -- when absent, it resolves `llvm-cov`, `llvm-profdata`, and the test binary directly from the exec root using `COVERAGE_DIR` to derive the configuration bin directory via a new `config_bin_dir` helper.
1 parent 65c96a3 commit 81ca812

1 file changed

Lines changed: 60 additions & 18 deletions

File tree

util/collect_coverage/collect_coverage.rs

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,12 @@
99
//! data files to compute the coverage report.
1010
//!
1111
//! This script assumes the following environment variables are set:
12-
//! - `COVERAGE_DIR``: Directory containing metadata files needed for coverage collection (e.g. gcda files, profraw).
12+
//! - `COVERAGE_DIR`: Directory containing metadata files needed for coverage collection (e.g. gcda files, profraw).
1313
//! - `COVERAGE_OUTPUT_FILE`: The coverage action output path.
1414
//! - `ROOT`: Location from where the code coverage collection was invoked.
15-
//! - `RUNFILES_DIR`: Location of the test's runfiles.
15+
//! - `RUNFILES_DIR` (optional): Location of the test's runfiles. Not set in split
16+
//! coverage postprocessing mode (`--experimental_split_coverage_postprocessing`).
17+
//! - `TEST_BINARY`: Runfiles-relative path to the test binary (used when `RUNFILES_DIR` is absent).
1618
//! - `VERBOSE_COVERAGE`: Print debug info from the coverage scripts
1719
//!
1820
//! The script looks in $COVERAGE_DIR for the Rust metadata coverage files
@@ -85,31 +87,71 @@ fn find_test_binary(execroot: &Path, runfiles_dir: &Path) -> PathBuf {
8587
}
8688
}
8789

90+
/// Derive the Bazel output configuration bin directory from `COVERAGE_DIR`.
91+
///
92+
/// `COVERAGE_DIR` follows the stable convention `bazel-out/<config>/testlogs/...`.
93+
/// Extracting the first two path components gives `bazel-out/<config>`, which
94+
/// combined with `bin` yields the directory containing the test binary.
95+
fn config_bin_dir(execroot: &Path, coverage_dir: &Path) -> PathBuf {
96+
let coverage_rel = coverage_dir.strip_prefix(execroot).unwrap_or(coverage_dir);
97+
let mut components = coverage_rel.components();
98+
let bazel_out = components
99+
.next()
100+
.expect("COVERAGE_DIR should have at least 2 path components");
101+
let config = components
102+
.next()
103+
.expect("COVERAGE_DIR should have at least 2 path components");
104+
PathBuf::from(bazel_out.as_os_str())
105+
.join(config.as_os_str())
106+
.join("bin")
107+
}
108+
88109
fn main() {
89110
let coverage_dir = PathBuf::from(env::var("COVERAGE_DIR").unwrap());
90111
let execroot = PathBuf::from(env::var("ROOT").unwrap());
91-
let mut runfiles_dir = PathBuf::from(env::var("RUNFILES_DIR").unwrap());
92112

93-
if !runfiles_dir.is_absolute() {
94-
runfiles_dir = execroot.join(runfiles_dir);
95-
}
113+
// RUNFILES_DIR is explicitly removed by Bazel in split coverage
114+
// postprocessing mode (--experimental_split_coverage_postprocessing).
115+
let runfiles_dir = env::var("RUNFILES_DIR")
116+
.map(|d| {
117+
let p = PathBuf::from(d);
118+
if p.is_absolute() {
119+
p
120+
} else {
121+
execroot.join(p)
122+
}
123+
})
124+
.ok();
96125

97126
debug_log!("ROOT: {}", execroot.display());
98-
debug_log!("RUNFILES_DIR: {}", runfiles_dir.display());
127+
match runfiles_dir {
128+
Some(ref rd) => debug_log!("RUNFILES_DIR: {}", rd.display()),
129+
None => debug_log!("RUNFILES_DIR: not set (split coverage postprocessing)"),
130+
}
99131

100132
let coverage_output_file = coverage_dir.join("coverage.dat");
101133
let profdata_file = coverage_dir.join("coverage.profdata");
102-
let llvm_cov = find_metadata_file(
103-
&execroot,
104-
&runfiles_dir,
105-
&env::var("RUST_LLVM_COV").unwrap(),
106-
);
107-
let llvm_profdata = find_metadata_file(
108-
&execroot,
109-
&runfiles_dir,
110-
&env::var("RUST_LLVM_PROFDATA").unwrap(),
111-
);
112-
let test_binary = find_test_binary(&execroot, &runfiles_dir);
134+
let llvm_cov_path = env::var("RUST_LLVM_COV").unwrap();
135+
let llvm_profdata_path = env::var("RUST_LLVM_PROFDATA").unwrap();
136+
let llvm_cov = match runfiles_dir {
137+
Some(ref rd) => find_metadata_file(&execroot, rd, &llvm_cov_path),
138+
None => execroot.join(&llvm_cov_path),
139+
};
140+
let llvm_profdata = match runfiles_dir {
141+
Some(ref rd) => find_metadata_file(&execroot, rd, &llvm_profdata_path),
142+
None => execroot.join(&llvm_profdata_path),
143+
};
144+
let test_binary = match runfiles_dir {
145+
Some(ref rd) => find_test_binary(&execroot, rd),
146+
None => {
147+
let bin_dir = config_bin_dir(&execroot, &coverage_dir);
148+
let test_binary = execroot
149+
.join(bin_dir)
150+
.join(env::var("TEST_BINARY").unwrap());
151+
debug_log!("Resolved TEST_BINARY to: {}", test_binary.display());
152+
test_binary
153+
}
154+
};
113155
let profraw_files: Vec<PathBuf> = fs::read_dir(coverage_dir)
114156
.unwrap()
115157
.flatten()

0 commit comments

Comments
 (0)