1use super::{CV, ConfigKey, ConfigRelativePath, GlobalContext, OptValue, PathAndArgs, StringList};
2use crate::compiler::{BuildOutput, LibraryPath, LinkArgTarget};
3use crate::util::CargoResult;
4use crate::util::data_structures::HashMap;
5use cargo_util_terminal::report::Level;
6use serde::Deserialize;
7use std::collections::BTreeMap;
8use std::path::{Path, PathBuf};
9use std::rc::Rc;
10
11#[derive(Debug, Deserialize)]
15pub struct TargetCfgConfig {
16 pub runner: OptValue<PathAndArgs>,
17 pub rustflags: OptValue<StringList>,
18 pub rustdocflags: OptValue<StringList>,
19 pub linker: OptValue<ConfigRelativePath>,
20 #[serde(flatten)]
24 pub other: BTreeMap<String, toml::Value>,
25}
26
27#[derive(Debug, Clone, Default)]
29pub struct TargetConfig {
30 pub runner: OptValue<PathAndArgs>,
32 pub rustflags: OptValue<StringList>,
34 pub rustdocflags: OptValue<StringList>,
36 pub linker: OptValue<ConfigRelativePath>,
38 pub links_overrides: Rc<BTreeMap<String, BuildOutput>>,
44}
45
46pub(super) fn load_target_cfgs(
48 gctx: &GlobalContext,
49) -> CargoResult<Vec<(String, TargetCfgConfig)>> {
50 let mut result = Vec::new();
52 let target: BTreeMap<String, TargetCfgConfig> = gctx.get(["target"])?;
57 tracing::debug!("Got all targets {:#?}", target);
58 for (key, cfg) in target {
59 if let Ok(platform) = key.parse::<cargo_platform::Platform>() {
60 let mut warnings = Vec::new();
61 platform.check_cfg_keywords(&mut warnings, &Path::new(".cargo/config.toml"));
62 for w in warnings {
63 gctx.shell().warn(w)?;
64 }
65 }
66 if key.starts_with("cfg(") {
67 for other_key in cfg.other.keys() {
72 gctx.shell().warn(format!(
73 "unused key `{}` in [target] config table `{}`",
74 other_key, key
75 ))?;
76 }
77 result.push((key, cfg));
78 }
79 }
80 Ok(result)
81}
82
83pub(super) fn get_target_applies_to_host(gctx: &GlobalContext) -> CargoResult<bool> {
85 if gctx.cli_unstable().target_applies_to_host {
86 if let Ok(target_applies_to_host) = gctx.get::<bool>(["target-applies-to-host"]) {
87 Ok(target_applies_to_host)
88 } else {
89 Ok(!gctx.cli_unstable().host_config)
90 }
91 } else if gctx.cli_unstable().host_config {
92 anyhow::bail!(
93 "the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set"
94 );
95 } else {
96 Ok(true)
97 }
98}
99
100pub(super) fn load_host_triple(gctx: &GlobalContext, triple: &str) -> CargoResult<TargetConfig> {
102 if gctx.cli_unstable().host_config {
103 let host_triple_prefix = ["host", triple];
104 let host_triple_key = ConfigKey::from_parts(host_triple_prefix);
105 let host_prefix = match gctx.get_cv(&host_triple_key)? {
106 Some(_) => host_triple_prefix.as_slice(),
107 None => &["host"],
108 };
109 load_config_table(gctx, host_prefix)
110 } else {
111 Ok(TargetConfig::default())
112 }
113}
114
115pub(super) fn load_target_triple(gctx: &GlobalContext, tuple: &str) -> CargoResult<TargetConfig> {
117 let cfg = load_config_table(gctx, &["target", tuple])?;
118
119 if tuple.contains(".")
120 && gctx
121 .get::<Option<toml::Table>>(&["target", tuple])?
122 .is_none()
123 {
124 let key = format!("target.{tuple}");
125 let key = key.split(".").collect::<Vec<_>>();
126 if gctx.get::<Option<toml::Table>>(&key)?.is_some() {
127 const DOTTED_TARGETS_1_98: &[&str] = &[
129 "thumbv8m.base-none-eabi",
130 "thumbv8m.main-none-eabi",
131 "thumbv8m.main-none-eabihf",
132 "thumbv8m.base-nuttx-eabi",
133 "thumbv8m.main-nuttx-eabi",
134 "thumbv8m.main-nuttx-eabihf",
135 ];
136 if DOTTED_TARGETS_1_98.contains(&tuple) {
137 let _ = gctx.shell().print_report(
141 &[Level::WARNING
142 .secondary_title(format!("reading config from `[target.{tuple}]`"))
143 .element(Level::NOTE.message(format!(
144 r#"`[target.{tuple}]` will be ignored or error in a future version"#,
145 )))
146 .element(Level::HELP.message(format!(
147 r#"to configure `{tuple}`, quote the key like `[target."{tuple}"]`"#,
148 )))],
149 false,
150 )?;
151 return load_config_table(gctx, &key);
152 } else {
153 let _ = gctx.shell().print_report(
154 &[Level::WARNING
155 .secondary_title(format!("unused config table `[target.{tuple}]`"))
156 .element(Level::HELP.message(format!(
157 r#"to configure `{tuple}`, quote the key like `[target."{tuple}"]`"#,
158 )))],
159 false,
160 )?;
161 }
162 }
163 }
164
165 Ok(cfg)
166}
167
168fn load_config_table(gctx: &GlobalContext, prefix: &[&str]) -> CargoResult<TargetConfig> {
170 let runner: OptValue<PathAndArgs> = gctx.get(prefix.iter().copied().chain(["runner"]))?;
176 let rustflags: OptValue<StringList> = gctx.get(prefix.iter().copied().chain(["rustflags"]))?;
177 let rustdocflags: OptValue<StringList> =
178 gctx.get(prefix.iter().copied().chain(["rustdocflags"]))?;
179 let linker: OptValue<ConfigRelativePath> =
180 gctx.get(prefix.iter().copied().chain(["linker"]))?;
181 let target_key = ConfigKey::from_parts(prefix);
183 let links_overrides = match gctx.get_table(&target_key)? {
184 Some(links) => parse_links_overrides(&target_key, links.val)?,
185 None => BTreeMap::new(),
186 };
187 Ok(TargetConfig {
188 runner,
189 rustflags,
190 rustdocflags,
191 linker,
192 links_overrides: Rc::new(links_overrides),
193 })
194}
195
196fn parse_links_overrides(
197 target_key: &ConfigKey,
198 links: HashMap<String, CV>,
199) -> CargoResult<BTreeMap<String, BuildOutput>> {
200 let mut links_overrides = BTreeMap::new();
201
202 for (lib_name, value) in links {
203 match lib_name.as_str() {
205 "ar" | "linker" | "runner" | "rustflags" | "rustdocflags" => continue,
207 _ => {}
208 }
209 let mut output = BuildOutput::default();
210 let table = value.table(&format!("{}.{}", target_key, lib_name))?.0;
211 let mut pairs = Vec::new();
213 for (k, value) in table {
214 pairs.push((k, value));
215 }
216 pairs.sort_by_key(|p| p.0);
217 for (key, value) in pairs {
218 match key.as_str() {
219 "rustc-flags" => {
220 let flags = value.string(key)?;
221 let whence = format!("target config `{}.{}` (in {})", target_key, key, flags.1);
222 let (paths, links) = BuildOutput::parse_rustc_flags(flags.0, &whence)?;
223 output
224 .library_paths
225 .extend(paths.into_iter().map(LibraryPath::External));
226 output.library_links.extend(links);
227 }
228 "rustc-link-lib" => {
229 let list = value.string_list(key)?;
230 output
231 .library_links
232 .extend(list.iter().map(|v| v.0.clone()));
233 }
234 "rustc-link-search" => {
235 let list = value.string_list(key)?;
236 output.library_paths.extend(
237 list.iter()
238 .map(|v| PathBuf::from(&v.0))
239 .map(LibraryPath::External),
240 );
241 }
242 "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
243 let args = extra_link_args(LinkArgTarget::Cdylib, key, value)?;
244 output.linker_args.extend(args);
245 }
246 "rustc-link-arg-bins" => {
247 let args = extra_link_args(LinkArgTarget::Bin, key, value)?;
248 output.linker_args.extend(args);
249 }
250 "rustc-link-arg" => {
251 let args = extra_link_args(LinkArgTarget::All, key, value)?;
252 output.linker_args.extend(args);
253 }
254 "rustc-link-arg-tests" => {
255 let args = extra_link_args(LinkArgTarget::Test, key, value)?;
256 output.linker_args.extend(args);
257 }
258 "rustc-link-arg-benches" => {
259 let args = extra_link_args(LinkArgTarget::Bench, key, value)?;
260 output.linker_args.extend(args);
261 }
262 "rustc-link-arg-examples" => {
263 let args = extra_link_args(LinkArgTarget::Example, key, value)?;
264 output.linker_args.extend(args);
265 }
266 "rustc-cfg" => {
267 let list = value.string_list(key)?;
268 output.cfgs.extend(list.iter().map(|v| v.0.clone()));
269 }
270 "rustc-check-cfg" => {
271 let list = value.string_list(key)?;
272 output.check_cfgs.extend(list.iter().map(|v| v.0.clone()));
273 }
274 "rustc-env" => {
275 for (name, val) in value.table(key)?.0 {
276 let val = val.string(name)?.0;
277 output.env.push((name.clone(), val.to_string()));
278 }
279 }
280 "warning" | "rerun-if-changed" | "rerun-if-env-changed" => {
281 anyhow::bail!("`{}` is not supported in build script overrides", key);
282 }
283 _ => {
284 let val = value.string(key)?.0;
285 output.metadata.push((key.clone(), val.to_string()));
286 }
287 }
288 }
289 links_overrides.insert(lib_name, output);
290 }
291 Ok(links_overrides)
292}
293
294fn extra_link_args(
295 link_type: LinkArgTarget,
296 key: &str,
297 value: &CV,
298) -> CargoResult<Vec<(LinkArgTarget, String)>> {
299 let args = value.string_list(key)?;
300 Ok(args.into_iter().map(|v| (link_type.clone(), v.0)).collect())
301}