Skip to content

Commit 935a534

Browse files
coverage: update (ferrocene) (#111)
Allow running Ferrocene rust coverage from reference_integration against external labels by resolving external repo roots and test.outputs paths. Infer crate_root more robustly, pass absolute paths to symbol-report and map external sources via output_base for blanket path resolution. Document integration-workspace usage and add explanatory comments. Signed-off-by: Dan Calavrezo <195309321+dcalavrezo-qorix@users.noreply.github.com>
1 parent 1767102 commit 935a534

File tree

3 files changed

+133
-25
lines changed

3 files changed

+133
-25
lines changed

MODULE.bazel

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
module(
1515
name = "score_tooling",
16-
version = "1.1.0",
16+
version = "0.0.0",
1717
compatibility_level = 1,
1818
)
1919

@@ -94,7 +94,7 @@ multitool.hub(
9494
)
9595
use_repo(multitool, "yamlfmt_hub")
9696

97-
bazel_dep(name = "score_docs_as_code", version = "2.2.0")
97+
bazel_dep(name = "score_docs_as_code", version = "3.0.0", dev_dependency = True)
9898
git_override(
9999
module_name = "score_docs_as_code",
100100
commit = "718388bd5e0f10debd97a131d238ad4d758ddd1e",

coverage/README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,26 @@ and point the report generator to the directory:
108108
bazel run //:rust_coverage -- --profraw-dir /path/to/profraw
109109
```
110110

111+
## Running from an integration workspace (external labels)
112+
113+
You can invoke the report generator from a top-level integration repo (for
114+
example, reference_integration) while targeting tests that live in external
115+
modules. Use a query that references external labels and run the wrapper
116+
target from the integration repo:
117+
118+
```bash
119+
bazel run //images/linux_x86_64:per_rust_coverage --config=ferrocene-coverage -- \
120+
--query 'kind("rust_test", @score_persistency//src/rust/...)'
121+
```
122+
123+
If the `.profraw` files were produced in that same workspace, the reporter
124+
auto-discovers them under `bazel-testlogs/` (including
125+
`bazel-testlogs/external/<repo>+` for external labels), so you do not need
126+
to pass `--profraw-dir`. If they were copied from elsewhere, pass
127+
`--profraw-dir` to point to the directory containing the `.profraw` files.
128+
External source paths are resolved via Bazel's output_base so
129+
`external/<repo>/...` paths are handled.
130+
111131
## Coverage Gate Behavior
112132

113133
`--min-line-coverage` applies per target. If any target is below the minimum,

coverage/ferrocene_report.sh

Lines changed: 111 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ label_to_path() {
335335
local label
336336
local pkg="${2:-}"
337337
label="$(strip_quotes "$1")"
338+
# External labels look like "@repo//pkg:target". Strip the repo prefix so
339+
# path conversion works for both workspace and external repos.
340+
if [[ "${label}" == @*//?* ]]; then
341+
label="//${label#*//}"
342+
fi
343+
# If the label still starts with "@", we do not know how to map it to a path.
338344
if [[ "${label}" == @* ]]; then
339345
echo ""
340346
return 0
@@ -403,6 +409,10 @@ label_pkg() {
403409
if [[ "${label}" =~ ^Label\\(\"(.*)\"\\)$ ]]; then
404410
label="${BASH_REMATCH[1]}"
405411
fi
412+
# External labels include "@repo//". Strip the repo prefix to get the package.
413+
if [[ "${label}" == @*//?* ]]; then
414+
label="//${label#*//}"
415+
fi
406416
if [[ "${label}" == //* ]]; then
407417
local rest="${label#//}"
408418
echo "${rest%%:*}"
@@ -411,6 +421,37 @@ label_pkg() {
411421
echo ""
412422
}
413423

424+
# Resolve the "external/<repo>" prefix for an external label.
425+
# We use bazel query --output=location to find a real file path, then extract
426+
# the repo name from either "external/<repo>/..." or ".../external/<repo>/...".
427+
workspace_root_for_label() {
428+
local label
429+
label="$(strip_quotes "$1")"
430+
if [[ "${label}" =~ ^Label\\(\"(.*)\"\\)$ ]]; then
431+
label="${BASH_REMATCH[1]}"
432+
fi
433+
# Non-external labels live in the workspace, so no external prefix is needed.
434+
if [[ "${label}" != @* ]]; then
435+
echo ""
436+
return 0
437+
fi
438+
# The location output may be absolute; handle both direct external paths
439+
# and absolute paths that contain "/external/<repo>/".
440+
local location
441+
location="$(bazel query --output=location "${label}" 2>/dev/null | head -n 1)"
442+
location="${location%%:*}"
443+
local rest=""
444+
if [[ "${location}" == external/* ]]; then
445+
rest="${location#external/}"
446+
elif [[ "${location}" == */external/* ]]; then
447+
rest="${location#*/external/}"
448+
fi
449+
local repo="${rest%%/*}"
450+
if [[ -n "${repo}" ]]; then
451+
echo "external/${repo}"
452+
fi
453+
}
454+
414455
resolve_runfile() {
415456
local bin="$1"
416457
local name="$2"
@@ -563,10 +604,20 @@ for label in "${targets[@]}"; do
563604
pkg="${pkg%%:*}"
564605
name="${label##*:}"
565606

607+
# Resolve the package path and repo root so test.outputs works for
608+
# workspace labels (//pkg:target) and external labels (@repo//pkg:target).
609+
label_pkg_path="$(label_pkg "${label}")"
610+
if [[ -z "${label_pkg_path}" ]]; then
611+
label_pkg_path="${pkg}"
612+
fi
613+
label_repo_root="$(workspace_root_for_label "${label}")"
614+
566615
if [[ -n "${PROFRAW_DIR}" ]]; then
567616
test_out_dir="${PROFRAW_DIR}"
617+
elif [[ -n "${label_repo_root}" ]]; then
618+
test_out_dir="${PROFRAW_ROOT}/${label_repo_root}/${label_pkg_path}/${name}/test.outputs"
568619
else
569-
test_out_dir="${PROFRAW_ROOT}/${pkg}/${name}/test.outputs"
620+
test_out_dir="${PROFRAW_ROOT}/${label_pkg_path}/${name}/test.outputs"
570621
fi
571622

572623
shopt -s nullglob
@@ -622,46 +673,77 @@ for label in "${targets[@]}"; do
622673
if [[ -z "${crate_pkg}" ]]; then
623674
crate_pkg="${pkg}"
624675
fi
676+
repo_root="$(workspace_root_for_label "${crate_target}")"
625677

626678
crate_root_raw="$(query_labels_attr "${crate_target}" "crate_root")"
627679
if [[ -z "${crate_root_raw}" ]]; then
628680
crate_root_raw="$(query_attr_build "${crate_target}" "crate_root")"
629681
fi
630682
crate_root="$(label_to_path "${crate_root_raw}" "${crate_pkg}")"
631-
if [[ -z "${crate_root}" ]]; then
632-
# Prefer explicit srcs for rust_test targets when no crate attribute is set.
633-
srcs_label="$(query_labels_attr "${label}" "srcs")"
634-
if [[ -n "${srcs_label}" ]]; then
635-
srcs_path="$(label_to_path "${srcs_label}" "${pkg}")"
636-
if [[ -n "${srcs_path}" && "${srcs_path}" == *.rs ]]; then
637-
crate_root="${srcs_path}"
638-
fi
639-
fi
640-
fi
683+
# First, try conventional crate roots to avoid choosing a random source file.
641684
if [[ -z "${crate_root}" ]]; then
642685
for candidate in \
643686
"${crate_pkg}/src/lib.rs" \
644687
"${crate_pkg}/src/main.rs" \
645688
"${crate_pkg}/lib.rs" \
646689
"${crate_pkg}/main.rs"; do
647-
if [[ -f "${workspace}/${candidate}" ]]; then
690+
if [[ -n "${repo_root}" ]]; then
691+
if [[ -f "${exec_root}/${repo_root}/${candidate}" ]]; then
692+
crate_root="${candidate}"
693+
break
694+
fi
695+
elif [[ -f "${workspace}/${candidate}" ]]; then
648696
crate_root="${candidate}"
649697
break
650698
fi
651699
done
652-
if [[ -z "${crate_root}" ]]; then
653-
echo "Skipping ${label}: could not determine crate root for ${crate_target}" >&2
654-
continue
700+
fi
701+
# If there is no conventional root, fall back to the crate's declared srcs.
702+
if [[ -z "${crate_root}" ]]; then
703+
srcs_label="$(query_labels_attr "${crate_target}" "srcs")"
704+
if [[ -n "${srcs_label}" ]]; then
705+
srcs_path="$(label_to_path "${srcs_label}" "${crate_pkg}")"
706+
if [[ -n "${srcs_path}" && "${srcs_path}" == *.rs ]]; then
707+
crate_root="${srcs_path}"
708+
fi
655709
fi
656710
fi
657-
711+
if [[ -z "${crate_root}" ]]; then
712+
# As a last resort, try rust_test srcs when the test target defines them.
713+
# This handles rust_test targets that directly list their sources.
714+
srcs_label="$(query_labels_attr "${label}" "srcs")"
715+
if [[ -n "${srcs_label}" ]]; then
716+
srcs_path="$(label_to_path "${srcs_label}" "${pkg}")"
717+
if [[ -n "${srcs_path}" && "${srcs_path}" == *.rs ]]; then
718+
crate_root="${srcs_path}"
719+
fi
720+
fi
721+
fi
722+
# Without a crate root, symbol-report cannot build the crate.
723+
if [[ -z "${crate_root}" ]]; then
724+
echo "Skipping ${label}: could not determine crate root for ${crate_target}" >&2
725+
continue
726+
fi
727+
# Convert the crate root into an absolute path. External repos live under
728+
# exec_root/external/<repo> (symlinked from output_base), while workspace
729+
# sources live under $workspace.
658730
if [[ "${crate_root}" != /* ]]; then
659-
crate_root="${workspace}/${crate_root}"
731+
if [[ -n "${repo_root}" && "${crate_root}" != "${repo_root}/"* ]]; then
732+
crate_root="${repo_root}/${crate_root}"
733+
fi
734+
if [[ "${crate_root}" == external/* ]]; then
735+
crate_root="${exec_root}/${crate_root}"
736+
else
737+
crate_root="${workspace}/${crate_root}"
738+
fi
660739
fi
661740

741+
# Keep a workspace- or exec_root-relative path for reporting and mapping.
662742
crate_root_rel="${crate_root}"
663743
if [[ "${crate_root_rel}" == "${workspace}/"* ]]; then
664744
crate_root_rel="${crate_root_rel#${workspace}/}"
745+
elif [[ "${crate_root_rel}" == "${exec_root}/"* ]]; then
746+
crate_root_rel="${crate_root_rel#${exec_root}/}"
665747
fi
666748

667749
crate_name="$(normalize_scalar "$(query_attr_build "${crate_target}" "crate_name")")"
@@ -754,6 +836,7 @@ for label in "${targets[@]}"; do
754836
remap_args+=("--remap-path-prefix=${workspace}/=.")
755837
fi
756838

839+
# Pass the absolute crate root; relative external paths fail to canonicalize.
757840
(
758841
cd "${exec_root}"
759842
SYMBOL_REPORT_OUT="${symbol_report_json}" \
@@ -767,7 +850,7 @@ for label in "${targets[@]}"; do
767850
--sysroot "${sysroot_arg}" \
768851
-o /dev/null \
769852
"${remap_args[@]}" \
770-
"${crate_root_rel}"
853+
"${crate_root}"
771854
)
772855

773856
# Normalize symbol-report paths to be workspace-relative (like the demo),
@@ -779,18 +862,23 @@ for label in "${targets[@]}"; do
779862
bin_arg="${bin_rel}"
780863
fi
781864

782-
# Blanket expects report paths to resolve under --ferrocene-src; add a
783-
# path-equivalence so workspace files map cleanly to report entries.
865+
# Blanket resolves report filenames by joining them with --ferrocene-src.
866+
# Use a path-equivalence so source files map cleanly to report entries.
867+
# For external crates, profiler paths are absolute under output_base/external,
868+
# so we point --ferrocene-src there instead of the workspace.
784869
ferrocene_src="${workspace}"
870+
if [[ "${crate_root_rel}" == external/* ]]; then
871+
ferrocene_src="${output_base}"
872+
fi
785873
crate_root_dir_rel="$(dirname "${crate_root_rel}")"
786874
path_prefix="${crate_root_rel%%/*}"
787875
if [[ -n "${path_prefix}" && "${path_prefix}" != "${crate_root_rel}" && "${path_prefix}" != "." ]]; then
788876
# Broader remap to cover any file under the top-level directory (e.g. src/...).
789-
path_equiv_args=("--path-equivalence" "${path_prefix},${workspace}/${path_prefix}")
877+
path_equiv_args=("--path-equivalence" "${path_prefix},${ferrocene_src}/${path_prefix}")
790878
elif [[ "${crate_root_dir_rel}" == "." ]]; then
791-
path_equiv_args=("--path-equivalence" ".,${workspace}")
879+
path_equiv_args=("--path-equivalence" ".,${ferrocene_src}")
792880
else
793-
path_equiv_args=("--path-equivalence" "${crate_root_dir_rel},${workspace}/${crate_root_dir_rel}")
881+
path_equiv_args=("--path-equivalence" "${crate_root_dir_rel},${ferrocene_src}/${crate_root_dir_rel}")
794882
fi
795883

796884
(

0 commit comments

Comments
 (0)