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