Skip to main content

cargo/util/config/
target.rs

1use super::{Config, ConfigKey, ConfigRelativePath, OptValue, PathAndArgs, StringList, CV};
2use crate::core::compiler::BuildOutput;
3use crate::util::CargoResult;
4use serde::Deserialize;
5use std::collections::{BTreeMap, HashMap};
6use std::path::PathBuf;
7
8/// Config definition of a [target.'cfg(…)'] table.
9///
10/// This is a subset of `TargetConfig`.
11#[derive(Debug, Deserialize)]
12pub struct TargetCfgConfig {
13    pub runner: OptValue<PathAndArgs>,
14    pub rustflags: OptValue<StringList>,
15    // This is here just to ignore fields from normal `TargetConfig` because
16    // all `[target]` tables are getting deserialized, whether they start with
17    // `cfg(` or not.
18    #[serde(flatten)]
19    pub other: BTreeMap<String, toml::Value>,
20}
21
22/// Config definition of a [target] table.
23#[derive(Debug)]
24pub struct TargetConfig {
25    /// Process to run as a wrapper for `cargo run`, `test`, and `bench` commands.
26    pub runner: OptValue<PathAndArgs>,
27    /// Additional rustc flags to pass.
28    pub rustflags: OptValue<StringList>,
29    /// The path of the linker for this target.
30    pub linker: OptValue<ConfigRelativePath>,
31    /// Build script override for the given library name.
32    ///
33    /// Any package with a `links` value for the given library name will skip
34    /// running its build script and instead use the given output from the
35    /// config file.
36    pub links_overrides: BTreeMap<String, BuildOutput>,
37}
38
39/// Loads all of the `target.'cfg()'` tables.
40pub(super) fn load_target_cfgs(config: &Config) -> CargoResult<Vec<(String, TargetCfgConfig)>> {
41    // Load all [target] tables, filter out the cfg() entries.
42    let mut result = Vec::new();
43    // Use a BTreeMap so the keys are sorted. This is important for
44    // deterministic ordering of rustflags, which affects fingerprinting and
45    // rebuilds. We may perhaps one day wish to ensure a deterministic
46    // ordering via the order keys were defined in files perhaps.
47    let target: BTreeMap<String, TargetCfgConfig> = config.get("target")?;
48    log::debug!("Got all targets {:#?}", target);
49    for (key, cfg) in target {
50        if key.starts_with("cfg(") {
51            // Unfortunately this is not able to display the location of the
52            // unused key. Using config::Value<toml::Value> doesn't work. One
53            // solution might be to create a special "Any" type, but I think
54            // that will be quite difficult with the current design.
55            for other_key in cfg.other.keys() {
56                config.shell().warn(format!(
57                    "unused key `{}` in [target] config table `{}`",
58                    other_key, key
59                ))?;
60            }
61            result.push((key, cfg));
62        }
63    }
64    Ok(result)
65}
66
67/// Loads a single `[target]` table for the given triple.
68pub(super) fn load_target_triple(config: &Config, triple: &str) -> CargoResult<TargetConfig> {
69    // This needs to get each field individually because it cannot fetch the
70    // struct all at once due to `links_overrides`. Can't use `serde(flatten)`
71    // because it causes serde to use `deserialize_map` which means the config
72    // deserializer does not know which keys to deserialize, which means
73    // environment variables would not work.
74    let runner: OptValue<PathAndArgs> = config.get(&format!("target.{}.runner", triple))?;
75    let rustflags: OptValue<StringList> = config.get(&format!("target.{}.rustflags", triple))?;
76    let linker: OptValue<ConfigRelativePath> = config.get(&format!("target.{}.linker", triple))?;
77    // Links do not support environment variables.
78    let target_key = ConfigKey::from_str(&format!("target.{}", triple));
79    let links_overrides = match config.get_table(&target_key)? {
80        Some(links) => parse_links_overrides(&target_key, links.val)?,
81        None => BTreeMap::new(),
82    };
83    Ok(TargetConfig {
84        runner,
85        rustflags,
86        linker,
87        links_overrides,
88    })
89}
90
91fn parse_links_overrides(
92    target_key: &ConfigKey,
93    links: HashMap<String, CV>,
94) -> CargoResult<BTreeMap<String, BuildOutput>> {
95    let mut links_overrides = BTreeMap::new();
96    for (lib_name, value) in links {
97        // Skip these keys, it shares the namespace with `TargetConfig`.
98        match lib_name.as_str() {
99            // `ar` is a historical thing.
100            "ar" | "linker" | "runner" | "rustflags" => continue,
101            _ => {}
102        }
103        let mut output = BuildOutput::default();
104        let table = value.table(&format!("{}.{}", target_key, lib_name))?.0;
105        // We require deterministic order of evaluation, so we must sort the pairs by key first.
106        let mut pairs = Vec::new();
107        for (k, value) in table {
108            pairs.push((k, value));
109        }
110        pairs.sort_by_key(|p| p.0);
111        for (key, value) in pairs {
112            match key.as_str() {
113                "rustc-flags" => {
114                    let flags = value.string(key)?;
115                    let whence = format!("target config `{}.{}` (in {})", target_key, key, flags.1);
116                    let (paths, links) = BuildOutput::parse_rustc_flags(flags.0, &whence)?;
117                    output.library_paths.extend(paths);
118                    output.library_links.extend(links);
119                }
120                "rustc-link-lib" => {
121                    let list = value.list(key)?;
122                    output
123                        .library_links
124                        .extend(list.iter().map(|v| v.0.clone()));
125                }
126                "rustc-link-search" => {
127                    let list = value.list(key)?;
128                    output
129                        .library_paths
130                        .extend(list.iter().map(|v| PathBuf::from(&v.0)));
131                }
132                "rustc-cdylib-link-arg" => {
133                    let args = value.list(key)?;
134                    output.linker_args.extend(args.iter().map(|v| v.0.clone()));
135                }
136                "rustc-cfg" => {
137                    let list = value.list(key)?;
138                    output.cfgs.extend(list.iter().map(|v| v.0.clone()));
139                }
140                "rustc-env" => {
141                    for (name, val) in value.table(key)?.0 {
142                        let val = val.string(name)?.0;
143                        output.env.push((name.clone(), val.to_string()));
144                    }
145                }
146                "warning" | "rerun-if-changed" | "rerun-if-env-changed" => {
147                    anyhow::bail!("`{}` is not supported in build script overrides", key);
148                }
149                _ => {
150                    let val = value.string(key)?.0;
151                    output.metadata.push((key.clone(), val.to_string()));
152                }
153            }
154        }
155        links_overrides.insert(lib_name, output);
156    }
157    Ok(links_overrides)
158}