Skip to main content

lamina_platform/
target.rs

1//! Target architecture and operating system definitions.
2
3use 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    /// Detect the host system's target.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use lamina_platform::Target;
43    /// let host = Target::detect_host();
44    /// println!("Host target: {}", host);
45    /// ```
46    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}