1use std::{fmt, str::FromStr};
4
5use crate::detection::{detect_host_architecture_only, detect_host_os};
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Target {
9 pub architecture: TargetArchitecture,
10 pub operating_system: TargetOperatingSystem,
11}
12
13impl Target {
14 pub fn new(architecture: TargetArchitecture, operating_system: TargetOperatingSystem) -> Self {
15 Self {
16 architecture,
17 operating_system,
18 }
19 }
20
21 pub fn gpu_triple(&self) -> Option<&'static str> {
22 match (self.architecture, self.operating_system) {
23 (TargetArchitecture::Nvptx64, TargetOperatingSystem::Cuda) => {
24 Some("nvptx64-nvidia-cuda")
25 }
26 (TargetArchitecture::Amdgcn, TargetOperatingSystem::AmdHsa) => {
27 Some("amdgcn-amd-amdhsa")
28 }
29 _ => None,
30 }
31 }
32
33 pub fn to_str(&self) -> String {
34 format!("{}_{}", self.architecture, self.operating_system)
35 }
36
37 pub fn detect_host() -> Self {
47 let arch = detect_host_architecture_only();
48 let os = detect_host_os();
49 Self::new(
50 TargetArchitecture::from_str(arch).unwrap_or(TargetArchitecture::Unknown),
51 TargetOperatingSystem::from_str(os).unwrap_or(TargetOperatingSystem::Unknown),
52 )
53 }
54}
55
56impl FromStr for Target {
57 type Err = &'static str;
58
59 fn from_str(target: &str) -> Result<Self, Self::Err> {
60 let parts: Vec<&str> = target.rsplitn(2, '_').collect();
61 if parts.len() != 2 {
62 return Err("Invalid target format: expected 'architecture_os'");
63 }
64 Ok(Self::new(
65 TargetArchitecture::from_str(parts[1]).map_err(|_| "Invalid architecture")?,
66 TargetOperatingSystem::from_str(parts[0]).map_err(|_| "Invalid operating system")?,
67 ))
68 }
69}
70
71impl fmt::Display for Target {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73 write!(f, "{}_{}", self.architecture, self.operating_system)
74 }
75}
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum TargetArchitecture {
79 X86_64,
80 Aarch64,
81 Arx64,
82 Arm32,
83 Riscv32,
84 Riscv64,
85 #[cfg(feature = "nightly")]
86 Riscv128,
87 Wasm32,
88 Wasm64,
89 PowerPC64,
90 Nvptx64,
91 Amdgcn,
92 #[cfg(feature = "nightly")]
93 Lisa,
94 Unknown,
95}
96
97impl FromStr for TargetArchitecture {
98 type Err = &'static str;
99
100 fn from_str(s: &str) -> Result<Self, Self::Err> {
101 match s {
102 "nvptx64" => Ok(Self::Nvptx64),
103 "amdgcn" => Ok(Self::Amdgcn),
104 "x86_64" => Ok(Self::X86_64),
105 "aarch64" => Ok(Self::Aarch64),
106 "arx64" => Ok(Self::Arx64),
107 "arm32" => Ok(Self::Arm32),
108 "riscv32" => Ok(Self::Riscv32),
109 "riscv64" => Ok(Self::Riscv64),
110 #[cfg(feature = "nightly")]
111 "riscv128" => Ok(Self::Riscv128),
112 "wasm32" => Ok(Self::Wasm32),
113 "wasm64" => Ok(Self::Wasm64),
114 "wasm" => Ok(Self::Wasm32),
115 "ppc64" | "ppc64le" => Ok(Self::PowerPC64),
116 #[cfg(feature = "nightly")]
117 "lisa" => Ok(Self::Lisa),
118 _ => Err("Unknown architecture"),
119 }
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124pub enum TargetOperatingSystem {
125 Cuda,
126 AmdHsa,
127 Linux,
128 MacOS,
129 Windows,
130 Android,
131 IOS,
132 TvOS,
133 WatchOS,
134 VisionOS,
135 FreeBSD,
136 OpenBSD,
137 NetBSD,
138 DragonFly,
139 Redox,
140 Horizon,
141 Psp,
142 Psx,
143 Vita,
144 Orbis,
145 Prospero,
146 #[cfg(feature = "nightly")]
147 Artery,
148 Unknown,
149}
150
151impl FromStr for TargetOperatingSystem {
152 type Err = &'static str;
153
154 fn from_str(s: &str) -> Result<Self, Self::Err> {
155 match s {
156 "cuda" => Ok(Self::Cuda),
157 "amdhsa" => Ok(Self::AmdHsa),
158 "linux" => Ok(Self::Linux),
159 "macos" => Ok(Self::MacOS),
160 "windows" => Ok(Self::Windows),
161 "android" => Ok(Self::Android),
162 "ios" => Ok(Self::IOS),
163 "tvos" => Ok(Self::TvOS),
164 "watchos" => Ok(Self::WatchOS),
165 "visionos" => Ok(Self::VisionOS),
166 "freebsd" => Ok(Self::FreeBSD),
167 "openbsd" => Ok(Self::OpenBSD),
168 "netbsd" => Ok(Self::NetBSD),
169 "dragonfly" => Ok(Self::DragonFly),
170 "redox" => Ok(Self::Redox),
171 "horizon" => Ok(Self::Horizon),
172 "psp" => Ok(Self::Psp),
173 "psx" => Ok(Self::Psx),
174 "vita" => Ok(Self::Vita),
175 "orbis" | "ps4" => Ok(Self::Orbis),
176 "prospero" | "ps5" => Ok(Self::Prospero),
177 "unknown" => Ok(Self::Unknown),
178 #[cfg(feature = "nightly")]
179 "artery" => Ok(Self::Artery),
180 "bsd" => Ok(Self::FreeBSD),
181 _ => Err("Unknown operating system"),
182 }
183 }
184}
185
186impl fmt::Display for TargetArchitecture {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 let s = match self {
189 Self::Nvptx64 => "nvptx64",
190 Self::Amdgcn => "amdgcn",
191 Self::X86_64 => "x86_64",
192 Self::Aarch64 => "aarch64",
193 Self::Arx64 => "arx64",
194 Self::Arm32 => "arm32",
195 Self::Riscv32 => "riscv32",
196 Self::Riscv64 => "riscv64",
197 #[cfg(feature = "nightly")]
198 Self::Riscv128 => "riscv128",
199 Self::Wasm32 => "wasm32",
200 Self::Wasm64 => "wasm64",
201 Self::PowerPC64 => "ppc64le",
202 #[cfg(feature = "nightly")]
203 Self::Lisa => "lisa",
204 Self::Unknown => "unknown",
205 };
206 write!(f, "{s}")
207 }
208}
209
210impl fmt::Display for TargetOperatingSystem {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 let s = match self {
213 Self::Cuda => "cuda",
214 Self::AmdHsa => "amdhsa",
215 Self::Linux => "linux",
216 Self::MacOS => "macos",
217 Self::Windows => "windows",
218 Self::Android => "android",
219 Self::IOS => "ios",
220 Self::TvOS => "tvos",
221 Self::WatchOS => "watchos",
222 Self::VisionOS => "visionos",
223 Self::FreeBSD => "freebsd",
224 Self::OpenBSD => "openbsd",
225 Self::NetBSD => "netbsd",
226 Self::DragonFly => "dragonfly",
227 Self::Redox => "redox",
228 Self::Horizon => "horizon",
229 Self::Psp => "psp",
230 Self::Psx => "psx",
231 Self::Vita => "vita",
232 Self::Orbis => "orbis",
233 Self::Prospero => "prospero",
234 #[cfg(feature = "nightly")]
235 Self::Artery => "artery",
236 Self::Unknown => "unknown",
237 };
238 write!(f, "{s}")
239 }
240}
241
242pub const HOST_ARCH_LIST: &[&str] = &[
243 "nvptx64_cuda",
244 "amdgcn_amdhsa",
245 "x86_64_unknown",
246 "x86_64_linux",
247 "x86_64_windows",
248 "x86_64_macos",
249 "x86_64_android",
250 "x86_64_ios",
251 "x86_64_tvos",
252 "x86_64_watchos",
253 "aarch64_unknown",
254 "aarch64_macos",
255 "aarch64_linux",
256 "aarch64_windows",
257 "aarch64_android",
258 "aarch64_ios",
259 "aarch64_tvos",
260 "aarch64_watchos",
261 "aarch64_visionos",
262 "aarch64_horizon",
263 "arm32_android",
264 "arm32_ios",
265 "arm32_watchos",
266 "arm32_vita",
267 "x86_64_orbis",
268 "x86_64_prospero",
269 "arx64_unknown",
270 "wasm32_unknown",
271 "wasm64_unknown",
272 "riscv32_unknown",
273 "riscv64_unknown",
274 "ppc64_linux",
275 "ppc64_unknown",
276 #[cfg(feature = "nightly")]
277 "riscv128_unknown",
278];
279
280#[cfg(test)]
281mod tests {
282 use super::*;
283
284 #[test]
285 fn gpu_targets_round_trip() {
286 for (name, triple) in [
287 ("nvptx64_cuda", "nvptx64-nvidia-cuda"),
288 ("amdgcn_amdhsa", "amdgcn-amd-amdhsa"),
289 ] {
290 let target: Target = name.parse().unwrap();
291 assert_eq!(target.to_string(), name);
292 assert_eq!(target.gpu_triple(), Some(triple));
293 assert!(HOST_ARCH_LIST.contains(&name));
294 }
295 }
296
297 #[test]
298 fn parses_known_mobile_and_console_targets() {
299 assert_eq!(
300 Target::from_str("aarch64_ios").unwrap(),
301 Target::new(TargetArchitecture::Aarch64, TargetOperatingSystem::IOS)
302 );
303 assert_eq!(
304 Target::from_str("aarch64_horizon").unwrap(),
305 Target::new(TargetArchitecture::Aarch64, TargetOperatingSystem::Horizon)
306 );
307 assert_eq!(
308 Target::from_str("arm32_vita").unwrap(),
309 Target::new(TargetArchitecture::Arm32, TargetOperatingSystem::Vita)
310 );
311 }
312
313 #[test]
314 fn rejects_big_endian_powerpc64_name() {
315 assert!(Target::from_str("powerpc64_linux").is_err());
316 assert_eq!(
317 Target::from_str("ppc64le_linux").unwrap(),
318 Target::new(TargetArchitecture::PowerPC64, TargetOperatingSystem::Linux)
319 );
320 }
321
322 #[test]
323 fn displays_known_mobile_and_console_os_names() {
324 assert_eq!(TargetOperatingSystem::VisionOS.to_string(), "visionos");
325 assert_eq!(TargetOperatingSystem::Psp.to_string(), "psp");
326 assert_eq!(TargetOperatingSystem::Psx.to_string(), "psx");
327 }
328}