diff --git a/cargo/private/cargo_build_script.bzl b/cargo/private/cargo_build_script.bzl index c23108851a..c903b38228 100644 --- a/cargo/private/cargo_build_script.bzl +++ b/cargo/private/cargo_build_script.bzl @@ -449,7 +449,7 @@ def _cargo_build_script_impl(ctx): # Pull in env vars which may be required for the cc_toolchain to work (e.g. on OSX, the SDK version). # We hope that the linker env is sufficient for the whole cc_toolchain. cc_toolchain, feature_configuration = find_cc_toolchain(ctx) - linker, _, link_args, linker_env = get_linker_and_args(ctx, "bin", toolchain, cc_toolchain, feature_configuration, None) + linker, _, _, link_args, linker_env = get_linker_and_args(ctx, "bin", toolchain, cc_toolchain, feature_configuration, None) env.update(**linker_env) env["LD"] = linker env["LDFLAGS"] = " ".join(_pwd_flags(link_args)) @@ -577,7 +577,7 @@ def _cargo_build_script_impl(ctx): args = ctx.actions.args() args.add(script, format = "--script=%s") args.add(links, format = "--links=%s") - args.add(out_dir.path, format = "--out_dir=%s") + args.add_all([out_dir], format_each = "--out_dir=%s", expand_directories = False) args.add(env_out, format = "--env_out=%s") args.add(flags_out, format = "--flags_out=%s") args.add(link_flags, format = "--link_flags=%s") @@ -649,6 +649,7 @@ def _cargo_build_script_impl(ctx): mnemonic = "CargoBuildScriptRun", progress_message = "Running Cargo build script {}".format(pkg_name), env = env, + execution_requirements = {"supports-path-mapping": "1"}, toolchain = None, use_default_shell_env = use_default_shell_env, ) diff --git a/extensions/bindgen/private/bindgen.bzl b/extensions/bindgen/private/bindgen.bzl index a79ed4bfda..8a50d6b192 100644 --- a/extensions/bindgen/private/bindgen.bzl +++ b/extensions/bindgen/private/bindgen.bzl @@ -369,7 +369,7 @@ def _rust_bindgen_impl(ctx): for define in ctx.attr.cc_lib[CcInfo].compilation_context.defines.to_list(): args.add("-D" + define) - _, _, _, linker_env = get_linker_and_args(ctx, "bin", rust_toolchain, cc_toolchain, feature_configuration, None) + _, _, _, _, linker_env = get_linker_and_args(ctx, "bin", rust_toolchain, cc_toolchain, feature_configuration, None) env.update(**linker_env) # Set the dynamic linker search path so that clang uses the libstdcxx from the toolchain. diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 34e19bd4d1..57e402159d 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -53,6 +53,14 @@ load( "relativize", ) +def _file_dirname(file): + """Returns the dirname of a file. + + Used as a ``map_each`` callback so that Bazel can apply path mapping + (``--experimental_output_paths=strip``) to the resulting string. + """ + return file.dirname + # This feature is disabled unless one of the dependencies is a cc_library. # Authors of C++ toolchains can place linker flags that should only be applied # when linking with C objects in a feature with this name, or require this @@ -419,6 +427,7 @@ def get_linker_and_args(ctx, crate_type, toolchain, cc_toolchain, feature_config Returns: tuple: A tuple of the following items: - (str): The tool path for given action. + - (File or None): The linker File object for path mapping, or None when the cc_toolchain provides the linker. - (bool): Whether or not the linker is a direct driver (e.g. `ld`) vs a wrapper (e.g. `gcc`). - (sequence): A flattened command line flags for given action. - (dict): Environment variables to be set for given action. @@ -426,6 +435,7 @@ def get_linker_and_args(ctx, crate_type, toolchain, cc_toolchain, feature_config user_link_flags = get_cc_user_link_flags(ctx) ld = None + ld_file = None # File object for path mapping, when available ld_is_direct_driver = False link_args = [] link_env = {} @@ -473,7 +483,8 @@ def get_linker_and_args(ctx, crate_type, toolchain, cc_toolchain, feature_config ld_is_direct_driver = False if not ld or toolchain.linker_preference == "rust": - ld = toolchain.linker.path + ld_file = toolchain.linker + ld = ld_file.path ld_is_direct_driver = toolchain.linker_type == "direct" # When using rust-lld directly, we still need library search paths from cc_toolchain @@ -539,7 +550,7 @@ def get_linker_and_args(ctx, crate_type, toolchain, cc_toolchain, feature_config for element in link_env["LIB"].split(";") ]) - return ld, ld_is_direct_driver, link_args, link_env + return ld, ld_file, ld_is_direct_driver, link_args, link_env def _symlink_for_ambiguous_lib(actions, toolchain, crate_info, lib): """Constructs a disambiguating symlink for a library dependency. @@ -727,7 +738,7 @@ def collect_inputs( Returns: tuple: A tuple: A tuple of the following items: - (list): A list of all build info `OUT_DIR` File objects - - (str): The `OUT_DIR` of the current build info + - (File): The `OUT_DIR` tree artifact of the current build info, or None - (File): An optional path to a generated environment file from a `cargo_build_script` target - (depset[File]): All direct and transitive build flag files from the current build info - (list[File]): Linkstamp outputs @@ -999,8 +1010,16 @@ def construct_arguments( components = "${{pwd}}/{}/{}".format(ctx.label.workspace_root, ctx.label.package).split("/") env["CARGO_MANIFEST_DIR"] = "/".join([c for c in components if c]) + # Pass OUT_DIR via --set-env so it goes through Bazel's path mapping + # (--experimental_output_paths=strip). The env dict is NOT path-mapped; + # only Args objects are. out_dir is a tree artifact (directory File). if out_dir != None: - env["OUT_DIR"] = "${pwd}/" + out_dir + process_wrapper_flags.add_all( + [out_dir], + before_each = "--set-env", + format_each = "OUT_DIR=${pwd}/%s", + expand_directories = False, + ) # Arguments for launching rustc from the process wrapper rustc_path = ctx.actions.args() @@ -1047,9 +1066,9 @@ def construct_arguments( # Configure process_wrapper to terminate rustc when metadata are emitted process_wrapper_flags.add("--rustc-quit-on-rmeta", "true") if crate_info.rustc_rmeta_output: - process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output.path) + process_wrapper_flags.add("--output-file", crate_info.rustc_rmeta_output) elif crate_info.rustc_output: - process_wrapper_flags.add("--output-file", crate_info.rustc_output.path) + process_wrapper_flags.add("--output-file", crate_info.rustc_output) rustc_flags.add(error_format, format = "--error-format=%s") @@ -1065,7 +1084,7 @@ def construct_arguments( rustc_flags.add(output_hash, format = "--codegen=extra-filename=-%s") if output_dir: - rustc_flags.add(output_dir, format = "--out-dir=%s") + rustc_flags.add_all([crate_info.output], map_each = _file_dirname, format_each = "--out-dir=%s") compilation_mode = get_compilation_mode_opts(ctx, toolchain) rustc_flags.add(compilation_mode.opt_level, format = "--codegen=opt-level=%s") @@ -1094,8 +1113,9 @@ def construct_arguments( if linker_script: rustc_flags.add(linker_script, format = "--codegen=link-arg=-T%s") - # Tell Rustc where to find the standard library (or libcore) - rustc_flags.add_all(toolchain.rust_std_paths, before_each = "-L", format_each = "%s") + # Tell Rustc where to find the standard library (or libcore). + # Use map_each for path mapping (--experimental_output_paths=strip). + rustc_flags.add_all(toolchain.rust_std_paths, map_each = _file_dirname, before_each = "-L", uniquify = True) rustc_flags.add_all(rust_flags, map_each = map_flag) # Gather data path from crate_info since it is inherited from real crate for rust_doc and rust_test @@ -1122,7 +1142,7 @@ def construct_arguments( else: rpaths = depset() - ld, ld_is_direct_driver, link_args, link_env = get_linker_and_args( + ld, ld_file, ld_is_direct_driver, link_args, link_env = get_linker_and_args( ctx, crate_info.type, toolchain, @@ -1133,7 +1153,7 @@ def construct_arguments( ) env.update(link_env) - rustc_flags.add(ld, format = "--codegen=linker=%s") + rustc_flags.add(ld_file if ld_file else ld, format = "--codegen=linker=%s") # Split link args into individual "--codegen=link-arg=" flags to handle nested spaces. # Additional context: https://github.com/rust-lang/rust/pull/36574 @@ -1524,6 +1544,7 @@ def rustc_compile_action( ), toolchain = "@rules_rust//rust:toolchain_type", resource_set = get_rustc_resource_set(toolchain), + execution_requirements = {"supports-path-mapping": "1"}, ) if args_metadata: ctx.actions.run( @@ -1541,6 +1562,7 @@ def rustc_compile_action( "" if len(srcs) == 1 else "s", ), toolchain = "@rules_rust//rust:toolchain_type", + execution_requirements = {"supports-path-mapping": "1"}, ) elif hasattr(ctx.executable, "_bootstrap_process_wrapper"): # Run without process_wrapper @@ -1562,6 +1584,7 @@ def rustc_compile_action( ), toolchain = "@rules_rust//rust:toolchain_type", resource_set = get_rustc_resource_set(toolchain), + execution_requirements = {"supports-path-mapping": "1"}, ) else: fail("No process wrapper was defined for {}".format(ctx.label)) @@ -2021,7 +2044,7 @@ def _process_build_scripts( # We include the direct dep build_info because crates which use cargo build scripts may need to e.g. include_str! a generated file. if build_info: if build_info.out_dir: - out_dir = build_info.out_dir.path + out_dir = build_info.out_dir direct_inputs.append(build_info.out_dir) build_env_file = build_info.rustc_env if build_info.flags: diff --git a/rust/toolchain.bzl b/rust/toolchain.bzl index f59f7ac853..9f904e8a5b 100644 --- a/rust/toolchain.bzl +++ b/rust/toolchain.bzl @@ -593,7 +593,7 @@ def _rust_toolchain_impl(ctx): make_variables = make_variable_info, rust_doc = sysroot.rustdoc, rust_std = sysroot.rust_std, - rust_std_paths = depset([file.dirname for file in sysroot.rust_std.to_list()]), + rust_std_paths = sysroot.rust_std, rustc = sysroot.rustc, rustc_lib = sysroot.rustc_lib, rustfmt = sysroot.rustfmt, diff --git a/util/process_wrapper/options.rs b/util/process_wrapper/options.rs index 2f252cadc7..0c04e1770d 100644 --- a/util/process_wrapper/options.rs +++ b/util/process_wrapper/options.rs @@ -66,6 +66,7 @@ pub(crate) fn options() -> Result { let mut output_file = None; let mut rustc_quit_on_rmeta_raw = None; let mut rustc_output_format_raw = None; + let mut set_env_raw = None; let mut flags = Flags::new(); let mut require_explicit_unstable_features = None; flags.define_repeated_flag("--subst", "", &mut subst_mapping_raw); @@ -121,6 +122,15 @@ pub(crate) fn options() -> Result { other -Zallow-features= is present in the rustc flags.", &mut require_explicit_unstable_features, ); + flags.define_repeated_flag( + "--set-env", + "Set a child environment variable from a KEY=VALUE pair. \ + Unlike the action env dict, values passed here go through \ + Bazel's path mapping (--experimental_output_paths=strip) \ + because they are conveyed via Args objects. \ + The ${pwd} substitution is applied to values.", + &mut set_env_raw, + ); let mut child_args = match flags .parse(env::args().collect()) @@ -157,7 +167,15 @@ pub(crate) fn options() -> Result { stable_status_file_raw.map_or_else(Vec::new, |s| read_stamp_status_to_array(s).unwrap()); let volatile_stamp_mappings = volatile_status_file_raw.map_or_else(Vec::new, |s| read_stamp_status_to_array(s).unwrap()); - let environment_file_block = env_from_files(env_file_raw.unwrap_or_default())?; + let mut environment_file_block = env_from_files(env_file_raw.unwrap_or_default())?; + // Merge --set-env KEY=VALUE pairs into the environment block. + // These arrive via Args objects and are path-mapped by Bazel. + for kv in set_env_raw.unwrap_or_default() { + let (key, val) = kv.split_once('=').ok_or_else(|| { + OptionError::Generic(format!("--set-env requires KEY=VALUE, got '{kv}'")) + })?; + environment_file_block.insert(key.to_owned(), val.to_owned()); + } let mut file_arguments = args_from_file(arg_file_raw.unwrap_or_default())?; // Process --copy-output let copy_output = copy_output_raw