diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 1b647d44f5..b40eee6a4c 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -965,12 +965,16 @@ def has_location_expansion(values): return False def _args_map_bin_dir(file): - """Extract `bazel-out//bin` from a File whose path lives in the configuration's bin directory. + """Return a generated File's output root in its consuming action's layout. - Evaluated at action-execution time so that Bazel's path mapping (`--experimental_output_paths=strip`) can - rewrite the `` segment to `cfg` before we slice it off. + `File.root.path` provides the root depth, including sibling repositories; + `File.path` carries the configuration selected by the action's path mapper. """ - return "/".join(file.path.split("/", 3)[:3]) + return "/".join(file.path.split("/")[:len(file.root.path.split("/"))]) + +def _args_map_output_configuration(file): + """Return the output configuration in the consuming action's path layout.""" + return _args_map_bin_dir(file).split("/")[-2] def construct_arguments( *, @@ -1105,6 +1109,16 @@ def construct_arguments( for build_env_file in build_env_files: process_wrapper_flags.add("--env-file", build_env_file) + # Source env files have no output configuration to substitute. + if not build_env_file.is_source: + process_wrapper_flags.add_all( + [build_env_file], + before_each = "--subst", + format_each = "rustc_env_file:" + build_env_file.short_path.replace("%", "%25").replace("=", "%3D") + "=%s", + map_each = _args_map_output_configuration, + expand_directories = False, + ) + process_wrapper_flags.add_all(build_flags_files, before_each = "--arg-file") all_allowed_unstable_features = [] @@ -1362,8 +1376,14 @@ def construct_arguments( # If linker_type is not explicitly set, infer from which linker is actually being used ld_is_direct_driver = False + # Metadata does not link, so avoid expanding string-valued linker flags. + adds_linker_args = not build_metadata and ( + ("link" in emit and crate_info.type not in ["rlib", "lib"]) or + add_flags_for_binary + ) + # Link! - if ("link" in emit and crate_info.type not in ["rlib", "lib"]) or add_flags_for_binary: + if adds_linker_args: # Rust's built-in linker can handle linking wasm files. We don't want to attempt to use the cc # linker since it won't understand. compilation_mode = ctx.var["COMPILATION_MODE"] @@ -1645,7 +1665,16 @@ def construct_arguments( rustc_path = rustc_path, rustc_flags = rustc_flags, extra_rustc_flags = rust_flags_args, - supports_path_mapping = not target_has_location_expansion, + # Native linker selection and C/C++ feature expansion flatten generated + # paths into strings that Bazel cannot remap. Pure Wasm remains mappable. + supports_path_mapping = not target_has_location_expansion and not ( + adds_linker_args and + (include_link_flags or force_depend_on_objects) and + ( + toolchain.target_arch not in ("wasm32", "wasm64") or + bool(dep_info.transitive_noncrates) + ) + ), all = all_args, ) diff --git a/rust/private/rustdoc.bzl b/rust/private/rustdoc.bzl index 031a44d345..694ce0d4b8 100644 --- a/rust/private/rustdoc.bzl +++ b/rust/private/rustdoc.bzl @@ -160,7 +160,8 @@ def rustdoc_compile_action( build_flags_files = build_flags_files, emit = [], remap_path_prefix = None, - add_flags_for_binary = True, + # Documentation does not link; doctest compilation does. + add_flags_for_binary = is_test, include_link_flags = is_test, force_depend_on_objects = is_test, skip_expanding_rustc_env = True, diff --git a/test/unit/metadata_output_groups/metadata_output_groups.bzl b/test/unit/metadata_output_groups/metadata_output_groups.bzl index f3cfc44159..276eef3af2 100644 --- a/test/unit/metadata_output_groups/metadata_output_groups.bzl +++ b/test/unit/metadata_output_groups/metadata_output_groups.bzl @@ -32,6 +32,13 @@ def _metadata_output_groups_present_test_impl(ctx): "Expected %s to end with .rustc-output" % rustc_rmeta_output[0], ) + metadata_action = [action for action in tut.actions if action.mnemonic == "RustcMetadata"][0] + asserts.equals(env, [], [ + arg + for arg in metadata_action.argv + if arg.startswith("--codegen=linker=") or arg.startswith("--codegen=link-arg=") + ]) + return analysistest.end(env) def _metadata_output_groups_missing_test_impl(ctx): diff --git a/test/unit/native_deps/native_deps_test.bzl b/test/unit/native_deps/native_deps_test.bzl index 20423203fa..ef1d7cd81b 100644 --- a/test/unit/native_deps/native_deps_test.bzl +++ b/test/unit/native_deps/native_deps_test.bzl @@ -192,6 +192,10 @@ def _bin_has_native_dep_and_alwayslink_test_impl(ctx, use_cc_linker): link_args = _extract_linker_args(action.argv) bin_dir = get_bin_dir_from_action(action) + # Native linking must retain the same output root as its declared output. + output_bin_dir = action.outputs.to_list()[0].path.split("/bin/", 1)[0] + "/bin" + asserts.equals(env, output_bin_dir, bin_dir) + # Validate bin_dir structure (ignoring ST-{hash} suffix from config transitions) _assert_bin_dir_structure(env, ctx, bin_dir, toolchain) diff --git a/test/unit/rustdoc/rustdoc_unit_test.bzl b/test/unit/rustdoc/rustdoc_unit_test.bzl index 59dfc3ae50..d4d695ce8b 100644 --- a/test/unit/rustdoc/rustdoc_unit_test.bzl +++ b/test/unit/rustdoc/rustdoc_unit_test.bzl @@ -52,6 +52,7 @@ def _common_rustdoc_checks(env, tut): # `Rustdoc` actions assert_argv_contains_prefix_not(env, action, "--remap-path-prefix") assert_argv_contains_prefix_not(env, action, "--emit") + assert_argv_contains_prefix_not(env, action, "--codegen=linker=") def _rustdoc_for_lib_test_impl(ctx): env = analysistest.begin(ctx) diff --git a/tools/rust_analyzer/env_file.bzl b/tools/rust_analyzer/env_file.bzl index 5e1f6abcbd..959e56edf5 100644 --- a/tools/rust_analyzer/env_file.bzl +++ b/tools/rust_analyzer/env_file.bzl @@ -1,32 +1,27 @@ -"""A `rustc_env_files`-format rule whose VALUE is a path-mapping-aware file path.""" - -load("@bazel_features//:features.bzl", "bazel_features") - -def _arg_map_pair(value): - key, file = value - return "{}=${{pwd}}/{}".format(key, file.path) +"""A `rustc_env_files`-format rule whose file paths follow the consuming action.""" def _env_file_impl(ctx): out = ctx.actions.declare_file(ctx.label.name + ".rustc_env") - content = ctx.actions.args() - content.set_param_file_format("multiline") - content.add_all( - [(ctx.attr.key, ctx.file.src)], - map_each = _arg_map_pair, - expand_directories = False, - ) - if bazel_features.rules.write_action_has_execution_requirements: - ctx.actions.write( - output = out, - content = content, - execution_requirements = {"supports-path-mapping": ""}, - ) - else: - ctx.actions.write( - output = out, - content = content, + # ':' reserves this namespace; escape '%' first to keep keys injective. + substitution = "rustc_env_file:" + out.short_path.replace("%", "%25").replace("=", "%3D") + source_root = ctx.file.src.root.path + root_components = source_root.split("/") + + # Generated inputs share this rule's configuration; defer that component to + # the consuming action while preserving their repository and output root. + source_path = ( + ctx.file.src.path if ctx.file.src.is_source else "{}/${{{}}}/{}/{}".format( + "/".join(root_components[:-2]), + substitution, + root_components[-1], + ctx.file.src.path[len(source_root) + 1:], ) + ) + ctx.actions.write( + output = out, + content = "{}=${{pwd}}/{}\n".format(ctx.attr.key, source_path), + ) return [DefaultInfo(files = depset([out]))] env_file = rule( @@ -43,11 +38,10 @@ env_file = rule( ), }, doc = """\ -Emit a one-line `KEY=${pwd}/\\n` file suitable for `rust_library`'s -`rustc_env_files` attribute. The path is generated through `Args.add_all`'s -`format_each` so Bazel's path mapping (`--experimental_output_paths=strip`) -rewrites it before the action runs, and the `${pwd}` prefix is later resolved -to the exec_root by `process_wrapper`'s env-block substitution. +Emit a `KEY=${pwd}/\\n` file suitable for `rustc_env_files`. +Generated inputs share this rule's configuration. A File-backed substitution +selects the consuming action's mapped or unmapped configuration while preserving +the input's repository and output root. Pair with a matching `compile_data = [src]` on the consumer crate and use `include_str!(env!("KEY"))` in Rust to embed the file's content at compile