From bb318034fe739f4a7765b9f92c9608db00afac85 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 13 Feb 2026 09:25:50 +0800 Subject: [PATCH 1/4] test(host-config): runner incorrectly apply to normal builds Add tests documenting that `host.runner`incorrectly apply to normal target builds. The test will be fixed in the next commit. --- tests/testsuite/build_script.rs | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index befd6496893..4a47e3ea8da 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -963,6 +963,55 @@ Caused by: .run(); } +#[cargo_test] +fn host_runner_does_not_apply_to_cargo_run() { + // `host.runner` should only wrap build scripts, not `cargo run`. + let target = rustc_host(); + let p = project() + .file( + ".cargo/config.toml", + r#" + [host] + runner = "nonexistent-host-runner" + "#, + ) + .file("src/main.rs", "fn main() { println!(\"hello\"); }") + .build(); + + // with --target + p.cargo("run -Z target-applies-to-host -Z host-config --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[RUNNING] `target/[HOST_TARGET]/debug/foo[EXE]` + +"#]]) + .with_stdout_data(str![[r#" +hello + +"#]]) + .run(); + + // without --target + p.cargo("run -Z target-applies-to-host -Z host-config") + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[RUNNING] `nonexistent-host-runner target/debug/foo[EXE]` +[ERROR] could not execute process `nonexistent-host-runner target/debug/foo` (never executed) + +Caused by: + [NOT_FOUND] + +"#]]) + .with_stdout_data(str![""]) + .run(); +} + #[cargo_test] fn target_runner_does_not_apply_to_build_script() { // Regression test for https://github.com/rust-lang/miri/issues/4855 From f6bb5ab43f4cf6843e36195c288df82d16d38b0d Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 13 Feb 2026 14:01:15 +0800 Subject: [PATCH 2/4] test: ensure in host mode Cargo picks up target config host mode means without any `--target` cross-compilation settings --- tests/testsuite/tool_paths.rs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/testsuite/tool_paths.rs b/tests/testsuite/tool_paths.rs index 0849f6375c6..9dd8b2ba7ba 100644 --- a/tests/testsuite/tool_paths.rs +++ b/tests/testsuite/tool_paths.rs @@ -405,6 +405,39 @@ fn custom_runner_env_true() { .run(); } +#[cargo_test] +fn custom_runner_target_applies_to_host() { + // This ensures that without `--target` (non-cross-target mode) + // Cargo still respects targe configuration. + let target = rustc_host(); + + let p = project() + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + &format!( + r#" + [target.{}] + runner = "nonexistent-runner -r" + "#, + target + ), + ) + .build(); + + // FIXME: Target configuration should be applied also for target-applies-to-host + p.cargo("run -Z target-applies-to-host") + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .env("CARGO_TARGET_APPLIES_TO_HOST", "false") + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s +[RUNNING] `target/debug/foo[EXE]` + +"#]]) + .run(); +} + #[cargo_test] fn custom_linker_env() { let p = project().file("src/main.rs", "fn main() {}").build(); From f5ae2fc7b28a873c0be2348b741a45a4e7938257 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 13 Feb 2026 09:55:14 +0800 Subject: [PATCH 3/4] fix(host-config): `host.runner` should not apply to `cargo run` `host.runner` was incorrectly applied to `cargo run` and other target processes when `-Zhost-config` was enabled but no `--target` flag was specified. The root cause was that `target_runner()` used `target_config(kind)` which routes through `host_config` for `CompileKind::Host`. But `CompileKind::Host` is used for both build scripts and normal binaries when no `--target` is specified. --- src/cargo/core/compiler/compilation.rs | 47 ++++++++++++++++++++------ tests/testsuite/build_script.rs | 10 +++--- tests/testsuite/tool_paths.rs | 8 +++-- 3 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index f94d750a18f..8b894ab6c8f 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -9,6 +9,7 @@ use cargo_util::{ProcessBuilder, paths}; use crate::core::Package; use crate::core::compiler::BuildContext; +use crate::core::compiler::CompileTarget; use crate::core::compiler::RustdocFingerprint; use crate::core::compiler::apply_env_config; use crate::core::compiler::{CompileKind, Unit, UnitHash}; @@ -140,6 +141,22 @@ impl<'gctx> Compilation<'gctx> { let rustc_process = bcx.rustc().process(); let primary_rustc_process = bcx.build_config.primary_unit_rustc.clone(); let rustc_workspace_wrapper_process = bcx.rustc().workspace_process(); + let host = bcx.host_triple().to_string(); + let mut target_runners = bcx + .build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .map(|kind| Ok((*kind, target_runner(bcx, *kind)?))) + .collect::>>()?; + if !bcx.gctx.target_applies_to_host()? { + // When `target-applies-to-host=false`, and without `--target`, + // there will be only `CompileKind::Host` in requested_kinds. + // Need to insert target config explicitly for target-applies-to-host=false + // to find the correct configs. + let kind = explicit_host_kind(&host); + target_runners.insert(kind, target_runner(bcx, kind)?); + } Ok(Compilation { native_dirs: BTreeSet::new(), root_output: HashMap::new(), @@ -153,17 +170,11 @@ impl<'gctx> Compilation<'gctx> { to_doc_test: Vec::new(), rustdoc_fingerprints: None, gctx: bcx.gctx, - host: bcx.host_triple().to_string(), + host, rustc_process, rustc_workspace_wrapper_process, primary_rustc_process, - target_runners: bcx - .build_config - .requested_kinds - .iter() - .chain(Some(&CompileKind::Host)) - .map(|kind| Ok((*kind, target_runner(bcx, *kind)?))) - .collect::>>()?, + target_runners, target_linkers: bcx .build_config .requested_kinds @@ -238,7 +249,10 @@ impl<'gctx> Compilation<'gctx> { // Only use host runner when -Zhost-config is enabled // to ensure `target..runner` does not wrap build scripts. let builder = if !self.gctx.target_applies_to_host()? - && let Some((runner, args)) = self.target_runner(CompileKind::Host) + && let Some((runner, args)) = self + .target_runners + .get(&CompileKind::Host) + .and_then(|x| x.as_ref()) { let mut builder = ProcessBuilder::new(runner); builder.args(args); @@ -251,6 +265,14 @@ impl<'gctx> Compilation<'gctx> { } pub fn target_runner(&self, kind: CompileKind) -> Option<&(PathBuf, Vec)> { + let target_applies_to_host = self.gctx.target_applies_to_host().unwrap_or(true); + let kind = if !target_applies_to_host && kind.is_host() { + // Use explicit host target triple when `target-applies-to-host=false` + // This ensures `host.runner` won't be accidentally applied to `cargo run` / `cargo test`. + explicit_host_kind(&self.host) + } else { + kind + }; self.target_runners.get(&kind).and_then(|x| x.as_ref()) } @@ -446,8 +468,6 @@ fn target_runner( bcx: &BuildContext<'_, '_>, kind: CompileKind, ) -> CargoResult)>> { - // Try host.runner / target.{}.runner via target_config, which routes - // through host_config for CompileKind::Host when -Zhost-config is enabled. if let Some(runner) = bcx.target_data.target_config(kind).runner.as_ref() { let path = runner.val.path.clone().resolve_program(bcx.gctx); return Ok(Some((path, runner.val.args.clone()))); @@ -516,3 +536,8 @@ fn target_linker(bcx: &BuildContext<'_, '_>, kind: CompileKind) -> CargoResult CompileKind { + let target = CompileTarget::new(host, false).expect("must be a host tuple"); + CompileKind::Target(target) +} diff --git a/tests/testsuite/build_script.rs b/tests/testsuite/build_script.rs index 4a47e3ea8da..5f19e8ca179 100644 --- a/tests/testsuite/build_script.rs +++ b/tests/testsuite/build_script.rs @@ -997,18 +997,16 @@ hello // without --target p.cargo("run -Z target-applies-to-host -Z host-config") .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) - .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s -[RUNNING] `nonexistent-host-runner target/debug/foo[EXE]` -[ERROR] could not execute process `nonexistent-host-runner target/debug/foo` (never executed) +[RUNNING] `target/debug/foo[EXE]` -Caused by: - [NOT_FOUND] +"#]]) + .with_stdout_data(str![[r#" +hello "#]]) - .with_stdout_data(str![""]) .run(); } diff --git a/tests/testsuite/tool_paths.rs b/tests/testsuite/tool_paths.rs index 9dd8b2ba7ba..9da5027d3df 100644 --- a/tests/testsuite/tool_paths.rs +++ b/tests/testsuite/tool_paths.rs @@ -425,14 +425,18 @@ fn custom_runner_target_applies_to_host() { ) .build(); - // FIXME: Target configuration should be applied also for target-applies-to-host p.cargo("run -Z target-applies-to-host") .masquerade_as_nightly_cargo(&["target-applies-to-host"]) .env("CARGO_TARGET_APPLIES_TO_HOST", "false") + .with_status(101) .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s -[RUNNING] `target/debug/foo[EXE]` +[RUNNING] `nonexistent-runner -r target/debug/foo[EXE]` +[ERROR] could not execute process `nonexistent-runner -r target/debug/foo[EXE]` (never executed) + +Caused by: + [NOT_FOUND] "#]]) .run(); From c3ac327ec0c189da66a190e90665b946140a5413 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Fri, 13 Feb 2026 14:51:52 +0800 Subject: [PATCH 4/4] refactor: target_runners to runners As it covers both host and target runners. --- src/cargo/core/compiler/compilation.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 8b894ab6c8f..c533c5b7390 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -128,7 +128,8 @@ pub struct Compilation<'gctx> { /// `rustc_workspace_wrapper_process` primary_rustc_process: Option, - target_runners: HashMap)>>, + /// The runner to use for each host or target process. + runners: HashMap)>>, /// The linker to use for each host or target. target_linkers: HashMap>, @@ -142,7 +143,7 @@ impl<'gctx> Compilation<'gctx> { let primary_rustc_process = bcx.build_config.primary_unit_rustc.clone(); let rustc_workspace_wrapper_process = bcx.rustc().workspace_process(); let host = bcx.host_triple().to_string(); - let mut target_runners = bcx + let mut runners = bcx .build_config .requested_kinds .iter() @@ -155,7 +156,7 @@ impl<'gctx> Compilation<'gctx> { // Need to insert target config explicitly for target-applies-to-host=false // to find the correct configs. let kind = explicit_host_kind(&host); - target_runners.insert(kind, target_runner(bcx, kind)?); + runners.insert(kind, target_runner(bcx, kind)?); } Ok(Compilation { native_dirs: BTreeSet::new(), @@ -174,7 +175,7 @@ impl<'gctx> Compilation<'gctx> { rustc_process, rustc_workspace_wrapper_process, primary_rustc_process, - target_runners, + runners, target_linkers: bcx .build_config .requested_kinds @@ -250,7 +251,7 @@ impl<'gctx> Compilation<'gctx> { // to ensure `target..runner` does not wrap build scripts. let builder = if !self.gctx.target_applies_to_host()? && let Some((runner, args)) = self - .target_runners + .runners .get(&CompileKind::Host) .and_then(|x| x.as_ref()) { @@ -273,7 +274,7 @@ impl<'gctx> Compilation<'gctx> { } else { kind }; - self.target_runners.get(&kind).and_then(|x| x.as_ref()) + self.runners.get(&kind).and_then(|x| x.as_ref()) } /// Gets the user-specified linker for a particular host or target.