1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright (c) The cargo-guppy Contributors
// SPDX-License-Identifier: MIT OR Apache-2.0

use crate::Error;
use cfg_expr::targets::get_builtin_target_by_triple;
use custom_platforms::TargetInfo;
use std::{borrow::Cow, collections::BTreeSet, ops::Deref};

// This is generated by the build script.
include!(concat!(env!("OUT_DIR"), "/current_platform.rs"));

/// Support for creating custom platforms.
///
/// This module re-exports parts of the `cfg_expr` dependency to allow creating platforms unknown to
/// `rustc` by default.
pub mod custom_platforms {
    #[doc(inline)]
    pub use cfg_expr::targets::{Arch, Endian, Env, Family, Os, TargetInfo, Vendor};
}

/// A platform to evaluate target specs against.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Platform<'a> {
    target_info: &'a TargetInfo<'a>,
    target_features: TargetFeatures,
    flags: BTreeSet<Cow<'static, str>>,
    is_custom: bool,
}

impl Platform<'static> {
    /// Creates a new `Platform` from the given built-in triple and target features.
    ///
    /// Returns `None` if this platform wasn't known to `target-spec`.
    pub fn new(triple: impl AsRef<str>, target_features: TargetFeatures) -> Result<Self, Error> {
        let triple = triple.as_ref();
        Ok(Self {
            target_info: get_builtin_target_by_triple(triple)
                .ok_or_else(|| Error::UnknownPlatformTriple(triple.to_string()))?,
            target_features,
            flags: BTreeSet::new(),
            is_custom: false,
        })
    }
}

impl<'a> Platform<'a> {
    /// Creates a new, custom platform from a `TargetInfo` and target features.
    ///
    /// Custom platforms are often found in embedded and similar environments. For built-in
    /// platforms, `new` is recommended instead.
    pub fn custom(target_info: &'a TargetInfo<'a>, target_features: TargetFeatures) -> Self {
        Self {
            target_info,
            target_features,
            flags: BTreeSet::new(),
            is_custom: true,
        }
    }

    /// Adds a set of flags to accept.
    ///
    /// A flag is a single token like the `foo` in `cfg(not(foo))`.
    ///
    /// A default `cargo build` will always evaluate flags to false, but custom wrappers may cause
    /// some flags to evaluate to true. For example, as of version 0.6, `cargo web build` will cause
    /// `cargo_web` to evaluate to true.
    pub fn add_flags(&mut self, flags: impl IntoIterator<Item = impl Into<Cow<'static, str>>>) {
        self.flags.extend(flags.into_iter().map(|s| s.into()));
    }

    /// Returns the target triple for this platform.
    pub fn triple(&self) -> &'a str {
        self.target_info.triple
    }

    /// Returns the set of flags enabled for this platform.
    pub fn flags(&self) -> impl Iterator<Item = &str> {
        self.flags.iter().map(|flag| flag.deref())
    }

    /// Returns true if this flag was set with `add_flags`.
    pub fn has_flag(&self, flag: impl AsRef<str>) -> bool {
        self.flags.contains(flag.as_ref())
    }

    /// Returns the underlying `TargetInfo`.
    pub fn target_info(&self) -> &'a TargetInfo<'a> {
        self.target_info
    }

    /// Returns the set of target features for this platform.
    pub fn target_features(&self) -> &TargetFeatures {
        &self.target_features
    }

    /// Returns true if this is a custom platform, created by `Platform::custom`.
    pub fn is_custom(&self) -> bool {
        self.is_custom
    }
}

impl Platform<'static> {
    /// Returns the current platform, as detected at build time.
    ///
    /// This will return `None` if the current platform was unknown to this version of
    /// `target-spec`.
    pub fn current() -> Option<Self> {
        let target_info = get_builtin_target_by_triple(CURRENT_TARGET)?;
        let target_features = TargetFeatures::features(CURRENT_TARGET_FEATURES.iter().copied());
        Some(Self {
            target_info,
            target_features,
            flags: BTreeSet::new(),
            is_custom: false,
        })
    }
}

/// A set of target features to match.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[non_exhaustive]
pub enum TargetFeatures {
    /// The target features are unknown.
    Unknown,
    /// Only match the specified features.
    Features(BTreeSet<Cow<'static, str>>),
    /// Match all features.
    All,
}

impl TargetFeatures {
    /// Creates a new `TargetFeatures` which matches some features.
    pub fn features(features: impl IntoIterator<Item = impl Into<Cow<'static, str>>>) -> Self {
        TargetFeatures::Features(features.into_iter().map(|s| s.into()).collect())
    }

    /// Creates a new `TargetFeatures` which doesn't match any features.
    pub fn none() -> Self {
        TargetFeatures::Features(BTreeSet::new())
    }

    /// Returns `Some(true)` if this feature is a match, `Some(false)` if it isn't, and `None` if
    /// the set of target features is unknown.
    pub fn matches(&self, feature: &str) -> Option<bool> {
        match self {
            TargetFeatures::Unknown => None,
            TargetFeatures::Features(features) => Some(features.contains(feature)),
            TargetFeatures::All => Some(true),
        }
    }
}