]> git.proxmox.com Git - cargo.git/blob - src/cargo/util/config/target.rs
8190529a68607780e8d86e7b987d60ef87943bb2
[cargo.git] / src / cargo / util / config / target.rs
1 use super::{Config, ConfigKey, ConfigRelativePath, OptValue, PathAndArgs, StringList, CV};
2 use crate::core::compiler::{BuildOutput, LinkType};
3 use crate::util::CargoResult;
4 use serde::Deserialize;
5 use std::collections::{BTreeMap, HashMap};
6 use std::path::PathBuf;
7 use toml_edit::easy as toml;
8
9 /// Config definition of a `[target.'cfg(…)']` table.
10 ///
11 /// This is a subset of `TargetConfig`.
12 #[derive(Debug, Deserialize)]
13 pub struct TargetCfgConfig {
14 pub runner: OptValue<PathAndArgs>,
15 pub rustflags: OptValue<StringList>,
16 // This is here just to ignore fields from normal `TargetConfig` because
17 // all `[target]` tables are getting deserialized, whether they start with
18 // `cfg(` or not.
19 #[serde(flatten)]
20 pub other: BTreeMap<String, toml::Value>,
21 }
22
23 /// Config definition of a `[target]` table or `[host]`.
24 #[derive(Debug, Clone)]
25 pub struct TargetConfig {
26 /// Process to run as a wrapper for `cargo run`, `test`, and `bench` commands.
27 pub runner: OptValue<PathAndArgs>,
28 /// Additional rustc flags to pass.
29 pub rustflags: OptValue<StringList>,
30 /// The path of the linker for this target.
31 pub linker: OptValue<ConfigRelativePath>,
32 /// Build script override for the given library name.
33 ///
34 /// Any package with a `links` value for the given library name will skip
35 /// running its build script and instead use the given output from the
36 /// config file.
37 pub links_overrides: BTreeMap<String, BuildOutput>,
38 }
39
40 /// Loads all of the `target.'cfg()'` tables.
41 pub(super) fn load_target_cfgs(config: &Config) -> CargoResult<Vec<(String, TargetCfgConfig)>> {
42 // Load all [target] tables, filter out the cfg() entries.
43 let mut result = Vec::new();
44 // Use a BTreeMap so the keys are sorted. This is important for
45 // deterministic ordering of rustflags, which affects fingerprinting and
46 // rebuilds. We may perhaps one day wish to ensure a deterministic
47 // ordering via the order keys were defined in files perhaps.
48 let target: BTreeMap<String, TargetCfgConfig> = config.get("target")?;
49 log::debug!("Got all targets {:#?}", target);
50 for (key, cfg) in target {
51 if key.starts_with("cfg(") {
52 // Unfortunately this is not able to display the location of the
53 // unused key. Using config::Value<toml::Value> doesn't work. One
54 // solution might be to create a special "Any" type, but I think
55 // that will be quite difficult with the current design.
56 for other_key in cfg.other.keys() {
57 config.shell().warn(format!(
58 "unused key `{}` in [target] config table `{}`",
59 other_key, key
60 ))?;
61 }
62 result.push((key, cfg));
63 }
64 }
65 Ok(result)
66 }
67
68 /// Returns true if the `[target]` table should be applied to host targets.
69 pub(super) fn get_target_applies_to_host(config: &Config) -> CargoResult<bool> {
70 if config.cli_unstable().target_applies_to_host {
71 if let Ok(target_applies_to_host) = config.get::<bool>("target-applies-to-host") {
72 Ok(target_applies_to_host)
73 } else {
74 Ok(!config.cli_unstable().host_config)
75 }
76 } else if config.cli_unstable().host_config {
77 anyhow::bail!(
78 "the -Zhost-config flag requires the -Ztarget-applies-to-host flag to be set"
79 );
80 } else {
81 Ok(true)
82 }
83 }
84
85 /// Loads a single `[host]` table for the given triple.
86 pub(super) fn load_host_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
87 if config.cli_unstable().host_config {
88 let host_triple_prefix = format!("host.{}", triple);
89 let host_triple_key = ConfigKey::from_str(&host_triple_prefix);
90 let host_prefix = match config.get_cv(&host_triple_key)? {
91 Some(_) => host_triple_prefix,
92 None => "host".to_string(),
93 };
94 load_config_table(config, &host_prefix)
95 } else {
96 Ok(TargetConfig {
97 runner: None,
98 rustflags: None,
99 linker: None,
100 links_overrides: BTreeMap::new(),
101 })
102 }
103 }
104
105 /// Loads a single `[target]` table for the given triple.
106 pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
107 load_config_table(config, &format!("target.{}", triple))
108 }
109
110 /// Loads a single table for the given prefix.
111 fn load_config_table(config: &Config, prefix: &str) -> CargoResult<TargetConfig> {
112 // This needs to get each field individually because it cannot fetch the
113 // struct all at once due to `links_overrides`. Can't use `serde(flatten)`
114 // because it causes serde to use `deserialize_map` which means the config
115 // deserializer does not know which keys to deserialize, which means
116 // environment variables would not work.
117 let runner: OptValue<PathAndArgs> = config.get(&format!("{}.runner", prefix))?;
118 let rustflags: OptValue<StringList> = config.get(&format!("{}.rustflags", prefix))?;
119 let linker: OptValue<ConfigRelativePath> = config.get(&format!("{}.linker", prefix))?;
120 // Links do not support environment variables.
121 let target_key = ConfigKey::from_str(prefix);
122 let links_overrides = match config.get_table(&target_key)? {
123 Some(links) => parse_links_overrides(&target_key, links.val, config)?,
124 None => BTreeMap::new(),
125 };
126 Ok(TargetConfig {
127 runner,
128 rustflags,
129 linker,
130 links_overrides,
131 })
132 }
133
134 fn parse_links_overrides(
135 target_key: &ConfigKey,
136 links: HashMap<String, CV>,
137 config: &Config,
138 ) -> CargoResult<BTreeMap<String, BuildOutput>> {
139 let mut links_overrides = BTreeMap::new();
140 let extra_check_cfg = match config.cli_unstable().check_cfg {
141 Some((_, _, _, output)) => output,
142 None => false,
143 };
144
145 for (lib_name, value) in links {
146 // Skip these keys, it shares the namespace with `TargetConfig`.
147 match lib_name.as_str() {
148 // `ar` is a historical thing.
149 "ar" | "linker" | "runner" | "rustflags" => continue,
150 _ => {}
151 }
152 let mut output = BuildOutput::default();
153 let table = value.table(&format!("{}.{}", target_key, lib_name))?.0;
154 // We require deterministic order of evaluation, so we must sort the pairs by key first.
155 let mut pairs = Vec::new();
156 for (k, value) in table {
157 pairs.push((k, value));
158 }
159 pairs.sort_by_key(|p| p.0);
160 for (key, value) in pairs {
161 match key.as_str() {
162 "rustc-flags" => {
163 let flags = value.string(key)?;
164 let whence = format!("target config `{}.{}` (in {})", target_key, key, flags.1);
165 let (paths, links) = BuildOutput::parse_rustc_flags(flags.0, &whence)?;
166 output.library_paths.extend(paths);
167 output.library_links.extend(links);
168 }
169 "rustc-link-lib" => {
170 let list = value.list(key)?;
171 output
172 .library_links
173 .extend(list.iter().map(|v| v.0.clone()));
174 }
175 "rustc-link-search" => {
176 let list = value.list(key)?;
177 output
178 .library_paths
179 .extend(list.iter().map(|v| PathBuf::from(&v.0)));
180 }
181 "rustc-link-arg-cdylib" | "rustc-cdylib-link-arg" => {
182 let args = extra_link_args(LinkType::Cdylib, key, value)?;
183 output.linker_args.extend(args);
184 }
185 "rustc-link-arg-bins" => {
186 let args = extra_link_args(LinkType::Bin, key, value)?;
187 output.linker_args.extend(args);
188 }
189 "rustc-link-arg" => {
190 let args = extra_link_args(LinkType::All, key, value)?;
191 output.linker_args.extend(args);
192 }
193 "rustc-link-arg-tests" => {
194 let args = extra_link_args(LinkType::Test, key, value)?;
195 output.linker_args.extend(args);
196 }
197 "rustc-link-arg-benches" => {
198 let args = extra_link_args(LinkType::Bench, key, value)?;
199 output.linker_args.extend(args);
200 }
201 "rustc-link-arg-examples" => {
202 let args = extra_link_args(LinkType::Example, key, value)?;
203 output.linker_args.extend(args);
204 }
205 "rustc-cfg" => {
206 let list = value.list(key)?;
207 output.cfgs.extend(list.iter().map(|v| v.0.clone()));
208 }
209 "rustc-check-cfg" => {
210 if extra_check_cfg {
211 let list = value.list(key)?;
212 output.check_cfgs.extend(list.iter().map(|v| v.0.clone()));
213 } else {
214 config.shell().warn(format!(
215 "target config `{}.{}` requires -Zcheck-cfg=output flag",
216 target_key, key
217 ))?;
218 }
219 }
220 "rustc-env" => {
221 for (name, val) in value.table(key)?.0 {
222 let val = val.string(name)?.0;
223 output.env.push((name.clone(), val.to_string()));
224 }
225 }
226 "warning" | "rerun-if-changed" | "rerun-if-env-changed" => {
227 anyhow::bail!("`{}` is not supported in build script overrides", key);
228 }
229 _ => {
230 let val = value.string(key)?.0;
231 output.metadata.push((key.clone(), val.to_string()));
232 }
233 }
234 }
235 links_overrides.insert(lib_name, output);
236 }
237 Ok(links_overrides)
238 }
239
240 fn extra_link_args<'a>(
241 link_type: LinkType,
242 key: &str,
243 value: &'a CV,
244 ) -> CargoResult<impl Iterator<Item = (LinkType, String)> + 'a> {
245 let args = value.list(key)?;
246 Ok(args.iter().map(move |v| (link_type.clone(), v.0.clone())))
247 }