From 9f91986e61620c46a68b2351be80a19f401856dd Mon Sep 17 00:00:00 2001 From: Titus Fortner Date: Wed, 9 Sep 2026 12:59:24 -0500 Subject: [PATCH] fix: use the toolchain repository as the rustc sysroot to shorten Windows paths --- rust/private/rustc.bzl | 13 ++++-- rust/private/toolchain.bzl | 77 ++++++++++++++++++++++--------- test/toolchain/toolchain_test.bzl | 21 +++++++++ 3 files changed, 84 insertions(+), 27 deletions(-) diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index b74b046b25..23a72a73c8 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -1641,13 +1641,12 @@ def construct_arguments( {}, )) - # Ensure the sysroot is set for the target platform. Compute the dirname - # from the underlying `sysroot_anchor` `File` via `map_each` so Bazel's - # path mapping can rewrite it. + # Ensure the sysroot is set for the target platform. Derive it from the + # `rustc` `File` via `map_each` so Bazel's path mapping can rewrite it. if toolchain._toolchain_generated_sysroot: rustc_flags.add_all( - [toolchain.sysroot_anchor], - map_each = _get_dirname, + [toolchain.rustc], + map_each = _get_sysroot_dir, format_each = "--sysroot=%s", ) @@ -3633,6 +3632,10 @@ def _get_crate_root_path(args): else: return file.path +def _get_sysroot_dir(rustc): + """Return the sysroot containing `rustc`, which always sits at `/bin/rustc`.""" + return rustc.dirname.rpartition("/")[0] + def _get_dirname(file): """A helper function for `_add_native_link_flags`. diff --git a/rust/private/toolchain.bzl b/rust/private/toolchain.bzl index 10bdedcba1..f89c2009d6 100644 --- a/rust/private/toolchain.bzl +++ b/rust/private/toolchain.bzl @@ -135,7 +135,8 @@ def _symlink_sysroot_tree(ctx, name, target, target_files = None): Args: ctx (ctx): The toolchain's context object - name (str): The name of the sysroot directory (typically `ctx.label.name`) + name (str): The name of the sysroot directory (typically `ctx.label.name`), + or None to use the files in place. target (Target): A target owning files to symlink target_files (depset): An optional depset to use in place of `target.files`. @@ -145,6 +146,8 @@ def _symlink_sysroot_tree(ctx, name, target, target_files = None): tree_files = [] if target_files == None: target_files = target.files + if name == None: + return target_files for file in target_files.to_list(): # Parse the path to the file relative to the workspace root so a # symlink matching this path can be created within the sysroot. @@ -172,13 +175,15 @@ def _symlink_sysroot_bin(ctx, name, directory, target): Args: ctx (ctx): The rule's context object - name (str): A common name for the output directory + name (str): A common name for the output directory, or None to use `target` in place directory (str): The directory under `name` to put the file in target (File): A File object to symlink to Returns: File: A newly generated symlink file """ + if name == None: + return target symlink = ctx.actions.declare_file("{}/{}/{}".format( name, directory, @@ -199,6 +204,24 @@ def _default_runfiles_files(target): return target[DefaultInfo].default_runfiles.files +def _sysroot_of_rustc(path): + """Return the sysroot directory for a `/bin/rustc` path.""" + return path.rpartition("/")[0].rpartition("/")[0] + +def _is_source_sysroot(rustc, files): + """Whether `rustc` and `files` already form a sysroot tree of source files. + + Downloaded toolchain repositories unpack every component into one tree rustc accepts as a + sysroot directly. Using it in place keeps sysroot paths short enough for Windows. + """ + if not rustc.is_source or not rustc.dirname.endswith("/bin"): + return False + prefix = _sysroot_of_rustc(rustc.path) + "/" + for file in files: + if not file.is_source or not file.path.startswith(prefix): + return False + return True + def _generate_sysroot( ctx, rustc, @@ -237,9 +260,16 @@ def _generate_sysroot( rust_objcopy (File, optional): The path to a `rust-objcopy` executable. Returns: - struct: A struct of generated files representing the new sysroot + struct: A struct of files representing the sysroot, with `path` and `short_path` + naming its root directory. """ name = ctx.label.name + if _is_source_sysroot( + rustc, + [f for f in [rustdoc, clippy, cargo, cargo_clippy, rustfmt, rust_objcopy] if f] + + depset(transitive = [t.files for t in [rustc_lib, llvm_tools, rust_std, linker] if t] + [r for r in [_default_runfiles_files(linker)] if r]).to_list(), + ): + name = None # Define runfiles direct_files = [] @@ -343,22 +373,24 @@ def _generate_sysroot( transitive_file_sets.append(depset(ctx.files.rust_std)) # Declare a file in the root of the sysroot to make locating the sysroot easy - sysroot_anchor = ctx.actions.declare_file("{}/rust.sysroot".format(name)) - ctx.actions.write( - output = sysroot_anchor, - content = "\n".join([ - "cargo: {}".format(cargo), - "clippy: {}".format(clippy), - "cargo-clippy: {}".format(cargo_clippy), - "linker: {}".format(linker), - "llvm_tools: {}".format(llvm_tools), - "rust_std: {}".format(rust_std), - "rustc_lib: {}".format(rustc_lib), - "rustc: {}".format(rustc), - "rustdoc: {}".format(rustdoc), - "rustfmt: {}".format(rustfmt), - ]), - ) + sysroot_anchor = None + if name: + sysroot_anchor = ctx.actions.declare_file("{}/rust.sysroot".format(name)) + ctx.actions.write( + output = sysroot_anchor, + content = "\n".join([ + "cargo: {}".format(cargo), + "clippy: {}".format(clippy), + "cargo-clippy: {}".format(cargo_clippy), + "linker: {}".format(linker), + "llvm_tools: {}".format(llvm_tools), + "rust_std: {}".format(rust_std), + "rustc_lib: {}".format(rustc_lib), + "rustc: {}".format(rustc), + "rustdoc: {}".format(rustdoc), + "rustfmt: {}".format(rustfmt), + ]), + ) # Create a depset of all sysroot files (symlinks and their real paths) all_files = depset(direct_files, transitive = transitive_file_sets) @@ -369,11 +401,13 @@ def _generate_sysroot( cargo_clippy = sysroot_cargo_clippy, clippy = sysroot_clippy, linker = sysroot_linker, + path = _sysroot_of_rustc(sysroot_rustc.path), rust_std = sysroot_rust_std, rustc = sysroot_rustc, rustc_lib = sysroot_rustc_lib, rustdoc = sysroot_rustdoc, rustfmt = sysroot_rustfmt, + short_path = _sysroot_of_rustc(sysroot_rustc.short_path), sysroot_anchor = sysroot_anchor, ) @@ -476,9 +510,8 @@ def _rust_toolchain_impl(ctx): rust_objcopy = ctx.file.rust_objcopy, ) - # Determine the path and short_path of the sysroot - sysroot_path = sysroot.sysroot_anchor.dirname - sysroot_short_path, _, _ = sysroot.sysroot_anchor.short_path.rpartition("/") + sysroot_path = sysroot.path + sysroot_short_path = sysroot.short_path # Variables for make variable expansion make_variables = { diff --git a/test/toolchain/toolchain_test.bzl b/test/toolchain/toolchain_test.bzl index 8dda2f79aa..23d4b5ab1c 100644 --- a/test/toolchain/toolchain_test.bzl +++ b/test/toolchain/toolchain_test.bzl @@ -227,6 +227,21 @@ rust_stdlib_filegroup_provides_runfiles_test = analysistest.make( _rust_stdlib_filegroup_provides_runfiles_test_impl, ) +def _toolchain_uses_repository_sysroot_impl(ctx): + """Asserts a downloaded toolchain is used as the sysroot in place, not re-created under bazel-out.""" + env = analysistest.begin(ctx) + rustc_actions = [action for action in analysistest.target_actions(env) if action.mnemonic == "Rustc"] + asserts.true(env, len(rustc_actions) > 0, "No Rustc action found") + + for action in rustc_actions: + sysroots = [arg for arg in action.argv if arg.startswith("--sysroot=")] + asserts.equals(env, 1, len(sysroots), "Expected one --sysroot flag in: {}".format(action.argv)) + asserts.false(env, sysroots[0].startswith("--sysroot=bazel-out/"), "Sysroot was re-created under bazel-out: {}".format(sysroots[0])) + + return analysistest.end(env) + +toolchain_uses_repository_sysroot_test = analysistest.make(_toolchain_uses_repository_sysroot_impl) + def toolchain_test_suite(name): """ Instantiates tests for rust toolchains. @@ -250,11 +265,17 @@ def toolchain_test_suite(name): target_under_test = ":std_libs", ) + toolchain_uses_repository_sysroot_test( + name = "toolchain_uses_repository_sysroot_test", + target_under_test = ":lib", + ) + native.test_suite( name = name, tests = [ ":toolchain_adds_rustc_flags_lib_test", ":toolchain_adds_rustc_flags_shared_lib_test", ":rust_stdlib_filegroup_provides_runfiles_test", + ":toolchain_uses_repository_sysroot_test", ], )