#
cargo/util/context/
target.rs

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