This directory provides the Ferrocene Rust coverage workflow for Bazel-based
projects. It uses Ferrocene's symbol-report and blanket tools to generate
HTML coverage reports from .profraw files produced by Rust tests.
The workflow is intentionally split:
- Tests produce
.profrawfiles (can run on host or target hardware). - Reports are generated later on a host machine.
This makes it easy to collect coverage from cross-compiled tests or from hardware-in-the-loop runs.
- Run tests with coverage enabled:
bazel test --config=ferrocene-x86_64-linux --config=ferrocene-coverage \
--nocache_test_results \
//path/to:rust_tests- Generate coverage reports:
bazel run //:rust_coverage -- --min-line-coverage 80The default report directory is:
$(bazel info bazel-bin)/coverage/rust-tests/<target>/blanket/index.html
The script prints per-target line coverage plus an overall summary line.
Add score_tooling and score_toolchains_rust as dependencies:
bazel_dep(name = "score_tooling", version = "1.0.0")
bazel_dep(name = "score_toolchains_rust", version = "0.4.0")Add a Ferrocene coverage config. Names are examples; choose names that fit your repo:
# Ferrocene toolchain for host execution
build:ferrocene-x86_64-linux --host_platform=@score_bazel_platforms//:x86_64-linux
build:ferrocene-x86_64-linux --platforms=@score_bazel_platforms//:x86_64-linux
build:ferrocene-x86_64-linux --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu
# Coverage flags for rustc
build:ferrocene-coverage --@rules_rust//rust/settings:extra_rustc_flag=-Cinstrument-coverage
build:ferrocene-coverage --@rules_rust//rust/settings:extra_rustc_flag=-Clink-dead-code
build:ferrocene-coverage --@rules_rust//rust/settings:extra_rustc_flag=-Ccodegen-units=1
build:ferrocene-coverage --@rules_rust//rust/settings:extra_rustc_flag=-Cdebuginfo=2
build:ferrocene-coverage --@rules_rust//rust/settings:extra_exec_rustc_flag=-Cinstrument-coverage
build:ferrocene-coverage --@rules_rust//rust/settings:extra_exec_rustc_flag=-Clink-dead-code
build:ferrocene-coverage --@rules_rust//rust/settings:extra_exec_rustc_flag=-Ccodegen-units=1
build:ferrocene-coverage --@rules_rust//rust/settings:extra_exec_rustc_flag=-Cdebuginfo=2
test:ferrocene-coverage --run_under=@score_tooling//coverage:llvm_profile_wrapper
In a root BUILD file:
load("@score_tooling//coverage:coverage.bzl", "rust_coverage_report")
rust_coverage_report(
name = "rust_coverage",
bazel_configs = [
"ferrocene-x86_64-linux",
"ferrocene-coverage",
],
query = 'kind("rust_test", //...)',
min_line_coverage = "80",
)Run it with:
bazel run //:rust_coveragequery = 'kind("rust_test", //...) except //path/to:tests',If tests run on target hardware, copy the .profraw files back to the host
and point the report generator to the directory:
bazel run //:rust_coverage -- --profraw-dir /path/to/profrawYou can invoke the report generator from a top-level integration repo (for example, reference_integration) while targeting tests that live in external modules. Use a query that references external labels and run the wrapper target from the integration repo:
bazel run //images/linux_x86_64:per_rust_coverage --config=ferrocene-coverage -- \
--query 'kind("rust_test", @score_persistency//src/rust/...)'If the .profraw files were produced in that same workspace, the reporter
auto-discovers them under bazel-testlogs/ (including
bazel-testlogs/external/<repo>+ for external labels), so you do not need
to pass --profraw-dir. If they were copied from elsewhere, pass
--profraw-dir to point to the directory containing the .profraw files.
External source paths are resolved via Bazel's output_base so
external/<repo>/... paths are handled.
--min-line-coverage applies per target. If any target is below the minimum,
the script exits non-zero so CI can fail the job. An overall summary is printed
for visibility but does not change gating behavior.
- "running 0 tests": The Rust test harness found no
#[test]functions, so coverage is 0%. Add tests or exclude the target from the query. - "couldn't find source file" warnings: Usually path remapping or crate
mapping issues. Check that
crateattributes inrust_testtargets point to the library crate (or exclude the target). - Cached test results: Use
--nocache_test_resultsif you need to re-run tests and regenerate.profrawfiles.
- Verify the target contains real
#[test]functions. A rust_test target with no tests will run but report 0% coverage. - Ensure you ran tests with
--config=ferrocene-coverageso.profrawfiles exist. - If the test binary is cached, use
--nocache_test_results.
- Check
cratemapping onrust_testtargets. Ifcrate = "name"is used, ensure it refers to the library crate in the same package. - Confirm the reported paths exist in the workspace. Path remapping is required
so
blanketcan resolve files under--ferrocene-src.
- Ensure
test:ferrocene-coveragesets--run_under=@score_tooling//coverage:llvm_profile_wrapper. - Re-run tests with
--nocache_test_results. - If tests ran on target hardware, copy the
.profrawfiles back and pass--profraw-dir.
- The gate is per-target. A single target below the threshold fails the job.
- Use a stricter query (exclude known-zero targets) or add tests.
Keep coverage generation separate from docs:
-
Coverage workflow:
- run
bazel run //:rust_coverage - upload
bazel-bin/coverage/rust-testsas an artifact
- run
-
Docs workflow:
- download the artifact
- copy into the docs output (e.g.
docs/_static/coverage/) - publish Sphinx docs to GitHub Pages