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