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