rust_target_feature_data/lib.rs
1//! Data about Rust target features.
2//!
3//! Different versions of Rust support different [target
4//! features](https://rust-lang.github.io/rfcs/2045-target-feature.html). Different Rust targets
5//! automatically enable different target features. Different versions of Rust automatically enable
6//! different target features _for the same target_. Enabling the same target feature implies
7// enabling different target features in different versions of Rust.
8//!
9//! This crate provides target feature data for all targets covering Rust versions:
10//!
11//! * `"1.85.0"` (1.85.1 is identical)
12//! * `"1.86.0"`
13//! * `"1.87.0"` (from 1.87.0-beta.5)
14//!
15//! Rust 1.88.0 provides target feature data for the selected target [via `rustdoc`'s JSON output
16//! format](https://docs.rs/rustdoc-types/latest/rustdoc_types/struct.TargetFeature.html), making
17//! this crate obsolete going forward.
18//!
19//! # Example
20//!
21//! ```
22//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
23//! use rust_target_feature_data::find;
24//!
25//! let features: Vec<_> = find("1.85.0", "i686-linux-android")?.collect();
26//! let fxsr = features.iter().find(|f| f.name == "fxsr").unwrap();
27//! assert_eq!(fxsr.globally_enabled, false);
28//!
29//! let features: Vec<_> = find("1.86.0", "i686-linux-android")?.collect();
30//! let fxsr = features.iter().find(|f| f.name == "fxsr").unwrap();
31//! assert_eq!(fxsr.globally_enabled, true);
32//! # Ok(()) }
33//! ```
34
35use std::collections::BTreeSet;
36
37#[rustfmt::skip]
38mod generated;
39
40/// Information about a target feature.
41///
42/// Rust target features are used to influence code generation, especially around selecting
43/// instructions which are not universally supported by the target architecture.
44///
45/// Target features are commonly enabled by the [`#[target_feature]` attribute][1] to influence code
46/// generation for a particular function, and less commonly enabled by compiler options like
47/// `-Ctarget-feature` or `-Ctarget-cpu`. Targets themselves automatically enable certain target
48/// features by default, for example because the target's ABI specification requires saving specific
49/// registers which only exist in an architectural extension.
50///
51/// Target features can imply other target features: for example, x86-64 `avx2` implies `avx`, and
52/// aarch64 `sve2` implies `sve`, since both of these architectural extensions depend on their
53/// predecessors.
54///
55/// Target features can be probed at compile time by [`#[cfg(target_feature)]`][2] or `cfg!(…)`
56/// conditional compilation to determine whether a target feature is enabled in a particular
57/// context.
58///
59/// [1]: https://doc.rust-lang.org/stable/reference/attributes/codegen.html#the-target_feature-attribute
60/// [2]: https://doc.rust-lang.org/reference/conditional-compilation.html#target_feature
61#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
62pub struct TargetFeature {
63 /// The name of this target feature.
64 pub name: &'static str,
65 /// Other target features which are implied by this target feature, if any.
66 pub implies_features: BTreeSet<&'static str>,
67 /// If this target feature is unstable, the name of the associated language feature gate.
68 pub unstable_feature_gate: Option<&'static str>,
69 /// Whether this feature is globally enabled by default.
70 ///
71 /// Target features can be globally enabled implicitly as a result of the target's definition.
72 /// For example, x86-64 hardware floating point ABIs require saving x87 and SSE2 registers,
73 /// which in turn requires globally enabling the `x87` and `sse2` target features so that the
74 /// generated machine code conforms to the target's ABI.
75 ///
76 /// Target features can also be globally enabled explicitly as a result of compiler flags like
77 /// [`-Ctarget-feature`][1] or [`-Ctarget-cpu`][2].
78 ///
79 /// [1]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-feature
80 /// [2]: https://doc.rust-lang.org/beta/rustc/codegen-options/index.html#target-cpu
81 pub globally_enabled: bool,
82}
83
84/// An error finding target feature data.
85#[derive(Debug, Clone, Eq, PartialEq)]
86pub enum NotFoundError {
87 /// The compiler version was not found
88 CompilerNotFound(String),
89 /// The compiler was found but the target was not
90 TargetNotFound(String),
91}
92
93impl std::error::Error for NotFoundError {}
94
95impl std::fmt::Display for NotFoundError {
96 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
97 match self {
98 NotFoundError::CompilerNotFound(name) => {
99 write!(f, "compiler version {:?} not found", name)
100 }
101 NotFoundError::TargetNotFound(name) => {
102 write!(f, "target {:?} not found", name)
103 }
104 }
105 }
106}
107
108/// Find the target features applicable to a Rust version and target.
109///
110/// ```
111/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
112/// use rust_target_feature_data::NotFoundError;
113///
114/// // Different versions return different features
115/// for (version, count) in [
116/// // ("1.83.0", 90),
117/// // ("1.84.0", 91),
118/// ("1.85.0", 92),
119/// ("1.86.0", 92),
120/// ("1.87.0", 92),
121/// ] {
122/// assert_eq!(
123/// rust_target_feature_data::find(version, "aarch64-apple-darwin")?.count(),
124/// count,
125/// );
126/// }
127///
128/// // 1.84.0 data is not included
129/// assert_eq!(
130/// rust_target_feature_data::find("1.84.0", "x86_64-unknown-linux-gnu").err().unwrap(),
131/// NotFoundError::CompilerNotFound("1.84.0".into())
132/// );
133///
134/// // i686-unknown-redox became i586-unknown-redox
135/// assert!(
136/// rust_target_feature_data::find("1.85.0", "i686-unknown-redox").is_ok()
137/// );
138/// assert_eq!(
139/// rust_target_feature_data::find("1.86.0", "i686-unknown-redox").err().unwrap(),
140/// NotFoundError::TargetNotFound("i686-unknown-redox".into())
141/// );
142/// # Ok(()) }
143/// ```
144pub fn find(
145 rust_version: &str,
146 target: &str,
147) -> Result<impl Iterator<Item = TargetFeature>, NotFoundError> {
148 let mut targets = generated::all()
149 .find(|(version, _)| *version == rust_version)
150 .ok_or_else(|| NotFoundError::CompilerNotFound(rust_version.into()))?
151 .1;
152
153 targets
154 .find(|(name, _)| *name == target)
155 .map(|(_, features)| features)
156 .ok_or_else(|| NotFoundError::TargetNotFound(target.into()))
157}
158
159#[cfg(test)]
160mod tests;