diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index dd06f63b24e..957fecf55eb 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -282,7 +282,10 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { unit: unit.clone(), args, unstable_opts, - linker: self.compilation.target_linker(unit.kind).clone(), + linker: self + .compilation + .target_linker(unit.kind) + .map(|p| p.to_path_buf()), script_metas, env: artifact::get_env(&self, unit, self.unit_deps(unit))?, }); diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index c533c5b7390..4c5fbbc4c95 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeSet, HashMap}; use std::ffi::{OsStr, OsString}; +use std::path::Path; use std::path::PathBuf; use cargo_platform::CfgExpr; @@ -131,7 +132,7 @@ pub struct Compilation<'gctx> { /// The runner to use for each host or target process. runners: HashMap)>>, /// The linker to use for each host or target. - target_linkers: HashMap>, + linkers: HashMap>, /// The total number of lint warnings emitted by the compilation. pub lint_warning_count: usize, @@ -158,6 +159,19 @@ impl<'gctx> Compilation<'gctx> { let kind = explicit_host_kind(&host); runners.insert(kind, target_runner(bcx, kind)?); } + + let mut linkers = bcx + .build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .map(|kind| Ok((*kind, target_linker(bcx, *kind)?))) + .collect::>>()?; + if !bcx.gctx.target_applies_to_host()? { + // See above reason in runner why we do this. + let kind = explicit_host_kind(&host); + linkers.insert(kind, target_linker(bcx, kind)?); + } Ok(Compilation { native_dirs: BTreeSet::new(), root_output: HashMap::new(), @@ -176,13 +190,7 @@ impl<'gctx> Compilation<'gctx> { rustc_workspace_wrapper_process, primary_rustc_process, runners, - target_linkers: bcx - .build_config - .requested_kinds - .iter() - .chain(Some(&CompileKind::Host)) - .map(|kind| Ok((*kind, target_linker(bcx, *kind)?))) - .collect::>>()?, + linkers, lint_warning_count: 0, }) } @@ -277,9 +285,28 @@ impl<'gctx> Compilation<'gctx> { self.runners.get(&kind).and_then(|x| x.as_ref()) } + /// Gets the `[host.linker]` for host build target (build scripts and proc macros). + pub fn host_linker(&self) -> Option<&Path> { + self.linkers + .get(&CompileKind::Host) + .and_then(|x| x.as_ref()) + .map(|x| x.as_path()) + } + /// Gets the user-specified linker for a particular host or target. - pub fn target_linker(&self, kind: CompileKind) -> Option { - self.target_linkers.get(&kind).and_then(|x| x.clone()) + pub fn target_linker(&self, kind: CompileKind) -> Option<&Path> { + 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.linker` won't be accidentally applied to normal builds + explicit_host_kind(&self.host) + } else { + kind + }; + self.linkers + .get(&kind) + .and_then(|x| x.as_ref()) + .map(|x| x.as_path()) } /// Returns a [`ProcessBuilder`] appropriate for running a process for the diff --git a/src/cargo/core/compiler/fingerprint/mod.rs b/src/cargo/core/compiler/fingerprint/mod.rs index 02d7360218e..2b803c45e48 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -1635,7 +1635,12 @@ fn calculate_normal( unit.pkg.manifest().lint_rustflags(), )); let mut config = StableHasher::new(); - if let Some(linker) = build_runner.compilation.target_linker(unit.kind) { + let linker = if unit.target.for_host() && !build_runner.bcx.gctx.target_applies_to_host()? { + build_runner.compilation.host_linker() + } else { + build_runner.compilation.target_linker(unit.kind) + }; + if let Some(linker) = linker { linker.hash(&mut config); } if unit.mode.is_doc() && build_runner.bcx.gctx.cli_unstable().rustdoc_map { diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 57d2cd072b3..014a2781c79 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -1404,29 +1404,12 @@ fn build_base_args( cmd.arg("--out-dir") .arg(&build_runner.files().output_dir(unit)); - fn opt(cmd: &mut ProcessBuilder, key: &str, prefix: &str, val: Option<&OsStr>) { - if let Some(val) = val { - let mut joined = OsString::from(prefix); - joined.push(val); - cmd.arg(key).arg(joined); - } - } - unit.kind.add_target_arg(cmd); - opt( - cmd, - "-C", - "linker=", - build_runner - .compilation - .target_linker(unit.kind) - .as_ref() - .map(|s| s.as_ref()), - ); + add_codegen_linker(cmd, build_runner, unit, bcx.gctx.target_applies_to_host()?); + if incremental { - let dir = build_runner.files().incremental_dir(&unit); - opt(cmd, "-C", "incremental=", Some(dir.as_os_str())); + add_codegen_incremental(cmd, build_runner, unit) } let pkg_hint_mostly_unused = match hints.mostly_unused { @@ -1962,6 +1945,44 @@ pub fn extern_args( Ok(result) } +/// Adds `-C linker=` if specified. +fn add_codegen_linker( + cmd: &mut ProcessBuilder, + build_runner: &BuildRunner<'_, '_>, + unit: &Unit, + target_applies_to_host: bool, +) { + let linker = if unit.target.for_host() && !target_applies_to_host { + build_runner + .compilation + .host_linker() + .map(|s| s.as_os_str()) + } else { + build_runner + .compilation + .target_linker(unit.kind) + .map(|s| s.as_os_str()) + }; + + if let Some(linker) = linker { + let mut arg = OsString::from("linker="); + arg.push(linker); + cmd.arg("-C").arg(arg); + } +} + +/// Adds `-C incremental=`. +fn add_codegen_incremental( + cmd: &mut ProcessBuilder, + build_runner: &BuildRunner<'_, '_>, + unit: &Unit, +) { + let dir = build_runner.files().incremental_dir(&unit); + let mut arg = OsString::from("incremental="); + arg.push(dir.as_os_str()); + cmd.arg("-C").arg(arg); +} + fn envify(s: &str) -> String { s.chars() .flat_map(|c| c.to_uppercase()) diff --git a/tests/testsuite/cross_compile.rs b/tests/testsuite/cross_compile.rs index 40c46480508..896ef26e11b 100644 --- a/tests/testsuite/cross_compile.rs +++ b/tests/testsuite/cross_compile.rs @@ -1299,3 +1299,56 @@ fn always_emit_warnings_as_warnings_when_learning_target_info() { "#]]) .run(); } + +#[cargo_test] +fn host_linker_does_not_apply_to_binary_build() { + // `host.linker` should only apply to build scripts, not to normal binary builds. + let target = rustc_host(); + let p = project() + .file( + ".cargo/config.toml", + &format!( + r#" + [host] + linker = "nonexistent-host-linker" + [target.{target}] + linker = "nonexistent-target-linker" + "#, + ), + ) + .file("src/main.rs", "fn main() {}") + .build(); + + p.cargo("build -Z target-applies-to-host -Z host-config --target") + .arg(&target) + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + // Need to omit some MSVC-specific diagnostics + // because rustc prints extra stuff when linker was not found. + // https://github.com/rust-lang/rust/blob/7ad4e69ad585d8ff214f7b42d01f1959eda08f40/compiler/rustc_codegen_ssa/src/back/link.rs?plain=1#L971-L975 + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[ERROR] linker `nonexistent-target-linker` not found + | + = [NOTE] [NOT_FOUND] +... +"#]]) + .run(); + + // with target-applies-to-host=false, + // host.linker should not be applied but target.linker + p.cargo("build -Z target-applies-to-host -Z host-config") + .masquerade_as_nightly_cargo(&["target-applies-to-host", "host-config"]) + .with_status(101) + // Need to omit some MSVC-specific diagnostics + // because rustc prints extra stuff when linker was not found. + // https://github.com/rust-lang/rust/blob/7ad4e69ad585d8ff214f7b42d01f1959eda08f40/compiler/rustc_codegen_ssa/src/back/link.rs?plain=1#L971-L975 + .with_stderr_data(str![[r#" +[COMPILING] foo v0.0.1 ([ROOT]/foo) +[ERROR] linker `nonexistent-target-linker` not found + | + = [NOTE] [NOT_FOUND] +... +"#]]) + .run(); +}