1use super::{ConfigKey, ConfigRelativePath, GlobalContext, OptValue, PathAndArgs, StringList, CV};
2use crate::core::compiler::{BuildOutput, LinkArgTarget};
3use crate::util::CargoResult;
4use serde::Deserialize;
5use std::collections::{BTreeMap, HashMap};
6use std::path::{Path, PathBuf};
7use std::rc::Rc;
8
9#[derive(Debug, Deserialize)]
13pub struct TargetCfgConfig {
14 pub runner: OptValue<PathAndArgs>,
15 pub rustflags: OptValue<StringList>,
16 pub linker: OptValue<ConfigRelativePath>,
17 #[serde(flatten)]
21 pub other: BTreeMap<String, toml::Value>,
22}
23
24#[derive(Debug, Clone, Default)]
26pub struct TargetConfig {
27 pub runner: OptValue<PathAndArgs>,
29 pub rustflags: OptValue<StringList>,
31 pub rustdocflags: OptValue<StringList>,
33 pub linker: OptValue<ConfigRelativePath>,
35 pub links_overrides: Rc<BTreeMap<String, BuildOutput>>,
41}
42
43pub(super) fn load_target_cfgs(
45 gctx: &GlobalContext,
46) -> CargoResult<Vec<(String, TargetCfgConfig)>> {
47 let mut result = Vec::new();
49 let target: BTreeMap<String, TargetCfgConfig> = gctx.get("target")?;
54 tracing::debug!("Got all targets {:#?}", target);
55 for (key, cfg) in target {
56 if let Ok(platform) = key.parse::<cargo_platform::Platform>() {
57 let mut warnings = Vec::new();
58 platform.check_cfg_keywords(&mut warnings, &Path::new(".cargo/config.toml"));
59 for w in warnings {
60 gctx.shell().warn(w)?;
61 }
62 }
63 if key.starts_with("cfg(") {
64 for other_key in cfg.other.keys() {
69 gctx.shell().warn(format!(
70 "unused key `{}` in [target] config table `{}`",
71 other_key, key
72 ))?;
73 }
74 result.push((key, cfg));
75 }
76 }
77 Ok(result)
78}
79
80pub(super) fn get_target_applies_to_host(gctx: &GlobalContext) -> CargoResult<bool> {
82 if gctx.cli_unstable().target_applies_to_host {
83 if let Ok(target_applies_to_host) = gctx.get::<bool>("target-applies-to-host") {
84 Ok(target_applies_to_host)
85 } else {
86 Ok(!gctx.cli_unstable().host_config)
87 }
88 } else if gctx.cli_unstable().host_config {
89 anyhow::bail!(
90 "the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set"
91 );
92 } else {
93 Ok(true)
94 }
95}
96
97pub(super) fn load_host_triple(gctx: &GlobalContext, triple: &str) -> CargoResult<TargetConfig> {
99 if gctx.cli_unstable().host_config {
100 let host_triple_prefix = format!("host.{}", triple);
101 let host_triple_key = ConfigKey::from_str(&host_triple_prefix);
102 let host_prefix = match gctx.get_cv(&host_triple_key)? {
103 Some(_) => host_triple_prefix,
104 None => "host".to_string(),
105 };
106 load_config_table(gctx, &host_prefix)
107 } else {
108 Ok(TargetConfig::default())
109 }
110}
111
112pub(super) fn load_target_triple(gctx: &GlobalContext, triple: &str) -> CargoResult<TargetConfig> {
114 load_config_table(gctx, &format!("target.{}", triple))
115}
116
117fn load_config_table(gctx: &GlobalContext, prefix: &str) -> CargoResult<TargetConfig> {
119 let runner: OptValue<PathAndArgs> = gctx.get(&format!("{prefix}.runner"))?;
125 let rustflags: OptValue<StringList> = gctx.get(&format!("{prefix}.rustflags"))?;
126 let rustdocflags: OptValue<StringList> = gctx.get(&format!("{prefix}.rustdocflags"))?;
127 let linker: OptValue<ConfigRelativePath> = gctx.get(&format!("{prefix}.linker"))?;
128 let target_key = ConfigKey::from_str(prefix);
130 let links_overrides = match gctx.get_table(&target_key)? {
131 Some(links) => parse_links_overrides(&target_key, links.val)?,
132 None => BTreeMap::new(),
133 };
134 Ok(TargetConfig {
135 runner,
136 rustflags,
137 rustdocflags,
138 linker,
139 links_overrides: Rc::new(links_overrides),
140 })
141}
142
143fn parse_links_overrides(
144 target_key: &ConfigKey,
145 links: HashMap<String, CV>,
146) -> CargoResult<BTreeMap<String, BuildOutput>> {
147 let mut links_overrides = BTreeMap::new();
148
149 for (lib_name, value) in links {
150 match lib_name.as_str() {
152 "ar" | "linker" | "runner" | "rustflags" | "rustdocflags" => continue,
154 _ => {}
155 }
156 let mut output = BuildOutput::default();
157 let table = value.table(&format!("{}.{}", target_key, lib_name))?.0;
158 let mut pairs = Vec::new();
160 for (k, value) in table {
161 pairs.push((k, value));
162 }
163 pairs.sort_by_key(|p| p.0);
164 for (key, value) in pairs {
165 match key.as_str() {
166 "rustc-flags" => {
167 let flags = value.string(key)?;
168 let whence = format!("target config `{}.{}` (in {})", target_key, key, flags.1);
169 let (paths, links) = BuildOutput::parse_rustc_flags(flags.0, &whence)?;
170 output.library_paths.extend(paths);
171 output.library_links.extend(links);
172 }
173 "rustc-link-lib" => {
174 let list = value.list(key)?;
175 output
176 .library_links
177 .extend(list.iter().map(|v| v.0.clone()));
178 }
179 "rustc-link-search" => {
180 let list = value.list(key)?;
181 output
182 .library_paths
183 .extend(list.iter().map(|v| PathBuf::from(&v.0)));
184 }
185 "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
186 let args = extra_link_args(LinkArgTarget::Cdylib, key, value)?;
187 output.linker_args.extend(args);
188 }
189 "rustc-link-arg-bins" => {
190 let args = extra_link_args(LinkArgTarget::Bin, key, value)?;
191 output.linker_args.extend(args);
192 }
193 "rustc-link-arg" => {
194 let args = extra_link_args(LinkArgTarget::All, key, value)?;
195 output.linker_args.extend(args);
196 }
197 "rustc-link-arg-tests" => {
198 let args = extra_link_args(LinkArgTarget::Test, key, value)?;
199 output.linker_args.extend(args);
200 }
201 "rustc-link-arg-benches" => {
202 let args = extra_link_args(LinkArgTarget::Bench, key, value)?;
203 output.linker_args.extend(args);
204 }
205 "rustc-link-arg-examples" => {
206 let args = extra_link_args(LinkArgTarget::Example, key, value)?;
207 output.linker_args.extend(args);
208 }
209 "rustc-cfg" => {
210 let list = value.list(key)?;
211 output.cfgs.extend(list.iter().map(|v| v.0.clone()));
212 }
213 "rustc-check-cfg" => {
214 let list = value.list(key)?;
215 output.check_cfgs.extend(list.iter().map(|v| v.0.clone()));
216 }
217 "rustc-env" => {
218 for (name, val) in value.table(key)?.0 {
219 let val = val.string(name)?.0;
220 output.env.push((name.clone(), val.to_string()));
221 }
222 }
223 "warning" | "rerun-if-changed" | "rerun-if-env-changed" => {
224 anyhow::bail!("`{}` is not supported in build script overrides", key);
225 }
226 _ => {
227 let val = value.string(key)?.0;
228 output.metadata.push((key.clone(), val.to_string()));
229 }
230 }
231 }
232 links_overrides.insert(lib_name, output);
233 }
234 Ok(links_overrides)
235}
236
237fn extra_link_args<'a>(
238 link_type: LinkArgTarget,
239 key: &str,
240 value: &'a CV,
241) -> CargoResult<impl Iterator<Item = (LinkArgTarget, String)> + 'a> {
242 let args = value.list(key)?;
243 Ok(args.iter().map(move |v| (link_type.clone(), v.0.clone())))
244}