@@ -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+
414455resolve_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