diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index ad84f1066f..5a70b8cd4f 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1185,14 +1185,45 @@ def construct_arguments( rustc_flags.add(compilation_mode.strip_level, format = "--codegen=strip=%s") # For determinism to help with build distribution and such + # + # --remap-path-prefix tells rustc to replace a path prefix in all + # embedded paths (debug info, dep-info, panic locations, backtraces) + # so that absolute sandbox paths never leak into binaries or logs. + # + # When all sources are plain workspace files, remapping ${pwd} (the exec + # root) to remap_path_prefix (default ".") is enough. + # + # However, when a target mixes generated and non-generated sources (e.g. + # proto compile_data), transform_sources() symlinks every source file into + # bazel-out//bin/... so they sit next to the generated files. In + # that case the crate root is no longer a source file and its path starts + # with ctx.bin_dir.path (e.g. "bazel-out/k8-fastbuild/bin"). We detect + # this via crate_info.root.is_source and use a more specific remap that + # also strips the bin-dir component, giving clean workspace-relative paths + # in panic messages and backtraces. if remap_path_prefix != None: # `--remap-path-prefix` flags are applied in reverse order. We need to # specify the outermost directory (output_base) first, so that it's # remapped last. Otherwise we can end up with a partial rewrite where # "/path/to/output_base/execroot" becomes "./execroot" rather than ".". rustc_flags.add("--remap-path-prefix=${{output_base}}={}".format(remap_path_prefix)) - rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix)) - rustc_flags.add("--remap-path-prefix=${{exec_root}}={}".format(remap_path_prefix)) + if crate_info.root.is_source: + rustc_flags.add("--remap-path-prefix=${{pwd}}={}".format(remap_path_prefix)) + rustc_flags.add("--remap-path-prefix=${{exec_root}}={}".format(remap_path_prefix)) + else: + # Use add_all with the crate root File and a map_each callback + # so Bazel's path mapping (--experimental_output_paths=strip) + # can rewrite the config portion of the bin-dir prefix. + rustc_flags.add_all( + [crate_info.root], + map_each = _get_bin_dir_prefix, + format_each = "--remap-path-prefix=${pwd}/%s=" + remap_path_prefix, + ) + rustc_flags.add_all( + [crate_info.root], + map_each = _get_bin_dir_prefix, + format_each = "--remap-path-prefix=${exec_root}/%s=" + remap_path_prefix, + ) emit_without_paths = [] for kind in emit: @@ -1288,11 +1319,12 @@ def construct_arguments( if remap_path_prefix != None and _should_add_oso_prefix( toolchain, ): + oso_prefix = "${pwd}/" if crate_info.root.is_source else "${pwd}/" + ctx.bin_dir.path + "/" if ld_is_direct_driver: rustc_flags.add("--codegen=link-arg=-oso_prefix") - rustc_flags.add("${pwd}/", format = "--codegen=link-arg=%s") + rustc_flags.add(oso_prefix, format = "--codegen=link-arg=%s") else: - rustc_flags.add("--codegen=link-arg=-Wl,-oso_prefix,${pwd}/") + rustc_flags.add("--codegen=link-arg=-Wl,-oso_prefix," + oso_prefix) _add_native_link_flags( rustc_flags, @@ -2706,6 +2738,22 @@ def _add_native_link_flags( format_each = "-lstatic=%s", ) +def _get_bin_dir_prefix(file): + """Returns the bin-dir prefix (without trailing slash) from a generated file. + + For a generated file, file.path is "bazel-out//bin//" + and file.short_path is "/". The difference is the bin-dir + prefix. Using the File object with add_all/map_each lets Bazel apply + path mapping (--experimental_output_paths=strip) to the config portion. + + Args: + file (File): A generated file (e.g. the crate root). + + Returns: + str: The bin-dir prefix, e.g. "bazel-out/k8-fastbuild/bin". + """ + return file.path[:len(file.path) - len(file.short_path)].rstrip("/") + def _get_dirname(file): """A helper function for `_add_native_link_flags`. diff --git a/test/unit/remap_path_prefix/remap_path_prefix_test.bzl b/test/unit/remap_path_prefix/remap_path_prefix_test.bzl index a5e4d6728c..9a6a9d64e4 100644 --- a/test/unit/remap_path_prefix/remap_path_prefix_test.bzl +++ b/test/unit/remap_path_prefix/remap_path_prefix_test.bzl @@ -6,10 +6,13 @@ load("//rust:defs.bzl", "rust_binary", "rust_library") load( "//test/unit:common.bzl", "assert_action_mnemonic", + "assert_argv_contains", + "assert_argv_contains_prefix_suffix", "assert_list_contains_adjacent_elements", ) -def _remap_path_prefix_test_impl(ctx): +def _remap_path_prefix_source_test_impl(ctx): + """Verify remap flags for targets with plain source files.""" env = analysistest.begin(ctx) target = analysistest.target_under_test(env) @@ -24,7 +27,23 @@ def _remap_path_prefix_test_impl(ctx): return analysistest.end(env) -_remap_path_prefix_test = analysistest.make(_remap_path_prefix_test_impl) +_remap_path_prefix_source_test = analysistest.make(_remap_path_prefix_source_test_impl) + +def _remap_path_prefix_generated_test_impl(ctx): + """Verify remap flags for targets with generated sources (symlinked into bin dir).""" + env = analysistest.begin(ctx) + target = analysistest.target_under_test(env) + + action = target.actions[0] + assert_action_mnemonic(env, action, "Rustc") + + assert_argv_contains_prefix_suffix(env, action, "--remap-path-prefix=${pwd}/bazel-out/", "/bin=.") + assert_argv_contains_prefix_suffix(env, action, "--remap-path-prefix=${exec_root}/bazel-out/", "/bin=.") + assert_argv_contains(env, action, "--remap-path-prefix=${output_base}=.") + + return analysistest.end(env) + +_remap_path_prefix_generated_test = analysistest.make(_remap_path_prefix_generated_test_impl) def _subst_flags_test_impl(ctx): """Verify that process wrapper --subst flags are present.""" @@ -48,9 +67,12 @@ def remap_path_prefix_test_suite(name): Args: name (str): The name of the test suite. """ + + # Targets with generated sources (write_file produces non-source files, + # triggering transform_sources which symlinks into bin dir). write_file( - name = "remap_lib_src", - out = "remap_lib.rs", + name = "remap_lib_generated_src", + out = "remap_lib_generated.rs", content = [ "pub fn hello() {}", "", @@ -58,14 +80,14 @@ def remap_path_prefix_test_suite(name): ) rust_library( - name = "remap_lib", - srcs = [":remap_lib.rs"], + name = "remap_lib_generated", + srcs = [":remap_lib_generated.rs"], edition = "2021", ) write_file( - name = "remap_bin_src", - out = "remap_bin.rs", + name = "remap_bin_generated_src", + out = "remap_bin_generated.rs", content = [ "fn main() {}", "", @@ -73,34 +95,42 @@ def remap_path_prefix_test_suite(name): ) rust_binary( - name = "remap_bin", - srcs = [":remap_bin.rs"], + name = "remap_bin_generated", + srcs = [":remap_bin_generated.rs"], edition = "2021", ) - _remap_path_prefix_test( - name = "remap_path_prefix_lib_test", - target_under_test = ":remap_lib", + # Tests for plain source files (using existing dep.rs from the package). + _remap_path_prefix_source_test( + name = "remap_path_prefix_source_lib_test", + target_under_test = ":dep", + ) + + # Tests for generated sources (symlinked into bin dir). + _remap_path_prefix_generated_test( + name = "remap_path_prefix_generated_lib_test", + target_under_test = ":remap_lib_generated", ) - _remap_path_prefix_test( - name = "remap_path_prefix_bin_test", - target_under_test = ":remap_bin", + _remap_path_prefix_generated_test( + name = "remap_path_prefix_generated_bin_test", + target_under_test = ":remap_bin_generated", ) _subst_flags_test( name = "subst_flags_lib_test", - target_under_test = ":remap_lib", + target_under_test = ":remap_lib_generated", ) _subst_flags_test( name = "subst_flags_bin_test", - target_under_test = ":remap_bin", + target_under_test = ":remap_bin_generated", ) tests = [ - ":remap_path_prefix_lib_test", - ":remap_path_prefix_bin_test", + ":remap_path_prefix_source_lib_test", + ":remap_path_prefix_generated_lib_test", + ":remap_path_prefix_generated_bin_test", ":subst_flags_lib_test", ":subst_flags_bin_test", ]