Skip to main content

cargo/util/context/
target.rs

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/// Config definition of a `[target.'cfg(…)']` table.
10///
11/// This is a subset of `TargetConfig`.
12#[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    // This is here just to ignore fields from normal `TargetConfig` because
19    // all `[target]` tables are getting deserialized, whether they start with
20    // `cfg(` or not.
21    #[serde(flatten)]
22    pub other: BTreeMap<String, toml::Value>,
23}
24
25/// Config definition of a `[target]` table or `[host]`.
26#[derive(Debug, Clone, Default)]
27pub struct TargetConfig {
28    /// Process to run as a wrapper for `cargo run`, `test`, and `bench` commands.
29    pub runner: OptValue<PathAndArgs>,
30    /// Additional rustc flags to pass.
31    pub rustflags: OptValue<StringList>,
32    /// Additional rustdoc flags to pass.
33    pub rustdocflags: OptValue<StringList>,
34    /// The path of the linker for this target.
35    pub linker: OptValue<ConfigRelativePath>,
36    /// Build script override for the given library name.
37    ///
38    /// Any package with a `links` value for the given library name will skip
39    /// running its build script and instead use the given output from the
40    /// config file.
41    pub links_overrides: Rc<BTreeMap<String, BuildOutput>>,
42}
43
44/// Loads all of the `target.'cfg()'` tables.
45pub(super) fn load_target_cfgs(
46    gctx: &GlobalContext,
47) -> CargoResult<Vec<(String, TargetCfgConfig)>> {
48    // Load all [target] tables, filter out the cfg() entries.
49    let mut result = Vec::new();
50    // Use a BTreeMap so the keys are sorted. This is important for
51    // deterministic ordering of rustflags, which affects fingerprinting and
52    // rebuilds. We may perhaps one day wish to ensure a deterministic
53    // ordering via the order keys were defined in files perhaps.
54    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            // Unfortunately this is not able to display the location of the
66            // unused key. Using config::Value<toml::Value> doesn't work. One
67            // solution might be to create a special "Any" type, but I think
68            // that will be quite difficult with the current design.
69            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
81/// Returns true if the `[target]` table should be applied to host targets.
82pub(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
98/// Loads a single `[host]` table for the given triple.
99pub(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
113/// Loads a single `[target]` table for the given triple.
114pub(super) fn load_target_triple(gctx: &GlobalContext, triple: &str) -> CargoResult<TargetConfig> {
115    load_config_table(gctx, &format!("target.{}", triple))
116}
117
118/// Loads a single table for the given prefix.
119fn load_config_table(gctx: &GlobalContext, prefix: &str) -> CargoResult<TargetConfig> {
120    // This needs to get each field individually because it cannot fetch the
121    // struct all at once due to `links_overrides`. Can't use `serde(flatten)`
122    // because it causes serde to use `deserialize_map` which means the config
123    // deserializer does not know which keys to deserialize, which means
124    // environment variables would not work.
125    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    // Links do not support environment variables.
130    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        // Skip these keys, it shares the namespace with `TargetConfig`.
152        match lib_name.as_str() {
153            // `ar` is a historical thing.
154            "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        // We require deterministic order of evaluation, so we must sort the pairs by key first.
160        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}