1use std::borrow::Cow;
7
8use cfg_expr::targets::{
9 Abi, Arch, Env, Families, Family, HasAtomic, HasAtomics, Os, TargetInfo, Triple, Vendor,
10};
11use serde::{Deserialize, Serialize};
12
13#[derive(Clone, Debug, Deserialize, Serialize, Eq, Hash, Ord, PartialEq, PartialOrd)]
14#[serde(rename_all = "kebab-case")]
15pub(crate) struct TargetDefinition {
16 arch: String,
24 #[serde(rename = "target-pointer-width", with = "target_pointer_width")]
25 pointer_width: u8,
26
27 #[allow(dead_code)]
30 llvm_target: String,
31 #[allow(dead_code)]
32 data_layout: String,
33
34 #[serde(default)]
36 os: Option<String>,
37 #[serde(default)]
38 abi: Option<String>,
39 #[serde(default)]
40 env: Option<String>,
41 #[serde(default)]
42 vendor: Option<String>,
43 #[serde(default)]
44 target_family: Vec<String>,
45 #[serde(default)]
46 target_endian: Endian,
47 #[serde(default)]
48 min_atomic_width: Option<u16>,
49 #[serde(default)]
50 max_atomic_width: Option<u16>,
51 #[serde(default)]
52 panic_strategy: PanicStrategy,
53}
54
55impl TargetDefinition {
56 pub(crate) fn into_target_info(self, triple: Cow<'static, str>) -> TargetInfo {
57 let min_atomic_width = self.min_atomic_width.unwrap_or(8);
60 let max_atomic_width = self.max_atomic_width.unwrap_or(self.pointer_width as u16);
62
63 let mut has_atomics = Vec::new();
64 let mut atomic_width = 8;
67 while atomic_width <= max_atomic_width {
68 if atomic_width < min_atomic_width {
69 atomic_width *= 2;
70 continue;
71 }
72 has_atomics.push(HasAtomic::IntegerSize(atomic_width));
73 if atomic_width == self.pointer_width as u16 {
74 has_atomics.push(HasAtomic::Pointer);
75 }
76 atomic_width *= 2;
77 }
78
79 TargetInfo {
80 triple: Triple::new(triple),
81 os: self.os.map(Os::new),
82 abi: self.abi.map(Abi::new),
83 arch: Arch::new(self.arch),
84 env: self.env.map(Env::new),
85 vendor: self.vendor.map(Vendor::new),
86 families: Families::new(self.target_family.into_iter().map(Family::new)),
87 pointer_width: self.pointer_width,
88 endian: self.target_endian.to_cfg_expr(),
89 has_atomics: HasAtomics::new(has_atomics),
90 panic: self.panic_strategy.to_cfg_expr(),
91 }
92 }
93}
94
95mod target_pointer_width {
96 use serde::{de::Error, Deserialize, Deserializer, Serializer};
97
98 pub(super) fn deserialize<'de, D>(deserializer: D) -> Result<u8, D::Error>
99 where
100 D: Deserializer<'de>,
101 {
102 let string = String::deserialize(deserializer)?;
104 string
105 .parse::<u8>()
106 .map_err(|error| D::Error::custom(format!("error parsing as integer: {error}")))
107 }
108
109 pub(super) fn serialize<S>(value: &u8, serializer: S) -> Result<S::Ok, S::Error>
110 where
111 S: Serializer,
112 {
113 serializer.serialize_str(&value.to_string())
114 }
115}
116
117#[derive(
118 Copy, Clone, Debug, Deserialize, Serialize, Default, Eq, Hash, Ord, PartialEq, PartialOrd,
119)]
120#[serde(rename_all = "kebab-case")]
121enum Endian {
122 #[default]
123 Little,
124 Big,
125}
126
127impl Endian {
128 fn to_cfg_expr(self) -> cfg_expr::targets::Endian {
129 match self {
130 Self::Little => cfg_expr::targets::Endian::little,
131 Self::Big => cfg_expr::targets::Endian::big,
132 }
133 }
134}
135
136#[derive(
137 Copy, Clone, Debug, Deserialize, Serialize, Default, Eq, Hash, Ord, PartialEq, PartialOrd,
138)]
139#[serde(rename_all = "kebab-case")]
140enum PanicStrategy {
141 #[default]
142 Unwind,
143 Abort,
144}
145
146impl PanicStrategy {
147 fn to_cfg_expr(self) -> cfg_expr::targets::Panic {
148 match self {
149 Self::Unwind => cfg_expr::targets::Panic::unwind,
150 Self::Abort => cfg_expr::targets::Panic::abort,
151 }
152 }
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158 use std::{collections::BTreeMap, process::Command};
159
160 #[derive(Deserialize)]
161 #[serde(transparent)]
162 struct AllTargets(BTreeMap<String, TargetDefinition>);
163
164 #[test]
165 fn test_all_builtin_specs_recognized() {
166 let rustc_bin: String = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_owned());
167 let output = Command::new(rustc_bin)
168 .env("RUSTC_BOOTSTRAP", "1")
170 .args(["-Z", "unstable-options", "--print", "all-target-specs-json"])
171 .output()
172 .expect("rustc command succeeded");
173 assert!(output.status.success(), "rustc command succeeded");
174
175 let all_targets: AllTargets = serde_json::from_slice(&output.stdout)
176 .expect("deserializing all-target-specs-json succeeded");
177 for (triple, target_def) in all_targets.0 {
178 eprintln!("*** testing {triple}");
179 target_def.clone().into_target_info(triple.clone().into());
182 let json =
183 serde_json::to_string(&target_def).expect("target def serialized successfully");
184 eprintln!("* minified json: {json}");
185 let target_def_2 = serde_json::from_str(&json).expect("target def 2 deserialized");
186 assert_eq!(target_def, target_def_2, "matches");
187
188 if triple.starts_with("powerpc-") || triple.starts_with("powerpc64-") {
190 assert_eq!(
191 target_def.target_endian,
192 Endian::Big,
193 "powerpc is big-endian"
194 );
195 }
196 if triple.contains("-linux") {
197 assert!(
198 target_def.target_family.contains(&"unix".to_owned()),
199 "linux target_family should contain unix (was {:#?})",
200 target_def.target_family,
201 );
202 }
203 }
204 }
205}