1 use crate::error::Reason;
2 use std::{borrow::Cow, ops::Deref};
3
4 mod builtins;
5
6 /// A list of all of the [builtin](https://doc.rust-lang.org/nightly/nightly-rustc/rustc_target/spec/index.html#modules)
7 /// targets known to rustc, as of 1.54.0
8 pub use builtins::ALL_BUILTINS;
9
10 /// The unique identifier for a target.
11 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12 pub struct Triple(pub Cow<'static, str>);
13
14 /// The "architecture" field
15 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16 pub struct Arch(pub Cow<'static, str>);
17
18 /// The "vendor" field, which in practice is little more than an arbitrary modifier.
19 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20 pub struct Vendor(pub Cow<'static, str>);
21
22 /// The "operating system" field, which sometimes implies an environment, and
23 /// sometimes isn't an actual operating system.
24 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
25 pub struct Os(pub Cow<'static, str>);
26
27 /// Individual target families, which describe a set of targets grouped in some logical manner,
28 /// typically by operating system. This includes values like `unix` and `windows`.
29 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
30 pub struct Family(pub Cow<'static, str>);
31
32 /// The "environment" field, which specifies an ABI environment on top of the
33 /// operating system. In many configurations, this field is omitted, and the
34 /// environment is implied by the operating system.
35 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
36 pub struct Env(pub Cow<'static, str>);
37
38 macro_rules! field_impls {
39 ($kind:ident) => {
40 impl $kind {
41 /// Constructs a new instance of this field.
42 ///
43 /// This method accepts both owned `String`s and `&'static str`s.
44 #[inline]
45 pub fn new(val: impl Into<Cow<'static, str>>) -> Self {
46 Self(val.into())
47 }
48
49 /// Constructs a new instance of this field from a `&'static str`.
50 #[inline]
51 pub const fn new_const(val: &'static str) -> Self {
52 Self(Cow::Borrowed(val))
53 }
54
55 /// Returns the string for the field.
56 #[inline]
57 pub fn as_str(&self) -> &str {
58 &*self.0
59 }
60 }
61
62 impl AsRef<str> for $kind {
63 #[inline]
64 fn as_ref(&self) -> &str {
65 &*self.0
66 }
67 }
68
69 impl std::fmt::Display for $kind {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 f.write_str(self.as_str())
72 }
73 }
74 };
75 }
76
77 field_impls!(Triple);
78 field_impls!(Arch);
79 field_impls!(Vendor);
80 field_impls!(Os);
81 field_impls!(Family);
82 field_impls!(Env);
83
84 /// A set of families for a target.
85 ///
86 /// Each target can be part of one or more families. This struct represents them.
87 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
88 pub struct Families(Cow<'static, [Family]>);
89
90 impl Families {
91 /// Constructs a new instance.
92 ///
93 /// This method accepts both owned `String`s and `&'static str`s.
94 ///
95 /// If you have a `&'static [&'static str]`, prefer [`Self::new_const`].
96 #[inline]
new(val: impl IntoIterator<Item = Family>) -> Self97 pub fn new(val: impl IntoIterator<Item = Family>) -> Self {
98 let mut fams: Vec<_> = val.into_iter().collect();
99 fams.sort_unstable();
100 Self(Cow::Owned(fams))
101 }
102
103 /// Constructs a new instance of this field from a static slice of `&'static str`.
104 ///
105 /// `val` must be in sorted order: this constructor cannot check for that due to
106 /// limitations in current versions of Rust.
107 #[inline]
new_const(val: &'static [Family]) -> Self108 pub const fn new_const(val: &'static [Family]) -> Self {
109 // TODO: Check that val is sorted.
110 Self(Cow::Borrowed(val))
111 }
112
113 /// Returns true if this list of families contains a given family.
114 #[inline]
contains(&self, val: &Family) -> bool115 pub fn contains(&self, val: &Family) -> bool {
116 self.0.contains(val)
117 }
118 }
119
120 impl Deref for Families {
121 type Target = [Family];
deref(&self) -> &Self::Target122 fn deref(&self) -> &Self::Target {
123 &*self.0
124 }
125 }
126
127 impl AsRef<[Family]> for Families {
128 #[inline]
as_ref(&self) -> &[Family]129 fn as_ref(&self) -> &[Family] {
130 &*self.0
131 }
132 }
133
134 impl std::fmt::Display for Families {
fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result135 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136 write!(f, "{{")?;
137 let len = self.0.len();
138 for (idx, family) in self.0.iter().enumerate() {
139 write!(f, "{}", family)?;
140 if idx + 1 < len {
141 write!(f, ", ")?;
142 }
143 }
144 write!(f, "}}")
145 }
146 }
147
148 macro_rules! target_enum {
149 (
150 $(#[$outer:meta])*
151 pub enum $kind:ident {
152 $(
153 $(#[$inner:ident $($args:tt)*])*
154 $name:ident $(= $value:expr)?,
155 )+
156 }
157 ) => {
158 $(#[$outer])*
159 #[allow(non_camel_case_types)]
160 pub enum $kind {
161 $(
162 $(#[$inner $($args)*])*
163 $name $(= $value)?,
164 )+
165 }
166
167 impl_from_str! {
168 $kind {
169 $(
170 $(#[$inner $($args)*])*
171 $name $(= $value)?,
172 )+
173 }
174 }
175 };
176 }
177
178 macro_rules! impl_from_str {
179 (
180 $kind:ident {
181 $(
182 $(#[$attr:ident $($args:tt)*])*
183 $name:ident $(= $value:expr)?,
184 )+
185 }
186 ) => {
187 impl std::str::FromStr for $kind {
188 type Err = Reason;
189 fn from_str(s: &str) -> Result<Self, Self::Err> {
190 match s {
191 $(stringify!($name) => Ok(Self::$name),)+
192 _ => Err(Reason::Unexpected(&[$(stringify!($name),)+])),
193 }
194 }
195 }
196 };
197 }
198
199 target_enum! {
200 /// The endian types known to rustc
201 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
202 pub enum Endian {
203 big,
204 little,
205 }
206 }
207
208 /// Contains information regarding a particular target known to rustc
209 #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
210 pub struct TargetInfo {
211 /// The target's unique identifier
212 pub triple: Triple,
213 /// The target's operating system, if any. Used by the
214 /// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os)
215 /// predicate.
216 pub os: Option<Os>,
217 /// The target's CPU architecture. Used by the
218 /// [target_arch](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch)
219 /// predicate.
220 pub arch: Arch,
221 /// The target's ABI/libc used, if any. Used by the
222 /// [target_env](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env)
223 /// predicate.
224 pub env: Option<Env>,
225 /// The target's vendor, if any. Used by the
226 /// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor)
227 /// predicate.
228 pub vendor: Option<Vendor>,
229 /// The target's families, if any. Used by the
230 /// [target_family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family)
231 /// predicate.
232 pub families: Families,
233 /// The size of the target's pointer type. Used by the
234 /// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width)
235 /// predicate.
236 pub pointer_width: u8,
237 /// The target's endianness. Used by the
238 /// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian)
239 /// predicate.
240 pub endian: Endian,
241 }
242
243 /// Attempts to find the `TargetInfo` for the specified target triple
244 ///
245 /// ```
246 /// assert!(cfg_expr::targets::get_builtin_target_by_triple("x86_64-unknown-linux-musl").is_some());
247 /// ```
get_builtin_target_by_triple(triple: &str) -> Option<&'static TargetInfo>248 pub fn get_builtin_target_by_triple(triple: &str) -> Option<&'static TargetInfo> {
249 ALL_BUILTINS
250 .binary_search_by(|ti| ti.triple.as_ref().cmp(triple))
251 .map(|i| &ALL_BUILTINS[i])
252 .ok()
253 }
254
255 /// Retrieves the version of rustc for which the built-in targets were
256 /// retrieved from. Targets may be added and removed between different rustc
257 /// versions.
258 ///
259 /// ```
260 /// assert_eq!("1.59.0", cfg_expr::targets::rustc_version());
261 /// ```
rustc_version() -> &'static str262 pub fn rustc_version() -> &'static str {
263 builtins::RUSTC_VERSION
264 }
265
266 #[cfg(test)]
267 mod test {
268 use crate::targets::get_builtin_target_by_triple;
269 use std::collections::{BTreeSet, HashSet};
270
271 // rustc's target-list is currently sorted lexicographically
272 // by the target-triple, so ensure that stays the case
273 #[test]
targets_are_sorted()274 fn targets_are_sorted() {
275 for window in super::ALL_BUILTINS.windows(2) {
276 assert!(window[0].triple < window[1].triple);
277 }
278 }
279
280 // Ensure our workaround for https://github.com/rust-lang/rust/issues/36156
281 // still functions
282 #[test]
has_ios()283 fn has_ios() {
284 assert_eq!(
285 8,
286 super::ALL_BUILTINS
287 .iter()
288 .filter(|ti| ti.os == Some(super::Os::ios))
289 .count()
290 );
291 }
292
293 // Ensure that TargetInfo can be used as keys for btree and hash-based data structures.
294 #[test]
set_map_key()295 fn set_map_key() {
296 let target_info =
297 get_builtin_target_by_triple("x86_64-unknown-linux-gnu").expect("known target");
298
299 let mut btree_set = BTreeSet::new();
300 btree_set.insert(target_info);
301
302 let mut hash_set = HashSet::new();
303 hash_set.insert(target_info);
304 }
305
306 #[test]
family_comp()307 fn family_comp() {
308 let a = super::Families::new([super::Family::unix, super::Family::wasm]);
309 let b = super::Families::new([super::Family::wasm, super::Family::unix]);
310
311 assert_eq!(a, b);
312 }
313 }
314