diff --git a/src/cargo/core/compiler/build_context/mod.rs b/src/cargo/core/compiler/build_context/mod.rs index f35084e2b..19dee718b 100644 --- a/src/cargo/core/compiler/build_context/mod.rs +++ b/src/cargo/core/compiler/build_context/mod.rs @@ -10,7 +10,6 @@ use crate::util::errors::CargoResult; use crate::util::interning::InternedString; use crate::util::Rustc; use std::collections::{HashMap, HashSet}; -use std::path::PathBuf; mod target_info; pub use self::target_info::{ @@ -120,15 +119,6 @@ impl<'a, 'cfg> BuildContext<'a, 'cfg> { &self.target_data.rustc } - /// Gets the user-specified linker for a particular host or target. - pub fn linker(&self, kind: CompileKind) -> Option { - self.target_data - .target_config(kind) - .linker - .as_ref() - .map(|l| l.val.clone().resolve_program(self.config)) - } - /// Gets the host architecture triple. /// /// For example, x86_64-unknown-linux-gnu, would be diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index b263119b0..e008bd0e6 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -104,6 +104,8 @@ pub struct Compilation<'cfg> { primary_rustc_process: Option, target_runners: HashMap)>>, + /// The linker to use for each host or target. + target_linkers: HashMap>, } impl<'cfg> Compilation<'cfg> { @@ -150,6 +152,13 @@ impl<'cfg> Compilation<'cfg> { .chain(Some(&CompileKind::Host)) .map(|kind| Ok((*kind, target_runner(bcx, *kind)?))) .collect::>>()?, + target_linkers: bcx + .build_config + .requested_kinds + .iter() + .chain(Some(&CompileKind::Host)) + .map(|kind| Ok((*kind, target_linker(bcx, *kind)?))) + .collect::>>()?, }) } @@ -221,6 +230,11 @@ impl<'cfg> Compilation<'cfg> { self.target_runners.get(&kind).and_then(|x| x.as_ref()) } + /// 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()) + } + /// Returns a [`ProcessBuilder`] appropriate for running a process for the /// target platform. This is typically used for `cargo run` and `cargo /// test`. @@ -441,3 +455,39 @@ fn target_runner( ) })) } + +/// Gets the user-specified linker for a particular host or target from the configuration. +fn target_linker(bcx: &BuildContext<'_, '_>, kind: CompileKind) -> CargoResult> { + // Try host.linker and target.{}.linker. + if let Some(path) = bcx + .target_data + .target_config(kind) + .linker + .as_ref() + .map(|l| l.val.clone().resolve_program(bcx.config)) + { + return Ok(Some(path)); + } + + // Try target.'cfg(...)'.linker. + let target_cfg = bcx.target_data.info(kind).cfg(); + let mut cfgs = bcx + .config + .target_cfgs()? + .iter() + .filter_map(|(key, cfg)| cfg.linker.as_ref().map(|linker| (key, linker))) + .filter(|(key, _linker)| CfgExpr::matches_key(key, target_cfg)); + let matching_linker = cfgs.next(); + if let Some((key, linker)) = cfgs.next() { + anyhow::bail!( + "several matching instances of `target.'cfg(..)'.linker` in configurations\n\ + first match `{}` located in {}\n\ + second match `{}` located in {}", + matching_linker.unwrap().0, + matching_linker.unwrap().1.definition, + key, + linker.definition + ); + } + Ok(matching_linker.map(|(_k, linker)| linker.val.clone().resolve_program(bcx.config))) +} diff --git a/src/cargo/core/compiler/context/mod.rs b/src/cargo/core/compiler/context/mod.rs index 3f13f086c..010fe2793 100644 --- a/src/cargo/core/compiler/context/mod.rs +++ b/src/cargo/core/compiler/context/mod.rs @@ -272,7 +272,7 @@ impl<'a, 'cfg> Context<'a, 'cfg> { unit: unit.clone(), args, unstable_opts, - linker: self.bcx.linker(unit.kind), + linker: self.compilation.target_linker(unit.kind).clone(), script_meta, env: artifact::get_env(&self, self.unit_deps(unit))?, }); diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 85306aaac..ca6c29c3d 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -299,11 +299,8 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult { cmd.env(&var, value); } - if let Some(linker) = &bcx.target_data.target_config(unit.kind).linker { - cmd.env( - "RUSTC_LINKER", - linker.val.clone().resolve_program(bcx.config), - ); + if let Some(linker) = &cx.compilation.target_linker(unit.kind) { + cmd.env("RUSTC_LINKER", linker); } if let Some(links) = unit.pkg.manifest().links() { diff --git a/src/cargo/core/compiler/fingerprint/mod.rs b/src/cargo/core/compiler/fingerprint/mod.rs index 2e6fb7eed..1a2dfe4ee 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -1426,7 +1426,7 @@ fn calculate_normal(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult, cmd: &mut ProcessBuilder, unit: &Unit) cmd, "-C", "linker=", - bcx.linker(unit.kind).as_ref().map(|s| s.as_ref()), + cx.compilation + .target_linker(unit.kind) + .as_ref() + .map(|s| s.as_ref()), ); if incremental { let dir = cx.files().layout(unit.kind).incremental().as_os_str(); diff --git a/src/cargo/util/config/target.rs b/src/cargo/util/config/target.rs index b8aaf906d..6d6a6beff 100644 --- a/src/cargo/util/config/target.rs +++ b/src/cargo/util/config/target.rs @@ -12,6 +12,7 @@ use std::path::PathBuf; pub struct TargetCfgConfig { pub runner: OptValue, pub rustflags: OptValue, + pub linker: OptValue, // This is here just to ignore fields from normal `TargetConfig` because // all `[target]` tables are getting deserialized, whether they start with // `cfg(` or not. diff --git a/tests/testsuite/tool_paths.rs b/tests/testsuite/tool_paths.rs index a211b5328..8ddd358c6 100644 --- a/tests/testsuite/tool_paths.rs +++ b/tests/testsuite/tool_paths.rs @@ -393,7 +393,6 @@ fn cfg_ignored_fields() { [WARNING] unused key `ar` in [target] config table `cfg(not(target_os = \"none\"))` [WARNING] unused key `foo` in [target] config table `cfg(not(target_os = \"none\"))` [WARNING] unused key `invalid` in [target] config table `cfg(not(target_os = \"none\"))` -[WARNING] unused key `linker` in [target] config table `cfg(not(target_os = \"none\"))` [CHECKING] foo v0.0.1 ([..]) [FINISHED] [..] ",