From 561f0a18dbe0141df9005ef1e15b729860b2ec1a Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Mon, 27 Apr 2026 22:28:48 +0900 Subject: [PATCH 1/6] chore: Enable Cargo's new build-dir layout in boostrap --- src/bootstrap/src/core/build_steps/compile.rs | 21 ++++++++++++------- src/bootstrap/src/core/build_steps/tool.rs | 21 +++++++++++++++++-- src/bootstrap/src/core/builder/cargo.rs | 2 ++ src/ci/github-actions/jobs.yml | 7 ++++++- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 68a4f928464f1..3dbf4a576203a 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -2662,8 +2662,8 @@ pub fn run_cargo( ) -> Vec { // `target_root_dir` looks like $dir/$target/release let target_root_dir = stamp.path().parent().unwrap(); - // `target_deps_dir` looks like $dir/$target/release/deps - let target_deps_dir = target_root_dir.join("deps"); + // `target_build_dir` looks like $dir/$target/release/build + let target_build_dir = target_root_dir.join("build"); // `host_root_dir` looks like $dir/release let host_root_dir = target_root_dir .parent() @@ -2734,7 +2734,7 @@ pub fn run_cargo( // If this was output in the `deps` dir then this is a precise file // name (hash included) so we start tracking it. - if filename.starts_with(&target_deps_dir) { + if filename.starts_with(&target_build_dir) { deps.push((filename.to_path_buf(), DependencyType::Target)); continue; } @@ -2769,11 +2769,18 @@ pub fn run_cargo( // Ok now we need to actually find all the files listed in `toplevel`. We've // got a list of prefix/extensions and we basically just need to find the - // most recent file in the `deps` folder corresponding to each one. - let contents = target_deps_dir + // most recent file in the `build` folder corresponding to each one. + // + // Cargo's build folder is structured as `build///out/` so + // we need to traverse multiple directory layers to get to actual files. + let read_dir = |path: &Path| path.read_dir().ok().into_iter().flatten().filter_map(Result::ok); + let contents = target_build_dir .read_dir() - .unwrap_or_else(|e| panic!("Couldn't read {}: {}", target_deps_dir.display(), e)) - .map(|e| t!(e)) + .unwrap_or_else(|e| panic!("Couldn't read {}: {}", target_build_dir.display(), e)) + .map(|e| e.unwrap()) + .flat_map(|e| read_dir(&e.path())) + .flat_map(|e| read_dir(&e.path())) + .flat_map(|e| read_dir(&e.path())) .map(|e| (e.path(), e.file_name().into_string().unwrap(), t!(e.metadata()))) .collect::>(); for (prefix, extension, expected_len) in toplevel { diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index ed5c2586a5ed6..bf570a6a37ba1 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -10,7 +10,7 @@ //! return `ToolBuildResult` and should never prepare `cargo` invocations manually. use std::ffi::OsStr; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::{env, fs}; use crate::core::build_steps::compile::is_lto_stage; @@ -1623,7 +1623,7 @@ impl Builder<'_> { // Notably this munges the dynamic library lookup path to point to the // right location to run `compiler`. let mut lib_paths: Vec = - vec![self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("deps")]; + discover_out_dirs(self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("build")); // On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make // mode) and that C compiler may need some extra PATH modification. Do @@ -1651,3 +1651,20 @@ impl Builder<'_> { cmd } } + +/// Gets all of the `out` dirs in a given Cargo `build-dir//build` dir. +fn discover_out_dirs(dir: PathBuf) -> Vec { + if !dir.exists() { + return Vec::new(); + } + + let read_dir = |path: &Path| path.read_dir().ok().into_iter().flatten().filter_map(Result::ok); + dir.read_dir() + .unwrap_or_else(|e| panic!("Couldn't read {}: {}", dir.display(), e)) + .map(|e| e.unwrap()) + .flat_map(|e| read_dir(&e.path())) + .flat_map(|e| read_dir(&e.path())) + .map(|e| e.path()) + .filter(|path| path.ends_with("out")) + .collect::>() +} diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs index 8247e8739f35c..328802d47a393 100644 --- a/src/bootstrap/src/core/builder/cargo.rs +++ b/src/bootstrap/src/core/builder/cargo.rs @@ -600,6 +600,8 @@ impl Builder<'_> { let mut hostflags = HostFlags::default(); + cargo.env("CARGO_UNSTABLE_BUILD_DIR_NEW_LAYOUT", "true"); + // Codegen backends are not yet tracked by -Zbinary-dep-depinfo, // so we need to explicitly clear out if they've been updated. for backend in self.codegen_backends(compiler) { diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 97ddde799a757..51af86472ff99 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -2,7 +2,12 @@ # dynamically in CI from ci.yml. runners: - &base-job - env: { } + env: + # This force enables the new Cargo build-dir layout for all CI jobs. + # The new layout will become the default soon so we enable it on everything + # to catch issues as soon as possible. + # See: https://github.com/rust-lang/cargo/issues/15010 + CARGO_UNSTABLE_BUILD_DIR_NEW_LAYOUT: true - &job-linux-4c os: ubuntu-24.04 From 5bcf216442a0a22d8c14618d831c22b85fce05e4 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Thu, 30 Apr 2026 01:05:08 +0900 Subject: [PATCH 2/6] test: Enable Cargo's new layout in tests --- src/tools/compiletest/src/runtest/run_make.rs | 33 +++++++++++++++---- src/tools/miri/test-cargo-miri/run-test.py | 2 +- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs index 1044683ae6426..7c56fd345e5ff 100644 --- a/src/tools/compiletest/src/runtest/run_make.rs +++ b/src/tools/compiletest/src/runtest/run_make.rs @@ -1,3 +1,4 @@ +use std::path::{Path, PathBuf}; use std::process::{Command, Output, Stdio}; use std::{env, fs}; @@ -66,8 +67,8 @@ impl TestCx<'_> { // build// // ├── bootstrap-tools/ // │ ├── /release/librun_make_support.rlib // <- support rlib itself - // │ ├── /release/deps/ // <- deps - // │ └── release/deps/ // <- deps of deps + // │ ├── /release/build///out // <- deps + // │ └── release/build///out // <- deps of deps // ``` // // FIXME(jieyouxu): there almost certainly is a better way to do this (specifically how the @@ -77,8 +78,8 @@ impl TestCx<'_> { let support_host_path = tools_bin.join(&self.config.host).join("release"); let support_lib_path = support_host_path.join("librun_make_support.rlib"); - let support_lib_deps = support_host_path.join("deps"); - let support_lib_deps_deps = tools_bin.join("release").join("deps"); + let support_lib_deps = discover_out_dirs(support_host_path.join("build")); + let support_lib_deps_deps = discover_out_dirs(tools_bin.join("release").join("build")); // To compile the recipe with rustc, we need to provide suitable dynamic library search // paths to rustc. This includes both: @@ -100,6 +101,10 @@ impl TestCx<'_> { p }; + let out_dirs_to_args = |paths: Vec| { + paths.into_iter().map(|p| format!("-Ldependency={}", p.display())).collect::>() + }; + // run-make-support and run-make tests are compiled using the stage0 compiler // If the stage is 0, then the compiler that we test (either bootstrap or an explicitly // set compiler) is the one that actually compiled run-make-support. @@ -119,8 +124,8 @@ impl TestCx<'_> { .arg(&recipe_bin) // Specify library search paths for `run_make_support`. .arg(format!("-Ldependency={}", &support_lib_path.parent().unwrap())) - .arg(format!("-Ldependency={}", &support_lib_deps)) - .arg(format!("-Ldependency={}", &support_lib_deps_deps)) + .args(out_dirs_to_args(support_lib_deps)) + .args(out_dirs_to_args(support_lib_deps_deps)) // Provide `run_make_support` as extern prelude, so test writers don't need to write // `extern run_make_support;`. .arg("--extern") @@ -337,3 +342,19 @@ impl TestCx<'_> { } } } + +/// Gets all of the `out` dirs in a given Cargo `build-dir//build` dir. +fn discover_out_dirs(dir: Utf8PathBuf) -> Vec { + let read_dir = |path: &Path| path.read_dir().ok().into_iter().flatten().filter_map(Result::ok); + let contents = dir + .read_dir() + .unwrap_or_else(|e| panic!("Couldn't read {}: {}", dir, e)) + .map(|e| e.unwrap()) + .flat_map(|e| read_dir(&e.path())) + .flat_map(|e| read_dir(&e.path())) + .map(|e| e.path()) + .filter(|path| path.ends_with("out")) + .collect::>(); + + return contents; +} diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py index 6b3b6343b8258..bf1272f5bca0b 100755 --- a/src/tools/miri/test-cargo-miri/run-test.py +++ b/src/tools/miri/test-cargo-miri/run-test.py @@ -215,7 +215,7 @@ def test_cargo_miri_multi_target(): if os.listdir(target_dir) != ["miri"]: fail(f"`{target_dir}` contains unexpected files") # Ensure something exists inside that target dir. - os.access(os.path.join(target_dir, "miri", "debug", "deps"), os.F_OK) + os.access(os.path.join(target_dir, "miri", "debug"), os.F_OK) print("\nTEST SUCCESSFUL!") sys.exit(0) From 8103292b44b37e4f406a066aaa8dbca4c1b454e6 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Thu, 30 Apr 2026 01:17:54 +0900 Subject: [PATCH 3/6] test: Add support for cargo's build-dir v2 layout (rustfmt) --- src/tools/rustfmt/src/test/mod.rs | 19 +++++++++++++++++-- src/tools/rustfmt/tests/cargo-fmt/main.rs | 9 +++++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/tools/rustfmt/src/test/mod.rs b/src/tools/rustfmt/src/test/mod.rs index 36e6aa84fc2d5..4eded7c49eb50 100644 --- a/src/tools/rustfmt/src/test/mod.rs +++ b/src/tools/rustfmt/src/test/mod.rs @@ -1077,8 +1077,23 @@ fn rustfmt() -> PathBuf { let mut me = env::current_exe().expect("failed to get current executable"); // Chop of the test name. me.pop(); - // Chop off `deps`. - me.pop(); + + // Handle Cargo's old and new filesystem layouts + // * v1: `target//deps/test-bin-[HASH][EXE]` + // * v2: `target//build//[HASH]/out/test-bin-[HASH][EXE]` + if me.ends_with("deps") { + // Chop off `deps`. + me.pop(); + } else if me.ends_with("out") { + // Chop off `out`. + me.pop(); + // Chop off ``. + me.pop(); + // Chop off ``. + me.pop(); + // Chop off `build`. + me.pop(); + } me.push("rustfmt"); assert!( diff --git a/src/tools/rustfmt/tests/cargo-fmt/main.rs b/src/tools/rustfmt/tests/cargo-fmt/main.rs index dcdbca07a77c9..63cc12521a8f7 100644 --- a/src/tools/rustfmt/tests/cargo-fmt/main.rs +++ b/src/tools/rustfmt/tests/cargo-fmt/main.rs @@ -10,8 +10,17 @@ use rustfmt_config_proc_macro::rustfmt_only_ci_test; fn cargo_fmt(args: &[&str]) -> (String, String) { let mut bin_dir = env::current_exe().unwrap(); bin_dir.pop(); // chop off test exe name + + // Handle Cargo's old and new filesystem layouts + // * v1: `target//deps/test-bin-[HASH][EXE]` + // * v2: `target//build//[HASH]/out/test-bin-[HASH][EXE]` if bin_dir.ends_with("deps") { bin_dir.pop(); + } else if bin_dir.ends_with("out") { + bin_dir.pop(); // chop off `out` + bin_dir.pop(); // chop off `` + bin_dir.pop(); // chop off `` + bin_dir.pop(); // chop off `build` } let cmd = bin_dir.join(format!("cargo-fmt{}", env::consts::EXE_SUFFIX)); From 97866b2082dc4032c7bfe1f13e6d3455ab6d4866 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Thu, 30 Apr 2026 01:21:21 +0900 Subject: [PATCH 4/6] chore: Move CI to use Cargo's v2 layout (rustfmt) This also syncs `RUSTFLAGS: -D warnings` which is added in rust-lang/rustfmt --- src/tools/rustfmt/.github/workflows/linux.yml | 3 +++ src/tools/rustfmt/.github/workflows/mac.yml | 3 +++ src/tools/rustfmt/.github/workflows/windows.yml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/src/tools/rustfmt/.github/workflows/linux.yml b/src/tools/rustfmt/.github/workflows/linux.yml index 369b048f12fec..c8cbf00804bc3 100644 --- a/src/tools/rustfmt/.github/workflows/linux.yml +++ b/src/tools/rustfmt/.github/workflows/linux.yml @@ -36,4 +36,7 @@ jobs: rustup target add ${{ matrix.target }} - name: Build and Test + env: + RUSTFLAGS: -D warnings + CARGO_UNSTABLE_BUILD_DIR_NEW_LAYOUT: true run: ./ci/build_and_test.sh diff --git a/src/tools/rustfmt/.github/workflows/mac.yml b/src/tools/rustfmt/.github/workflows/mac.yml index b2e8401131b73..0838800dff06e 100644 --- a/src/tools/rustfmt/.github/workflows/mac.yml +++ b/src/tools/rustfmt/.github/workflows/mac.yml @@ -32,4 +32,7 @@ jobs: rustup target add ${{ matrix.target }} - name: Build and Test + env: + RUSTFLAGS: -D warnings + CARGO_UNSTABLE_BUILD_DIR_NEW_LAYOUT: true run: ./ci/build_and_test.sh diff --git a/src/tools/rustfmt/.github/workflows/windows.yml b/src/tools/rustfmt/.github/workflows/windows.yml index ef8d54fb93f57..af47fddcf59e1 100644 --- a/src/tools/rustfmt/.github/workflows/windows.yml +++ b/src/tools/rustfmt/.github/workflows/windows.yml @@ -59,4 +59,7 @@ jobs: - name: Build and Test shell: cmd + env: + RUSTFLAGS: -D warnings + CARGO_UNSTABLE_BUILD_DIR_NEW_LAYOUT: true run: ci\build_and_test.bat From 862d0a678a71439b4ffb90e20572c827de3c150d Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Wed, 27 May 2026 21:03:53 +0900 Subject: [PATCH 5/6] fix: Use arg files in bootstrap rustc and rustdoc --- src/bootstrap/Cargo.toml | 5 +- src/bootstrap/src/bin/rustc.rs | 9 +- src/bootstrap/src/bin/rustdoc.rs | 6 +- src/bootstrap/src/utils/shared_helpers.rs | 115 +++++++++++++++++++++- 4 files changed, 123 insertions(+), 12 deletions(-) diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml index 363802093a13a..2989b1247afb9 100644 --- a/src/bootstrap/Cargo.toml +++ b/src/bootstrap/Cargo.toml @@ -7,7 +7,7 @@ default-run = "bootstrap" [features] build-metrics = ["dep:sysinfo", "build_helper/metrics"] -tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:chrono", "dep:tempfile"] +tracing = ["dep:tracing", "dep:tracing-chrome", "dep:tracing-subscriber", "dep:chrono"] [lib] path = "src/lib.rs" @@ -55,6 +55,7 @@ termcolor = "1.4" toml = "0.5" walkdir = "2.4" xz2 = "0.1" +tempfile = "3.15.0" # Dependencies needed by the build-metrics feature sysinfo = { version = "0.39.0", default-features = false, optional = true, features = ["system"] } @@ -64,7 +65,6 @@ chrono = { version = "0.4", default-features = false, optional = true, features tracing = { version = "0.1", optional = true, features = ["attributes"] } tracing-chrome = { version = "0.7", optional = true } tracing-subscriber = { version = "0.3", optional = true, features = ["env-filter", "fmt", "registry", "std"] } -tempfile = { version = "3.15.0", optional = true } [target.'cfg(windows)'.dependencies.junction] version = "1.3.0" @@ -83,7 +83,6 @@ features = [ [dev-dependencies] pretty_assertions = "1.4" -tempfile = "3.15.0" insta = "1.43" # We care a lot about bootstrap's compile times, so don't include debuginfo for diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs index bb974e4645ea6..72c4b713c3ac9 100644 --- a/src/bootstrap/src/bin/rustc.rs +++ b/src/bootstrap/src/bin/rustc.rs @@ -21,8 +21,8 @@ use std::process::{Child, Command}; use std::time::Instant; use shared_helpers::{ - dylib_path, dylib_path_var, exe, maybe_dump, parse_rustc_stage, parse_rustc_verbose, - parse_value_from_args, + ArgFileCommand, dylib_path, dylib_path_var, exe, maybe_dump, parse_rustc_stage, + parse_rustc_verbose, parse_value_from_args, }; #[path = "../utils/shared_helpers.rs"] @@ -112,11 +112,11 @@ fn main() { let mut cmd = match env::var_os("RUSTC_WRAPPER_REAL") { Some(wrapper) if !wrapper.is_empty() => { - let mut cmd = Command::new(wrapper); + let mut cmd = ArgFileCommand::new(wrapper); cmd.arg(rustc_driver); cmd } - _ => Command::new(rustc_driver), + _ => ArgFileCommand::new(rustc_driver), }; cmd.args(&args).env(dylib_path_var(), env::join_paths(&dylib_path).unwrap()); @@ -271,6 +271,7 @@ fn main() { eprintln!("{prefix} libdir: {libdir:?}"); } + let (mut cmd, _arg_file) = cmd.build().unwrap(); maybe_dump(format!("stage{}-rustc", stage + 1), &cmd); let start = Instant::now(); diff --git a/src/bootstrap/src/bin/rustdoc.rs b/src/bootstrap/src/bin/rustdoc.rs index 2925892fed283..ea3c77e7051ce 100644 --- a/src/bootstrap/src/bin/rustdoc.rs +++ b/src/bootstrap/src/bin/rustdoc.rs @@ -4,10 +4,9 @@ use std::env; use std::path::PathBuf; -use std::process::Command; use shared_helpers::{ - dylib_path, dylib_path_var, maybe_dump, parse_rustc_stage, parse_rustc_verbose, + ArgFileCommand, dylib_path, dylib_path_var, maybe_dump, parse_rustc_stage, parse_rustc_verbose, parse_value_from_args, }; @@ -31,7 +30,7 @@ fn main() { let mut dylib_path = dylib_path(); dylib_path.insert(0, PathBuf::from(libdir.clone())); - let mut cmd = Command::new(rustdoc); + let mut cmd = ArgFileCommand::new(rustdoc); if target.is_some() { // The stage0 compiler has a special sysroot distinct from what we @@ -81,6 +80,7 @@ fn main() { } } + let (mut cmd, _arg_file) = cmd.build().unwrap(); maybe_dump(format!("stage{}-rustdoc", stage + 1), &cmd); if verbose > 1 { diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs index d620cc4bbb6b4..ced215665672d 100644 --- a/src/bootstrap/src/utils/shared_helpers.rs +++ b/src/bootstrap/src/utils/shared_helpers.rs @@ -14,12 +14,15 @@ #![allow(dead_code)] use std::env; -use std::ffi::OsString; +use std::ffi::{OsStr, OsString}; use std::fs::OpenOptions; use std::io::Write; -use std::process::Command; +use std::path::Path; +use std::process::{Command, CommandEnvs}; use std::str::FromStr; +use tempfile::NamedTempFile; + /// Returns the environment variable which the dynamic library lookup path /// resides in for this platform. pub fn dylib_path_var() -> &'static str { @@ -126,3 +129,111 @@ pub fn parse_value_from_args<'a>(args: &'a [OsString], key: &str) -> Option<&'a None } + +/// A wrapper around [`Command`] that adds support for arg files. +/// This is useful as we have some commands that can get very long and at times +/// hit the OS limit (usually Windows) +/// +/// This implementation is based off the of `ProcessBuilder` implementation in Cargo +/// but simplified. +/// +/// NOTE: In most scenarios we want to avoid arg files as it makes debugging more complicated +/// so we try to avoid it if the command is not close the the OS limit. +#[derive(Debug)] +pub struct ArgFileCommand { + command: Command, + args: Vec, +} + +impl ArgFileCommand { + #[track_caller] + pub fn new>(program: S) -> Self { + let command = Command::new(program); + Self { command, args: Vec::new() } + } + pub fn arg>(&mut self, arg: S) -> &mut Self { + self.args.push(arg.as_ref().to_os_string()); + self + } + + pub fn args(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + self.args.extend(args.into_iter().map(|s| s.as_ref().to_os_string())); + self + } + + pub fn env(&mut self, key: K, val: V) -> &mut Self + where + K: AsRef, + V: AsRef, + { + self.command.env(key, val); + self + } + + pub fn get_envs(&self) -> CommandEnvs<'_> { + self.command.get_envs() + } + + pub fn env_remove>(&mut self, key: K) -> &mut Self { + self.command.env_remove(key); + self + } + + pub fn current_dir>(&mut self, dir: P) -> &mut Self { + self.command.current_dir(dir); + self + } + + pub fn stdin(&mut self, stdin: std::process::Stdio) -> &mut Self { + self.command.stdin(stdin); + self + } + + pub fn build(mut self) -> std::io::Result<(Command, NamedTempFile)> { + let mut tmp = tempfile::Builder::new().prefix("bootstrap-argfile.").tempfile()?; + + // On Windows there is a hard limit of ~32KB, so we cut off at 30KB to + // give some buffer just incase. + #[cfg(windows)] + let threshold: usize = 30 * 1024; + // On unix the limit is defined by ARG_MAX. If its not explicitly set we set it to 1MB + // which is fairly large but lower than the ~2MB that it defaults to on most systems. + #[cfg(unix)] + let threshold: usize = + std::env::var("ARG_MAX").ok().and_then(|v| v.parse().ok()).unwrap_or(1024 * 1024); + + let total_arg_len: usize = self.args.iter().map(|a| a.len() + 1).sum(); + if total_arg_len <= threshold { + self.command.args(self.args); + return Ok((self.command, tmp)); + } + + let mut arg = OsString::from("@"); + arg.push(tmp.path()); + self.command.arg(arg); + + let cap = self.args.iter().map(|arg| arg.len() + 1).sum::(); + let mut buf = Vec::with_capacity(cap); + for arg in &self.args { + let arg = arg.to_str().ok_or_else(|| { + std::io::Error::other(format!( + "argument for argfile contains invalid UTF-8 characters: `{}`", + arg.to_string_lossy() + )) + })?; + if arg.contains('\n') { + return Err(std::io::Error::other(format!( + "argument for argfile contains newlines: `{arg}`" + ))); + } + writeln!(buf, "{arg}")?; + } + tmp.write_all(&buf)?; + + Ok((self.command, tmp)) + } +} From deab78d2ae869788acb6fe8e36386450f644e2e4 Mon Sep 17 00:00:00 2001 From: Ross Sullivan Date: Sun, 31 May 2026 16:48:34 +0900 Subject: [PATCH 6/6] fix: Filter out directories that do not contain dylibs This is an optimization to reduce the amount for paths that get added to the dylib search path. This is especially important for Windows as it has issues when PATH gets too long. With the new Cargo build-dir we increased the number of paths being passed. --- src/bootstrap/src/core/build_steps/tool.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs index bf570a6a37ba1..bc0ee1efa1250 100644 --- a/src/bootstrap/src/core/build_steps/tool.rs +++ b/src/bootstrap/src/core/build_steps/tool.rs @@ -1622,8 +1622,9 @@ impl Builder<'_> { // // Notably this munges the dynamic library lookup path to point to the // right location to run `compiler`. - let mut lib_paths: Vec = - discover_out_dirs(self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("build")); + let mut lib_paths: Vec = discover_out_dirs_with_dylibs( + self.cargo_out(compiler, Mode::ToolBootstrap, *host).join("build"), + ); // On MSVC a tool may invoke a C compiler (e.g., compiletest in run-make // mode) and that C compiler may need some extra PATH modification. Do @@ -1653,18 +1654,21 @@ impl Builder<'_> { } /// Gets all of the `out` dirs in a given Cargo `build-dir//build` dir. -fn discover_out_dirs(dir: PathBuf) -> Vec { +fn discover_out_dirs_with_dylibs(dir: PathBuf) -> Vec { if !dir.exists() { return Vec::new(); } - let read_dir = |path: &Path| path.read_dir().ok().into_iter().flatten().filter_map(Result::ok); + let has_dylib = |path: &Path| { + read_dir(path) + .any(|e| e.path().extension().is_some_and(|ext| ext == std::env::consts::DLL_EXTENSION)) + }; dir.read_dir() .unwrap_or_else(|e| panic!("Couldn't read {}: {}", dir.display(), e)) .map(|e| e.unwrap()) .flat_map(|e| read_dir(&e.path())) .flat_map(|e| read_dir(&e.path())) .map(|e| e.path()) - .filter(|path| path.ends_with("out")) + .filter(|path| path.ends_with("out") && has_dylib(path)) .collect::>() }