coverage: replace the Ferrocene symbol-report/blanket flow with the reusable LLVM coverage pipeline - #394
Open
dcalavrezo-qorix wants to merge 3 commits into
Open
coverage: replace the Ferrocene symbol-report/blanket flow with the reusable LLVM coverage pipeline#394dcalavrezo-qorix wants to merge 3 commits into
dcalavrezo-qorix wants to merge 3 commits into
Conversation
…LLVM pipeline Centralize the LLVM source-based coverage pipeline (proven in eclipse-score/communication and the persistency port) as the reusable coverage module of score_tooling: - Unified C++ + Rust coverage (line + branch) via llvm-cov directly: custom --coverage_output_generator (merger) and --coverage_report_generator (reporter) replacing gcov/genhtml. - Untested in-scope files appear at exact 0% through llvm-cov --empty-profile baselines over covmap-instrumented archives (including rlib expansion for Rust). - Justification system (COV_JUSTIFIED markers + YAML) with effective coverage metric, stale detection and threshold gating. - Consumer API: score_coverage_scope + score_coverage_reporter macros (coverage/defs.bzl); the reporter_wrapper resolves runfiles across module boundaries and receives the consumer's LLVM tool labels. - Unit tests (coverage/tests) and a consumer-style integration workspace (coverage/integration_tests) run in CI; the integration test asserts exact-0% entries for untested C++ and Rust files, gate behavior at high/low thresholds and the justification round-trip. - Adoption guide (coverage/README.md) and mechanism deep-dive (coverage/COVERAGE_GUIDE.md). BREAKING: rust_coverage_report, //coverage:ferrocene_report and the symbol-report/blanket helper scripts are removed. llvm_profile_wrapper and //coverage:combined_report are kept unchanged.
use_format_targets (third_party/format/macros.bzl) is public consumer API and load()s @aspect_rules_lint//format:defs.bzl; as a dev_dependency the repo is invisible to score_tooling's repo mapping when downstream modules consume the macro, breaking every consumer of use_format_targets.
…t coverage The integration workspace (and the adoption guide) declared a second Ferrocene instance whose only purpose was attaching the coverage-tools tarball — a leftover from before score_toolchains_rust 0.10.0. The standard toolchains in score_toolchains_rust's own MODULE.bazel already pin the ferrocene_toolchain_builder 1.3.1 coverage-tools (same LLVM tree as rustc) including the required link flags, so the toolchain registered for regular builds is the one that produces coverage. Consumer MODULE.bazel footprint for Rust coverage drops to zero; only the standard toolchain registration remains: common --extra_toolchains=@score_toolchains_rust//toolchains/ferrocene:ferrocene_x86_64_unknown_linux_gnu Integration checks re-validated unchanged (0% baselines, gate, justification).
Coverage ReportCoverage report was generated. Full report can be downloaded from the CI artifacts (expand Artifacts at the bottom of the run). Overall coverage rate: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR makes @score_tooling//coverage the shared home of the LLVM source-based coverage pipeline already proven in communication (merged there via eclipse-score/communication#772, visible in its nightly reports) and ported to persistency (not pushed yet). It replaces the previous Ferrocene symbol-report/blanket workflow.
What the pipeline provides, in one report:
Consumer API (full guide in coverage/README.md, mechanism deep-dive in coverage/COVERAGE_GUIDE.md, reference consumer in coverage/integration_tests/):
LLVM/Ferrocene toolchains stay consumer-side (this repo's established dev-dependency convention); the Rust side requires score_toolchains_rust ≥ 0.10.0 .
What we kept from @olivembo's score_cpp_policies#9
PR #9 explored centralizing the (pre-Rust) pipeline and its packaging layer is genuinely good — this PR adopts it:
What we deliberately did not take, and why:
Gotchas fixed (in detail)
These are the things that break exactly when the pipeline moves from an in-repo copy into a shared module — worth reading before reviewing the wrapper code:
aspect_rules_lint was a dev_dependency — breaking use_format_targets for every consumer (separate commit, independent of coverage). third_party/format/macros.bzl does load("@aspect_rules_lint//format:defs.bzl", ...). A load() statement resolves repo names against the repo mapping of the module that owns the .bzl file — i.e. score_tooling's own mapping. Dev dependencies are dropped from that mapping whenever score_tooling is consumed as a dependency rather than built standalone, so any downstream repo calling use_format_targets() (public API per our README) failed with No repository visible as '@aspect_rules_lint' from repository '@@score_tooling+' — even if the consumer declares aspect_rules_lint itself (the consumer's mapping is irrelevant to score_tooling's load()). Our own CI never caught it because in this repo score_tooling is the root module, where dev deps are visible. Fix: make it a regular dependency.
Runfiles paths break across module boundaries. The in-repo reporter_wrapper.bzl built runfiles paths as "_main/" + short_path — correct only while every file lives in the main repo. In the centralized setup one wrapper mixes files from three worlds: the consumer repo (MODULE.bazel, allowlist, baseline manifest → _main/...), score_tooling (the reporter binary → short_path starts with ../score_tooling+/...), and toolchain repos (llvm-cov etc.). The rewritten rule uses _rlocation_path() (strip ../ for external files, prepend workspace name for main-repo files) for every substituted path. Related: the launcher derives its own RUNFILES_DIR from $0 before falling back to the environment — Bazel invokes the coverage report generator from inside its coverage machinery where RUNFILES_DIR is already set pointing at the test's runfiles tree, so trusting the inherited value resolves every path against the wrong tree.
runfiles.CurrentRepository() returns the wrong repo once the script moves. reporter.py resolved baseline-manifest entries via Rlocation(CurrentRepository() + "/" + line). CurrentRepository() answers "which repo does this Python file live in" — previously "" (main repo), now score_tooling+. But the manifest lists files built by the consumer, which is always the root module in a coverage run. The reporter now resolves those entries against _main explicitly (with a comment explaining why CurrentRepository() must not be used there).
Hardcoded canonical repo names are a version trap. PR Fix offset mode #9's orchestration script resolves its helper binaries via hand-rolled runfiles lookups containing literal score_cpp_policies+/... paths. The canonical-name separator changed across Bazel releases (~ → +) and canonical names are explicitly not API — under local_path_override in a test workspace they differ again (plausibly why that script is broken in PR Fix offset mode #9's own tests/ workspace). Our generate_coverage_html.sh avoids runfiles entirely: it re-invokes bazel run @score_tooling//coverage:justify / :effective_coverage from the consumer workspace, where the apparent repo name is resolved by Bazel itself. Slightly slower, immune to the problem.
LCOV/HTML relativization must not touch hrefs. When adopting PR Fix offset mode #9's HTML path relativization we found a blanket text replacement corrupts the report: llvm-cov's hrefs and on-disk layout embed the absolute source path without a leading slash (coverage/home/user/src/...), so replacing /home/user/src/ hits the middle of every link. The rewrite is therefore scoped to the source-name-title header text only; a unit test pins hrefs staying untouched.
For downstream adopters two more consumer-side gotchas are documented (hit while validating with persistency, fixes belong in the consumer repos): a git_override pinning trlc < 3.0.0 breaks loading current score_tooling (the registered rules_score Sphinx toolchain loads @trlc//:trlc.bzl symbols that only exist in 3.0.0, and a git_override forces its commit graph-wide); and copyright_checker/use_format_targets are no longer re-exported from //:defs.bzl (load from //cr_checker:cr_checker.bzl / //third_party/format:macros.bzl).
Validation
Follow-ups (separate PRs / repos)