target_spec/platform.rs
1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{Error, Triple};
5use std::{borrow::Cow, collections::BTreeSet, ops::Deref};
6
7// This is generated by the build script.
8include!(concat!(env!("OUT_DIR"), "/build_target.rs"));
9
10/// A platform to evaluate target specifications against.
11///
12/// # Standard and custom platforms
13///
14/// `target-spec` recognizes two kinds of platforms:
15///
16/// * **Standard platforms:** These platforms are only specified by their triple string. For
17/// example, the platform `x86_64-unknown-linux-gnu` is a standard platform since it is recognized
18/// by Rust as a tier 1 platform.
19///
20/// All [builtin platforms](https://doc.rust-lang.org/nightly/rustc/platform-support.html) are
21/// standard platforms.
22///
23/// By default, if a platform isn't builtin, target-spec attempts to heuristically determine the
24/// characteristics of the platform based on the triple string. (Use the
25/// [`new_strict`](Self::new_strict) constructor to disable this.)
26///
27/// * **Custom platforms:** These platforms are specified via both a triple string and a JSON file
28/// in the format [defined by
29/// Rust](https://docs.rust-embedded.org/embedonomicon/custom-target.html). Custom platforms are
30/// used for targets not recognized by Rust.
31#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
32#[must_use]
33pub struct Platform {
34 triple: Triple,
35 target_features: TargetFeatures,
36 flags: BTreeSet<Cow<'static, str>>,
37}
38
39impl Platform {
40 /// Creates a new standard `Platform` from the given triple and target features.
41 ///
42 /// Returns an error if this platform wasn't known to `target-spec`.
43 pub fn new(
44 triple_str: impl Into<Cow<'static, str>>,
45 target_features: TargetFeatures,
46 ) -> Result<Self, Error> {
47 let triple = Triple::new(triple_str.into()).map_err(Error::UnknownPlatformTriple)?;
48 Ok(Self::from_triple(triple, target_features))
49 }
50
51 /// Creates a new standard `Platform` from the given triple and target features.
52 ///
53 /// This constructor only consults the builtin platform table, and does not attempt to
54 /// heuristically determine the platform's characteristics based on the triple string.
55 pub fn new_strict(
56 triple_str: impl Into<Cow<'static, str>>,
57 target_features: TargetFeatures,
58 ) -> Result<Self, Error> {
59 let triple = Triple::new_strict(triple_str.into()).map_err(Error::UnknownPlatformTriple)?;
60 Ok(Self::from_triple(triple, target_features))
61 }
62
63 /// Creates a new standard `Platform` from `rustc -vV` output and the given
64 /// target features.
65 pub fn from_rustc_version_verbose(
66 output: impl AsRef<[u8]>,
67 target_features: TargetFeatures,
68 ) -> Result<Self, Error> {
69 let triple = Triple::from_rustc_version_verbose(output.as_ref())?;
70 Ok(Self::from_triple(triple, target_features))
71 }
72
73 /// Previous name for [`Self::build_target`], renamed to clarify what
74 /// `current` means.
75 ///
76 /// This method is deprecated and will be removed in a future version.
77 #[deprecated(
78 since = "3.4.0",
79 note = "this method has been renamed to `build_target`"
80 )]
81 #[inline]
82 pub fn current() -> Result<Self, Error> {
83 Self::build_target()
84 }
85
86 /// Returns the target platform, as detected at build time.
87 ///
88 /// In case of cross-compilation, this will be the **target platform**, not
89 /// the host platform.
90 ///
91 /// Some target platforms are runtime cross-compatible, e.g.
92 /// `x86_64-unknown-linux-musl` binaries can be run on
93 /// `x86_64-unknown-linux-gnu`. In that case, this function returns
94 /// `x86_64-unknown-linux-musl`. To obtain `x86_64-unknown-linux-gnu`, run
95 /// `${RUSTC:-rustc} -vV` and pass it into [`Self::from_rustc_version_verbose`].
96 ///
97 /// This is currently always a standard platform, and will return an error if the current
98 /// platform was unknown to this version of `target-spec`.
99 ///
100 /// # Notes
101 ///
102 /// In the future, this constructor may also support custom platforms. This will not be
103 /// considered a breaking change.
104 pub fn build_target() -> Result<Self, Error> {
105 let triple = Triple::new(BUILD_TARGET).map_err(Error::UnknownPlatformTriple)?;
106 let target_features = TargetFeatures::features(BUILD_TARGET_FEATURES.iter().copied());
107 Ok(Self {
108 triple,
109 target_features,
110 flags: BTreeSet::new(),
111 })
112 }
113
114 /// Creates a new standard platform from a `Triple` and target features.
115 pub fn from_triple(triple: Triple, target_features: TargetFeatures) -> Self {
116 Self {
117 triple,
118 target_features,
119 flags: BTreeSet::new(),
120 }
121 }
122
123 /// Creates a new custom `Platform` from the given triple, platform, and target features.
124 #[cfg(feature = "custom")]
125 pub fn new_custom(
126 triple_str: impl Into<Cow<'static, str>>,
127 json: &str,
128 target_features: TargetFeatures,
129 ) -> Result<Self, Error> {
130 let triple = Triple::new_custom(triple_str, json).map_err(Error::CustomPlatformCreate)?;
131 Ok(Self {
132 triple,
133 target_features,
134 flags: BTreeSet::new(),
135 })
136 }
137
138 /// Adds a set of flags to accept.
139 ///
140 /// A flag is a single token like the `foo` in `cfg(not(foo))`.
141 ///
142 /// A default `cargo build` will always evaluate flags to false, but custom wrappers may cause
143 /// some flags to evaluate to true. For example, as of version 0.6, `cargo web build` will cause
144 /// `cargo_web` to evaluate to true.
145 pub fn add_flags(&mut self, flags: impl IntoIterator<Item = impl Into<Cow<'static, str>>>) {
146 self.flags.extend(flags.into_iter().map(|s| s.into()));
147 }
148
149 /// Returns the target triple string for this platform.
150 pub fn triple_str(&self) -> &str {
151 self.triple.as_str()
152 }
153
154 /// Returns the set of flags enabled for this platform.
155 pub fn flags(&self) -> impl ExactSizeIterator<Item = &str> {
156 self.flags.iter().map(|flag| flag.deref())
157 }
158
159 /// Returns true if this flag was set with `add_flags`.
160 pub fn has_flag(&self, flag: impl AsRef<str>) -> bool {
161 self.flags.contains(flag.as_ref())
162 }
163
164 /// Returns true if this is a standard platform.
165 ///
166 /// A standard platform can be either builtin, or heuristically determined.
167 ///
168 /// # Examples
169 ///
170 /// ```
171 /// use target_spec::{Platform, TargetFeatures};
172 ///
173 /// // x86_64-unknown-linux-gnu is Linux x86_64.
174 /// let platform = Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap();
175 /// assert!(platform.is_standard());
176 /// ```
177 pub fn is_standard(&self) -> bool {
178 self.triple.is_standard()
179 }
180
181 /// Returns true if this is a builtin platform.
182 ///
183 /// All builtin platforms are standard, but not all standard platforms are builtin.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use target_spec::{Platform, TargetFeatures};
189 ///
190 /// // x86_64-unknown-linux-gnu is Linux x86_64, which is a Rust tier 1 platform.
191 /// let platform = Platform::new("x86_64-unknown-linux-gnu", TargetFeatures::Unknown).unwrap();
192 /// assert!(platform.is_builtin());
193 /// ```
194 pub fn is_builtin(&self) -> bool {
195 self.triple.is_builtin()
196 }
197
198 /// Returns true if this is a heuristically determined platform.
199 ///
200 /// All heuristically determined platforms are standard, but most of the time, standard
201 /// platforms are builtin.
202 ///
203 /// # Examples
204 ///
205 /// ```
206 /// use target_spec::{Platform, TargetFeatures};
207 ///
208 /// // armv5te-apple-darwin is not a real platform, but target-spec can heuristically
209 /// // guess at its characteristics.
210 /// let platform = Platform::new("armv5te-apple-darwin", TargetFeatures::Unknown).unwrap();
211 /// assert!(platform.is_heuristic());
212 /// ```
213 pub fn is_heuristic(&self) -> bool {
214 self.triple.is_heuristic()
215 }
216
217 /// Returns true if this is a custom platform.
218 ///
219 /// This is always available, but if the `custom` feature isn't turned on this always returns
220 /// false.
221 pub fn is_custom(&self) -> bool {
222 self.triple.is_custom()
223 }
224
225 /// Returns the underlying [`Triple`].
226 pub fn triple(&self) -> &Triple {
227 &self.triple
228 }
229
230 /// Returns the set of target features for this platform.
231 pub fn target_features(&self) -> &TargetFeatures {
232 &self.target_features
233 }
234
235 #[cfg(feature = "summaries")]
236 pub(crate) fn custom_json(&self) -> Option<&str> {
237 self.triple.custom_json()
238 }
239}
240
241/// A set of target features to match.
242#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
243#[non_exhaustive]
244pub enum TargetFeatures {
245 /// The target features are unknown.
246 Unknown,
247 /// Only match the specified features.
248 Features(BTreeSet<Cow<'static, str>>),
249 /// Match all features.
250 All,
251}
252
253impl TargetFeatures {
254 /// Creates a new `TargetFeatures` which matches some features.
255 pub fn features(features: impl IntoIterator<Item = impl Into<Cow<'static, str>>>) -> Self {
256 TargetFeatures::Features(features.into_iter().map(|s| s.into()).collect())
257 }
258
259 /// Creates a new `TargetFeatures` which doesn't match any features.
260 pub fn none() -> Self {
261 TargetFeatures::Features(BTreeSet::new())
262 }
263
264 /// Returns `Some(true)` if this feature is a match, `Some(false)` if it isn't, and `None` if
265 /// the set of target features is unknown.
266 pub fn matches(&self, feature: &str) -> Option<bool> {
267 match self {
268 TargetFeatures::Unknown => None,
269 TargetFeatures::Features(features) => Some(features.contains(feature)),
270 TargetFeatures::All => Some(true),
271 }
272 }
273}