1use crate::error::{HasAtomicParseError, Reason};
2use std::{borrow::Cow, ops::Deref};
3
4mod 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
8pub use builtins::ALL_BUILTINS;
9
10/// The unique identifier for a target.
11#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
12pub struct Triple(pub Cow<'static, str>);
13
14/// The "abi" field
15#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
16pub struct Abi(pub Cow<'static, str>);
17
18/// The "architecture" field
19#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
20pub struct Arch(pub Cow<'static, str>);
21
22/// The "vendor" field, which in practice is little more than an arbitrary modifier.
23#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
24pub struct Vendor(pub Cow<'static, str>);
25
26/// The "operating system" field, which sometimes implies an environment, and
27/// sometimes isn't an actual operating system.
28#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
29pub struct Os(pub Cow<'static, str>);
30
31/// Individual target families, which describe a set of targets grouped in some logical manner,
32/// typically by operating system. This includes values like `unix` and `windows`.
33#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
34pub struct Family(pub Cow<'static, str>);
35
36/// The "environment" field, which specifies an ABI environment on top of the
37/// operating system. In many configurations, this field is omitted, and the
38/// environment is implied by the operating system.
39#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
40pub struct Env(pub Cow<'static, str>);
41
42/// The panic strategy used on this target by default.
43#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
44pub struct Panic(pub Cow<'static, str>);
45
46macro_rules! field_impls {
47 ($kind:ident) => {
48 impl $kind {
49 /// Constructs a new instance of this field.
50 ///
51 /// This method accepts both owned `String`s and `&'static str`s.
52 #[inline]
53 pub fn new(val: impl Into<Cow<'static, str>>) -> Self {
54 Self(val.into())
55 }
56
57 /// Constructs a new instance of this field from a `&'static str`.
58 #[inline]
59 pub const fn new_const(val: &'static str) -> Self {
60 Self(Cow::Borrowed(val))
61 }
62
63 /// Returns the string for the field.
64 #[inline]
65 pub fn as_str(&self) -> &str {
66 &*self.0
67 }
68 }
69
70 impl AsRef<str> for $kind {
71 #[inline]
72 fn as_ref(&self) -> &str {
73 &*self.0
74 }
75 }
76
77 impl std::fmt::Display for $kind {
78 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
79 f.write_str(self.as_str())
80 }
81 }
82 };
83}
84
85field_impls!(Triple);
86field_impls!(Abi);
87field_impls!(Arch);
88field_impls!(Vendor);
89field_impls!(Os);
90field_impls!(Family);
91field_impls!(Env);
92field_impls!(Panic);
93
94/// Integer size and pointers for which there's support for atomic functions.
95#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
96#[non_exhaustive]
97pub enum HasAtomic {
98 /// The platform supports atomics for the given integer size in bits (e.g. `AtomicU8` if
99 /// `HasAtomic::IntegerSize(8)`).
100 IntegerSize(u16),
101
102 /// The platform supports atomics for pointers (`AtomicPtr`).
103 Pointer,
104}
105
106impl std::str::FromStr for HasAtomic {
107 type Err = HasAtomicParseError;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 if let Ok(size: u16) = s.parse::<u16>() {
111 Ok(Self::IntegerSize(size))
112 } else if s == "ptr" {
113 Ok(HasAtomic::Pointer)
114 } else {
115 Err(HasAtomicParseError {
116 input: s.to_owned(),
117 })
118 }
119 }
120}
121
122impl std::fmt::Display for HasAtomic {
123 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 match self {
125 Self::IntegerSize(size: &u16) => write!(f, "{size}"),
126 Self::Pointer => write!(f, "ptr"),
127 }
128 }
129}
130
131/// A set of families for a target.
132///
133/// Each target can be part of one or more families. This struct represents them.
134#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
135pub struct Families(Cow<'static, [Family]>);
136
137impl Families {
138 /// Constructs a new instance.
139 ///
140 /// This method accepts both owned `String`s and `&'static str`s.
141 ///
142 /// If you have a `&'static [&'static str]`, prefer [`Self::new_const`].
143 #[inline]
144 pub fn new(val: impl IntoIterator<Item = Family>) -> Self {
145 let mut fams: Vec<_> = val.into_iter().collect();
146 fams.sort_unstable();
147 Self(Cow::Owned(fams))
148 }
149
150 /// Constructs a new instance of this field from a static slice of `&'static str`.
151 ///
152 /// `val` must be in sorted order: this constructor cannot check for that due to
153 /// limitations in current versions of Rust.
154 #[inline]
155 pub const fn new_const(val: &'static [Family]) -> Self {
156 // TODO: Check that val is sorted.
157 Self(Cow::Borrowed(val))
158 }
159
160 /// Returns true if this list of families contains a given family.
161 #[inline]
162 pub fn contains(&self, val: &Family) -> bool {
163 self.0.contains(val)
164 }
165}
166
167impl Deref for Families {
168 type Target = [Family];
169 fn deref(&self) -> &Self::Target {
170 &self.0
171 }
172}
173
174impl AsRef<[Family]> for Families {
175 #[inline]
176 fn as_ref(&self) -> &[Family] {
177 &self.0
178 }
179}
180
181impl std::fmt::Display for Families {
182 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
183 write!(f, "{{")?;
184 let len: usize = self.0.len();
185 for (idx: usize, family: &Family) in self.0.iter().enumerate() {
186 write!(f, "{family}")?;
187 if idx + 1 < len {
188 write!(f, ", ")?;
189 }
190 }
191 write!(f, "}}")
192 }
193}
194
195/// A set of [`HasAtomic`] instances a target.
196#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
197pub struct HasAtomics(Cow<'static, [HasAtomic]>);
198
199impl HasAtomics {
200 /// Constructs a new instance.
201 ///
202 /// If you have a `&'static [HasAtomic]`, prefer [`Self::new_const`].
203 #[inline]
204 pub fn new(val: impl IntoIterator<Item = HasAtomic>) -> Self {
205 let mut has_atomics: Vec<_> = val.into_iter().collect();
206 has_atomics.sort_unstable();
207 Self(Cow::Owned(has_atomics))
208 }
209
210 /// Constructs a new instance of this struct from a static slice of [`HasAtomic`].
211 ///
212 /// `val` must be in sorted order: this constructor cannot check for that due to
213 /// limitations in current versions of Rust.
214 #[inline]
215 pub const fn new_const(val: &'static [HasAtomic]) -> Self {
216 // TODO: Check that val is sorted.
217 Self(Cow::Borrowed(val))
218 }
219
220 /// Returns true if this list of families contains a given family.
221 #[inline]
222 pub fn contains(&self, val: HasAtomic) -> bool {
223 self.0.contains(&val)
224 }
225}
226
227impl Deref for HasAtomics {
228 type Target = [HasAtomic];
229 fn deref(&self) -> &Self::Target {
230 &self.0
231 }
232}
233
234impl AsRef<[HasAtomic]> for HasAtomics {
235 #[inline]
236 fn as_ref(&self) -> &[HasAtomic] {
237 &self.0
238 }
239}
240
241impl std::fmt::Display for HasAtomics {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 write!(f, "{{")?;
244 let len: usize = self.0.len();
245 for (idx: usize, has_atomic: &HasAtomic) in self.0.iter().enumerate() {
246 write!(f, "{has_atomic}")?;
247 if idx + 1 < len {
248 write!(f, ", ")?;
249 }
250 }
251 write!(f, "}}")
252 }
253}
254
255macro_rules! target_enum {
256 (
257 $(#[$outer:meta])*
258 pub enum $kind:ident {
259 $(
260 $(#[$inner:ident $($args:tt)*])*
261 $name:ident $(= $value:expr)?,
262 )+
263 }
264 ) => {
265 $(#[$outer])*
266 #[allow(non_camel_case_types)]
267 pub enum $kind {
268 $(
269 $(#[$inner $($args)*])*
270 $name $(= $value)?,
271 )+
272 }
273
274 impl_from_str! {
275 $kind {
276 $(
277 $(#[$inner $($args)*])*
278 $name $(= $value)?,
279 )+
280 }
281 }
282 };
283}
284
285macro_rules! impl_from_str {
286 (
287 $kind:ident {
288 $(
289 $(#[$attr:ident $($args:tt)*])*
290 $name:ident $(= $value:expr)?,
291 )+
292 }
293 ) => {
294 impl std::str::FromStr for $kind {
295 type Err = Reason;
296 fn from_str(s: &str) -> Result<Self, Self::Err> {
297 match s {
298 $(stringify!($name) => Ok(Self::$name),)+
299 _ => Err(Reason::Unexpected(&[$(stringify!($name),)+])),
300 }
301 }
302 }
303 };
304}
305
306target_enum! {
307 /// The endian types known to rustc
308 #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
309 pub enum Endian {
310 big,
311 little,
312 }
313}
314
315/// Contains information regarding a particular target known to rustc
316#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
317pub struct TargetInfo {
318 /// The target's unique identifier
319 pub triple: Triple,
320 /// The target's operating system, if any. Used by the
321 /// [target_os](https://doc.rust-lang.org/reference/conditional-compilation.html#target_os)
322 /// predicate.
323 pub os: Option<Os>,
324 /// The target's ABI, if any. Used by the
325 /// [target_abi](https://github.com/rust-lang/rust/issues/80970) predicate.
326 pub abi: Option<Abi>,
327 /// The target's CPU architecture. Used by the
328 /// [target_arch](https://doc.rust-lang.org/reference/conditional-compilation.html#target_arch)
329 /// predicate.
330 pub arch: Arch,
331 /// The target's ABI/libc used, if any. Used by the
332 /// [target_env](https://doc.rust-lang.org/reference/conditional-compilation.html#target_env)
333 /// predicate.
334 pub env: Option<Env>,
335 /// The target's vendor, if any. Used by the
336 /// [target_vendor](https://doc.rust-lang.org/reference/conditional-compilation.html#target_vendor)
337 /// predicate.
338 pub vendor: Option<Vendor>,
339 /// The target's families, if any. Used by the
340 /// [target_family](https://doc.rust-lang.org/reference/conditional-compilation.html#target_family)
341 /// predicate.
342 pub families: Families,
343 /// The size of the target's pointer type. Used by the
344 /// [target_pointer_width](https://doc.rust-lang.org/reference/conditional-compilation.html#target_pointer_width)
345 /// predicate.
346 pub pointer_width: u8,
347 /// The target's endianness. Used by the
348 /// [target_endian](https://doc.rust-lang.org/reference/conditional-compilation.html#target_endian)
349 /// predicate.
350 pub endian: Endian,
351 /// The target's support for atomics. Used by the has_target_atomics predicate.
352 pub has_atomics: HasAtomics,
353 /// The panic strategy used on this target by default. Used by the
354 /// [panic](https://doc.rust-lang.org/beta/reference/conditional-compilation.html#panic) predicate.
355 pub panic: Panic,
356}
357
358/// Attempts to find the `TargetInfo` for the specified target triple
359///
360/// ```
361/// assert!(cfg_expr::targets::get_builtin_target_by_triple("x86_64-unknown-linux-musl").is_some());
362/// ```
363pub fn get_builtin_target_by_triple(triple: &str) -> Option<&'static TargetInfo> {
364 ALL_BUILTINS
365 .binary_search_by(|ti| ti.triple.as_ref().cmp(triple))
366 .map(|i: usize| &ALL_BUILTINS[i])
367 .ok()
368}
369
370/// Retrieves the version of rustc for which the built-in targets were
371/// retrieved from. Targets may be added and removed between different rustc
372/// versions.
373///
374/// ```
375/// assert_eq!("1.76.0", cfg_expr::targets::rustc_version());
376/// ```
377pub fn rustc_version() -> &'static str {
378 builtins::RUSTC_VERSION
379}
380
381#[cfg(test)]
382mod test {
383 use crate::targets::get_builtin_target_by_triple;
384 use std::collections::{BTreeSet, HashSet};
385
386 // rustc's target-list is currently sorted lexicographically
387 // by the target-triple, so ensure that stays the case
388 #[test]
389 fn targets_are_sorted() {
390 for window in super::ALL_BUILTINS.windows(2) {
391 assert!(window[0].triple < window[1].triple);
392 }
393 }
394
395 // Ensure our workaround for https://github.com/rust-lang/rust/issues/36156
396 // still functions
397 #[test]
398 fn has_ios() {
399 assert_eq!(
400 8,
401 super::ALL_BUILTINS
402 .iter()
403 .filter(|ti| ti.os == Some(super::Os::ios))
404 .count()
405 );
406 }
407
408 // Ensure that TargetInfo can be used as keys for btree and hash-based data structures.
409 #[test]
410 fn set_map_key() {
411 let target_info =
412 get_builtin_target_by_triple("x86_64-unknown-linux-gnu").expect("known target");
413
414 let mut btree_set = BTreeSet::new();
415 btree_set.insert(target_info);
416
417 let mut hash_set = HashSet::new();
418 hash_set.insert(target_info);
419 }
420
421 #[test]
422 fn family_comp() {
423 let a = super::Families::new([super::Family::unix, super::Family::wasm]);
424 let b = super::Families::new([super::Family::wasm, super::Family::unix]);
425
426 assert_eq!(a, b);
427 }
428}
429