1 | use crate::error::{HasAtomicParseError, 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 "abi" field |
15 | #[derive (Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
16 | pub struct Abi(pub Cow<'static, str>); |
17 | |
18 | /// The "architecture" field |
19 | #[derive (Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] |
20 | pub 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)] |
24 | pub 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)] |
29 | pub 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)] |
34 | pub 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)] |
40 | pub 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)] |
44 | pub struct Panic(pub Cow<'static, str>); |
45 | |
46 | macro_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 | |
85 | field_impls!(Triple); |
86 | field_impls!(Abi); |
87 | field_impls!(Arch); |
88 | field_impls!(Vendor); |
89 | field_impls!(Os); |
90 | field_impls!(Family); |
91 | field_impls!(Env); |
92 | field_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 ] |
97 | pub 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 | |
106 | impl 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 | |
122 | impl 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)] |
135 | pub struct Families(Cow<'static, [Family]>); |
136 | |
137 | impl 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 | |
167 | impl Deref for Families { |
168 | type Target = [Family]; |
169 | fn deref(&self) -> &Self::Target { |
170 | &self.0 |
171 | } |
172 | } |
173 | |
174 | impl AsRef<[Family]> for Families { |
175 | #[inline ] |
176 | fn as_ref(&self) -> &[Family] { |
177 | &self.0 |
178 | } |
179 | } |
180 | |
181 | impl 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)] |
197 | pub struct HasAtomics(Cow<'static, [HasAtomic]>); |
198 | |
199 | impl 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 | |
227 | impl Deref for HasAtomics { |
228 | type Target = [HasAtomic]; |
229 | fn deref(&self) -> &Self::Target { |
230 | &self.0 |
231 | } |
232 | } |
233 | |
234 | impl AsRef<[HasAtomic]> for HasAtomics { |
235 | #[inline ] |
236 | fn as_ref(&self) -> &[HasAtomic] { |
237 | &self.0 |
238 | } |
239 | } |
240 | |
241 | impl 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 | |
255 | macro_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 | |
285 | macro_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 | |
306 | target_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)] |
317 | pub 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 | /// ``` |
363 | pub 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 | /// ``` |
377 | pub fn rustc_version() -> &'static str { |
378 | builtins::RUSTC_VERSION |
379 | } |
380 | |
381 | #[cfg (test)] |
382 | mod 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 | |