Skip to main content

cargo/context/
target.rs

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