Skip to main content

platforms/
platform.rs

1//! Rust platforms
2
3mod platforms;
4
5#[cfg(feature = "std")]
6mod req;
7mod tier;
8
9pub use self::tier::Tier;
10
11#[cfg(feature = "std")]
12pub use self::req::PlatformReq;
13
14use self::platforms::ALL;
15use crate::target::*;
16use core::fmt;
17
18/// Rust platforms supported by mainline rustc
19///
20/// Sourced from <https://doc.rust-lang.org/nightly/rustc/platform-support.html>
21/// as well as the latest nightly version of `rustc`
22#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
23#[non_exhaustive]
24pub struct Platform {
25    /// "Target triple" string uniquely identifying the platform. See:
26    /// <https://github.com/rust-lang/rfcs/blob/master/text/0131-target-specification.md>
27    ///
28    /// These are defined in the `rustc_target` crate of the Rust compiler:
29    /// <https://github.com/rust-lang/rust/blob/master/src/librustc_target/spec/mod.rs>
30    pub target_triple: &'static str,
31
32    /// Target architecture `cfg` attribute (i.e. `cfg(target_arch)`)
33    pub target_arch: Arch,
34
35    /// Target OS `cfg` attribute (i.e. `cfg(target_os)`).
36    pub target_os: OS,
37
38    /// Target environment `cfg` attribute (i.e. `cfg(target_env)`).
39    /// Only used when needed for disambiguation, e.g. on many GNU platforms
40    /// this value will be `None`.
41    pub target_env: Env,
42
43    /// Target pointer width `cfg` attribute, in bits (i.e. `cfg(target_pointer_width)`).
44    /// Typically 64 on modern platforms, 32 on older platforms, 16 on some microcontrollers.
45    pub target_pointer_width: PointerWidth,
46
47    /// Target [endianness](https://en.wikipedia.org/wiki/Endianness) `cfg` attribute (i.e. `cfg(target_endian)`).
48    /// Set to "little" on the vast majority of modern platforms.
49    pub target_endian: Endian,
50
51    /// Tier of this platform:
52    ///
53    /// - `Tier::One`: guaranteed to work
54    /// - `Tier::Two`: guaranteed to build
55    /// - `Tier::Three`: unofficially supported with no guarantees
56    pub tier: Tier,
57}
58
59impl Platform {
60    /// All valid Rust platforms usable from the mainline compiler.
61    ///
62    /// Note that this list will evolve over time, and platforms will be both added and removed.
63    pub const ALL: &'static [Platform] = ALL;
64
65    /// Find a Rust platform by its "target triple", e.g. `i686-apple-darwin`
66    pub fn find(target_triple: &str) -> Option<&'static Platform> {
67        Self::ALL
68            .iter()
69            .find(|platform| platform.target_triple == target_triple)
70    }
71}
72
73impl fmt::Display for Platform {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        f.write_str(self.target_triple)
76    }
77}
78
79#[cfg(all(test, feature = "std"))]
80mod tests {
81    use super::Platform;
82    use std::collections::HashSet;
83
84    /// Ensure there are no duplicate target triples in the platforms list
85    #[test]
86    fn no_dupes_test() {
87        let mut target_triples = HashSet::new();
88
89        for platform in Platform::ALL {
90            assert!(
91                target_triples.insert(platform.target_triple),
92                "duplicate target triple: {}",
93                platform.target_triple
94            );
95        }
96    }
97
98    use std::collections::HashMap;
99
100    use super::*;
101
102    /// `platforms` v2.0 used to provide various constants passed as `cfg` values,
103    /// and attempted to detect the target triple based on that.
104    /// This test is meant to check whether such detection can be accurate.
105    ///
106    /// Turns out that as of v3.0 this is infeasible,
107    /// even though the list of supported cfg values was expanded.
108    ///
109    /// I have also verified that no possible expansion of the supported cfg fields
110    /// will lets uniquely identify the platform based on cfg values using a shell script:
111    /// `rustc --print=target-list | parallel 'rustc --print=cfg --target={} > ./{}'; fdupes`
112    #[test]
113    #[ignore]
114    fn test_detection_feasibility() {
115        let mut all_platforms = HashMap::new();
116        for p in ALL {
117            if let Some(other_p) = all_platforms.insert(
118                (
119                    p.target_arch,
120                    p.target_os,
121                    p.target_env,
122                    p.target_endian,
123                    p.target_pointer_width,
124                ),
125                p.target_triple,
126            ) {
127                panic!("{} and {} have identical properties, and cannot be distinguished based on properties alone", p.target_triple, other_p);
128            }
129        }
130    }
131}