diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index 7f0786652e6..aced829b1b5 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -10,7 +10,7 @@ use crate::core::compiler::apply_env_config; use crate::core::compiler::{BuildRunner, CompileKind, CompileMode, CompileTarget, CrateType}; use crate::core::{Dependency, Package, Target, TargetKind, Workspace}; -use crate::util::context::{GlobalContext, StringList, TargetConfig}; +use crate::util::context::{GlobalContext, TargetConfig}; use crate::util::interning::InternedString; use crate::util::{CargoResult, Rustc}; use anyhow::Context as _; @@ -20,10 +20,12 @@ use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::collections::hash_map::{Entry, HashMap}; use std::path::{Path, PathBuf}; +use std::rc::Rc; use std::str::{self, FromStr}; use std::sync::Arc; -/// Information about the platform target gleaned from querying rustc. +/// Information about the platform target gleaned from querying rustc and from +/// merging configs. /// /// [`RustcTargetData`] keeps several of these, one for the host and the others /// for other specified targets. If no target is specified, it uses a clone from @@ -54,6 +56,8 @@ pub struct TargetInfo { pub rustflags: Arc<[String]>, /// Extra flags to pass to `rustdoc`, see [`extra_args`]. pub rustdocflags: Arc<[String]>, + /// Linker to use. If the value is `None` it is left up to rustc. + pub linker: Option>, /// Whether or not rustc (stably) supports the `--check-cfg` flag. /// /// Can be removed once the minimum supported rustc version of Cargo is @@ -157,9 +161,16 @@ impl TargetInfo { requested_kinds: &[CompileKind], rustc: &Rustc, kind: CompileKind, + target_config: &TargetConfig, ) -> CargoResult { - let mut rustflags = - extra_args(gctx, requested_kinds, &rustc.host, None, kind, Flags::Rust)?; + let mut rustflags = extra_args( + gctx, + requested_kinds, + None, + target_config, + kind, + Flags::Rust, + )?; let mut turn = 0; loop { let extra_fingerprint = kind.fingerprint_hash(); @@ -281,8 +292,8 @@ impl TargetInfo { let new_flags = extra_args( gctx, requested_kinds, - &rustc.host, Some(&cfg), + target_config, kind, Flags::Rust, )?; @@ -315,12 +326,13 @@ impl TargetInfo { rustdocflags: extra_args( gctx, requested_kinds, - &rustc.host, Some(&cfg), + target_config, kind, Flags::Rustdoc, )? .into(), + linker: target_linker(gctx, &cfg, target_config)?.map(Rc::new), cfg, support_split_debuginfo, support_check_cfg, @@ -668,13 +680,6 @@ enum Flags { } impl Flags { - fn as_key(self) -> &'static str { - match self { - Flags::Rust => "rustflags", - Flags::Rustdoc => "rustdocflags", - } - } - fn as_env(self) -> &'static str { match self { Flags::Rust => "RUSTFLAGS", @@ -711,8 +716,8 @@ impl Flags { fn extra_args( gctx: &GlobalContext, requested_kinds: &[CompileKind], - host_triple: &str, target_cfg: Option<&[Cfg]>, + target_config: &TargetConfig, kind: CompileKind, flags: Flags, ) -> CargoResult> { @@ -732,7 +737,7 @@ fn extra_args( // --target. Or, phrased differently, no `--target` behaves the same as `--target // `, and host artifacts are always "special" (they don't pick up `RUSTFLAGS` for // example). - return Ok(rustflags_from_host(gctx, flags, host_triple)?.unwrap_or_else(Vec::new)); + return Ok(rustflags_from_host(target_config, flags)?.unwrap_or_else(Vec::new)); } } @@ -742,9 +747,7 @@ fn extra_args( if let Some(rustflags) = rustflags_from_env(gctx, flags) { Ok(rustflags) - } else if let Some(rustflags) = - rustflags_from_target(gctx, host_triple, target_cfg, kind, flags)? - { + } else if let Some(rustflags) = rustflags_from_target(gctx, target_cfg, target_config, flags)? { Ok(rustflags) } else if let Some(rustflags) = rustflags_from_build(gctx, flags)? { Ok(rustflags) @@ -783,21 +786,18 @@ fn rustflags_from_env(gctx: &GlobalContext, flags: Flags) -> Option> /// See [`extra_args`] for more. fn rustflags_from_target( gctx: &GlobalContext, - host_triple: &str, target_cfg: Option<&[Cfg]>, - kind: CompileKind, + target_config: &TargetConfig, flag: Flags, ) -> CargoResult>> { let mut rustflags = Vec::new(); - // Then the target.*.rustflags value... - let target = match &kind { - CompileKind::Host => host_triple, - CompileKind::Target(target) => target.short_name(), + let config_flags = match flag { + Flags::Rust => &target_config.rustflags, + Flags::Rustdoc => &target_config.rustdocflags, }; - let key = format!("target.{}.{}", target, flag.as_key()); - if let Some(args) = gctx.get::>(&key)? { - rustflags.extend(args.as_slice().iter().cloned()); + if let Some(args) = config_flags { + rustflags.extend(args.val.as_slice().iter().cloned()); } // ...including target.'cfg(...)'.rustflags if let Some(target_cfg) = target_cfg { @@ -826,16 +826,50 @@ fn rustflags_from_target( } } +/// Gets the user-specified linker for a particular host or target from the configuration. +fn target_linker( + gctx: &GlobalContext, + target_cfg: &[Cfg], + target_config: &TargetConfig, +) -> CargoResult> { + // Try host.linker and target.{}.linker. + if let Some(path) = target_config + .linker + .as_ref() + .map(|l| l.val.clone().resolve_program(gctx)) + { + return Ok(Some(path)); + } + + // Try target.'cfg(...)'.linker. + let mut cfgs = gctx + .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(gctx))) +} + /// Gets compiler flags from `[host]` section in the config. /// See [`extra_args`] for more. fn rustflags_from_host( - gctx: &GlobalContext, + target_config: &TargetConfig, flag: Flags, - host_triple: &str, ) -> CargoResult>> { - let target_cfg = gctx.host_cfg_triple(host_triple)?; let list = match flag { - Flags::Rust => &target_cfg.rustflags, + Flags::Rust => &target_config.rustflags, Flags::Rustdoc => { // host.rustdocflags is not a thing, since it does not make sense return Ok(None); @@ -893,14 +927,25 @@ impl<'gctx> RustcTargetData<'gctx> { let mut target_info = HashMap::new(); let target_applies_to_host = gctx.target_applies_to_host()?; let host_target = CompileTarget::new(&rustc.host)?; - let host_info = TargetInfo::new(gctx, requested_kinds, &rustc, CompileKind::Host)?; - // This config is used for link overrides and choosing a linker. let host_config = if target_applies_to_host { - gctx.target_cfg_triple(&rustc.host)? + let mut config = gctx.target_cfg_triple(&rustc.host)?; + if requested_kinds != [CompileKind::Host] { + // When an explicit target flag is passed rustflags are ignored for host artifacts. + config.rustflags = None; + config.rustdocflags = None; + } + config } else { gctx.host_cfg_triple(&rustc.host)? }; + let host_info = TargetInfo::new( + gctx, + requested_kinds, + &rustc, + CompileKind::Host, + &host_config, + )?; // This is a hack. The unit_dependency graph builder "pretends" that // `CompileKind::Host` is `CompileKind::Target(host)` if the @@ -908,7 +953,7 @@ impl<'gctx> RustcTargetData<'gctx> { // needs access to the target config data, create a copy so that it // can be found. See `rebuild_unit_graph_shared` for why this is done. if requested_kinds.iter().any(CompileKind::is_host) { - target_config.insert(host_target, gctx.target_cfg_triple(&rustc.host)?); + let host_target_config = gctx.target_cfg_triple(&rustc.host)?; // If target_applies_to_host is true, the host_info is the target info, // otherwise we need to build target info for the target. @@ -920,9 +965,12 @@ impl<'gctx> RustcTargetData<'gctx> { requested_kinds, &rustc, CompileKind::Target(host_target), + &host_target_config, )?; target_info.insert(host_target, host_target_info); } + + target_config.insert(host_target, host_target_config); }; let mut res = RustcTargetData { @@ -969,14 +1017,20 @@ impl<'gctx> RustcTargetData<'gctx> { /// Insert `kind` into our `target_info` and `target_config` members if it isn't present yet. pub fn merge_compile_kind(&mut self, kind: CompileKind) -> CargoResult<()> { if let CompileKind::Target(target) = kind { - if !self.target_config.contains_key(&target) { - self.target_config - .insert(target, self.gctx.target_cfg_triple(target.short_name())?); - } + let target_config: &TargetConfig = match self.target_config.entry(target) { + Entry::Occupied(o) => o.into_mut(), + Entry::Vacant(v) => v.insert(self.gctx.target_cfg_triple(target.short_name())?), + }; if !self.target_info.contains_key(&target) { self.target_info.insert( target, - TargetInfo::new(self.gctx, &self.requested_kinds, &self.rustc, kind)?, + TargetInfo::new( + self.gctx, + &self.requested_kinds, + &self.rustc, + kind, + target_config, + )?, ); } } diff --git a/src/cargo/core/compiler/build_runner/mod.rs b/src/cargo/core/compiler/build_runner/mod.rs index 47c8219e450..eb6586a924d 100644 --- a/src/cargo/core/compiler/build_runner/mod.rs +++ b/src/cargo/core/compiler/build_runner/mod.rs @@ -284,7 +284,6 @@ impl<'a, 'gctx> BuildRunner<'a, 'gctx> { unit: unit.clone(), args, unstable_opts, - 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/compilation.rs b/src/cargo/core/compiler/compilation.rs index 405b17b9884..9f34152f571 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -40,8 +40,6 @@ pub struct Doctest { pub args: Vec, /// Whether or not -Zunstable-options is needed. pub unstable_opts: bool, - /// The -Clinker value to use. - pub linker: Option, /// The script metadata, if this unit's package has a build script. /// /// This is used for indexing [`Compilation::extra_env`]. @@ -120,8 +118,6 @@ pub struct Compilation<'gctx> { primary_rustc_process: Option, target_runners: HashMap)>>, - /// The linker to use for each host or target. - target_linkers: HashMap>, } impl<'gctx> Compilation<'gctx> { @@ -162,13 +158,6 @@ impl<'gctx> Compilation<'gctx> { .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::>>()?, }) } @@ -240,11 +229,6 @@ impl<'gctx> Compilation<'gctx> { 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`. @@ -484,39 +468,3 @@ 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.gctx)) - { - return Ok(Some(path)); - } - - // Try target.'cfg(...)'.linker. - let target_cfg = bcx.target_data.info(kind).cfg(); - let mut cfgs = bcx - .gctx - .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.gctx))) -} diff --git a/src/cargo/core/compiler/custom_build.rs b/src/cargo/core/compiler/custom_build.rs index 98ceeb5ad73..f657ba5c530 100644 --- a/src/cargo/core/compiler/custom_build.rs +++ b/src/cargo/core/compiler/custom_build.rs @@ -300,8 +300,8 @@ fn build_work(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResul cmd.env(&var, value); } - if let Some(linker) = &build_runner.compilation.target_linker(unit.kind) { - cmd.env("RUSTC_LINKER", linker); + if let Some(linker) = &unit.linker { + cmd.env("RUSTC_LINKER", linker.as_ref()); } 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 6ffebd753cd..cc3f5371cc7 100644 --- a/src/cargo/core/compiler/fingerprint/mod.rs +++ b/src/cargo/core/compiler/fingerprint/mod.rs @@ -1454,8 +1454,8 @@ fn calculate_normal( let m = unit.pkg.manifest().metadata(); let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository)); let mut config = StableHasher::new(); - if let Some(linker) = build_runner.compilation.target_linker(unit.kind) { - linker.hash(&mut config); + if let Some(linker) = &unit.linker { + linker.as_ref().hash(&mut config); } if unit.mode.is_doc() && build_runner.bcx.gctx.cli_unstable().rustdoc_map { if let Ok(map) = build_runner.bcx.gctx.doc_extern_map() { diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs index 70dfb3085d3..10fb0647c94 100644 --- a/src/cargo/core/compiler/mod.rs +++ b/src/cargo/core/compiler/mod.rs @@ -1156,11 +1156,7 @@ fn build_base_args( cmd, "-C", "linker=", - build_runner - .compilation - .target_linker(unit.kind) - .as_ref() - .map(|s| s.as_ref()), + unit.linker.as_ref().map(|s| s.as_ref().as_ref()), ); if incremental { let dir = build_runner diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 8b24cf7cdcb..31375d499de 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -220,6 +220,7 @@ pub fn generate_std_roots( target_data.info(*kind).rustflags.clone(), target_data.info(*kind).rustdocflags.clone(), target_data.target_config(*kind).links_overrides.clone(), + target_data.info(*kind).linker.clone(), /*is_std*/ true, /*dep_hash*/ 0, IsArtifact::No, diff --git a/src/cargo/core/compiler/unit.rs b/src/cargo/core/compiler/unit.rs index a26b92574e7..e2517b65485 100644 --- a/src/cargo/core/compiler/unit.rs +++ b/src/cargo/core/compiler/unit.rs @@ -13,6 +13,7 @@ use std::collections::{BTreeMap, HashSet}; use std::fmt; use std::hash::{Hash, Hasher}; use std::ops::Deref; +use std::path::PathBuf; use std::rc::Rc; use std::sync::Arc; @@ -90,6 +91,8 @@ pub struct UnitInner { /// running its build script and instead use the given output from the /// config file. pub links_overrides: Rc>, + /// Linker (if any) to pass to `rustc`, if not specified rustc chooses a default. + pub linker: Option>, // if `true`, the dependency is an artifact dependency, requiring special handling when // calculating output directories, linkage and environment variables provided to builds. pub artifact: IsArtifact, @@ -185,6 +188,7 @@ impl fmt::Debug for Unit { .field("rustflags", &self.rustflags) .field("rustdocflags", &self.rustdocflags) .field("links_overrides", &self.links_overrides) + .field("linker", &self.linker) .field("artifact", &self.artifact.is_true()) .field( "artifact_target_for_features", @@ -235,6 +239,7 @@ impl UnitInterner { rustflags: Arc<[String]>, rustdocflags: Arc<[String]>, links_overrides: Rc>, + linker: Option>, is_std: bool, dep_hash: u64, artifact: IsArtifact, @@ -271,6 +276,7 @@ impl UnitInterner { rustflags, rustdocflags, links_overrides, + linker, is_std, dep_hash, artifact, diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 59e356fc682..eeba8a9fa95 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -862,6 +862,7 @@ fn new_unit_dep_with_profile( .target_config(kind) .links_overrides .clone(), + state.target_data.info(kind).linker.clone(), state.is_std, /*dep_hash*/ 0, artifact.map_or(IsArtifact::No, |_| IsArtifact::Yes), diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index 77f6266355f..34935144695 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -178,7 +178,17 @@ pub fn print<'a>( if index != 0 { drop_println!(gctx); } - let target_info = TargetInfo::new(gctx, &build_config.requested_kinds, &rustc, *kind)?; + let target_config = match kind { + CompileKind::Host => gctx.target_cfg_triple(&rustc.host), + CompileKind::Target(target) => gctx.target_cfg_triple(target.short_name()), + }?; + let target_info = TargetInfo::new( + gctx, + &build_config.requested_kinds, + &rustc, + *kind, + &target_config, + )?; let mut process = rustc.process(); process.args(&target_info.rustflags); if let Some(args) = target_rustc_args { @@ -699,6 +709,7 @@ fn traverse_and_share( unit.rustflags.clone(), unit.rustdocflags.clone(), unit.links_overrides.clone(), + unit.linker.clone(), unit.is_std, unit.dep_hash, unit.artifact, @@ -727,6 +738,7 @@ fn traverse_and_share( unit.rustflags.clone(), unit.rustdocflags.clone(), unit.links_overrides.clone(), + unit.linker.clone(), unit.is_std, new_dep_hash, unit.artifact, @@ -891,6 +903,7 @@ fn override_rustc_crate_types( unit.rustflags.clone(), unit.rustdocflags.clone(), unit.links_overrides.clone(), + unit.linker.clone(), unit.is_std, unit.dep_hash, unit.artifact, diff --git a/src/cargo/ops/cargo_compile/unit_generator.rs b/src/cargo/ops/cargo_compile/unit_generator.rs index ce10e173c6c..4f6c1905c2c 100644 --- a/src/cargo/ops/cargo_compile/unit_generator.rs +++ b/src/cargo/ops/cargo_compile/unit_generator.rs @@ -174,6 +174,7 @@ impl<'a> UnitGenerator<'a, '_> { self.target_data.info(kind).rustflags.clone(), self.target_data.info(kind).rustdocflags.clone(), self.target_data.target_config(kind).links_overrides.clone(), + self.target_data.info(kind).linker.clone(), /*is_std*/ false, /*dep_hash*/ 0, IsArtifact::No, diff --git a/src/cargo/ops/cargo_test.rs b/src/cargo/ops/cargo_test.rs index 86e830e76f0..b422133507f 100644 --- a/src/cargo/ops/cargo_test.rs +++ b/src/cargo/ops/cargo_test.rs @@ -182,7 +182,6 @@ fn run_doc_tests( args, unstable_opts, unit, - linker, script_meta, env, } = doctest_info; @@ -236,9 +235,9 @@ fn run_doc_tests( p.arg("--runtool-arg").arg(arg); } } - if let Some(linker) = linker { + if let Some(linker) = &unit.linker { let mut joined = OsString::from("linker="); - joined.push(linker); + joined.push(linker.as_ref()); p.arg("-C").arg(joined); } } diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index f431fd9b5d5..9c787f86037 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -21,7 +21,8 @@ fn bad1() { p.cargo("check -v --target=nonexistent-target") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] expected table for configuration key `target.nonexistent-target`, but found string in [ROOT]/foo/.cargo/config.toml +[ERROR] invalid configuration for key `target.nonexistent-target` +expected a table, but found a string for `target.nonexistent-target` in [ROOT]/foo/.cargo/config.toml "#]]) .run(); diff --git a/tests/testsuite/tool_paths.rs b/tests/testsuite/tool_paths.rs index b733d6f9b1d..e49f75444b3 100644 --- a/tests/testsuite/tool_paths.rs +++ b/tests/testsuite/tool_paths.rs @@ -1,7 +1,9 @@ //! Tests for configuration values that point to programs. use cargo_test_support::prelude::*; -use cargo_test_support::{basic_lib_manifest, project, rustc_host, rustc_host_env, str}; +use cargo_test_support::{ + basic_bin_manifest, basic_lib_manifest, project, rustc_host, rustc_host_env, str, +}; #[cargo_test] fn pathless_tools() { @@ -485,3 +487,39 @@ fn cfg_ignored_fields() { "#]]) .run(); } + +#[cargo_test] +fn custom_linker_target_applies_to_host() { + let target = rustc_host(); + + let foo = project() + .file("Cargo.toml", &basic_bin_manifest("foo")) + .file("src/main.rs", "fn main() {}") + .file( + ".cargo/config.toml", + &format!( + r#" + [target.{}] + linker = "nonexistent-linker" + "#, + target + ), + ) + .build(); + + // Since we're building a binary this would error if + // linker was being passed to cargo. + foo.cargo("build --verbose") + .masquerade_as_nightly_cargo(&["target-applies-to-host"]) + .arg("-Ztarget-applies-to-host") + .env("CARGO_TARGET_APPLIES_TO_HOST", "false") + .with_status(101) + .with_stderr_data( + "\ +[COMPILING] foo v0.5.0 ([ROOT]/foo) +[RUNNING] `rustc [..] -C linker=nonexistent-linker [..] +[ERROR] linker `nonexistent-linker` not found +...", + ) + .run(); +}