Skip to main content

alien_core/
build_targets.rs

1//! Cross-compilation build target types
2//!
3//! These types identify target OS/architecture combinations used by the open-source
4//! build system (alien-build) for cross-compilation.
5
6use serde::{Deserialize, Serialize};
7#[cfg(feature = "openapi")]
8use utoipa::ToSchema;
9
10/// Build strategy for cross-compilation.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum CargoBuildStrategy {
13    /// Plain `cargo build` — native toolchain, no cross-compilation.
14    Native,
15    /// `cargo zigbuild` — uses Zig as the C toolchain for Linux musl targets.
16    Zigbuild,
17    /// `cargo xwin build` — uses xwin to provide MSVC CRT/SDK for Windows targets from non-Windows hosts.
18    Xwin,
19}
20
21impl CargoBuildStrategy {
22    /// The cargo subcommand to use (e.g. "build", "zigbuild", "xwin").
23    pub fn cargo_subcommand(&self) -> &'static str {
24        match self {
25            Self::Native => "build",
26            Self::Zigbuild => "zigbuild",
27            Self::Xwin => "xwin",
28        }
29    }
30
31    /// Cargo args: for xwin, the subcommand is `cargo xwin build` (two args).
32    pub fn cargo_args(&self) -> Vec<&'static str> {
33        match self {
34            Self::Native => vec!["build"],
35            Self::Zigbuild => vec!["zigbuild"],
36            Self::Xwin => vec!["xwin", "build"],
37        }
38    }
39
40    /// Human-readable name for logging.
41    pub fn display_name(&self) -> &'static str {
42        match self {
43            Self::Native => "cargo build",
44            Self::Zigbuild => "cargo zigbuild",
45            Self::Xwin => "cargo xwin build",
46        }
47    }
48
49    /// The cargo tool binary to install (None for native builds).
50    pub fn install_package(&self) -> Option<&'static str> {
51        match self {
52            Self::Native => None,
53            Self::Zigbuild => Some("cargo-zigbuild"),
54            Self::Xwin => Some("cargo-xwin"),
55        }
56    }
57}
58
59/// Types of source binaries used for package building
60#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
61#[cfg_attr(feature = "openapi", derive(ToSchema))]
62#[serde(rename_all = "kebab-case")]
63pub enum SourceBinaryType {
64    /// alien-deploy binary
65    Cli,
66    /// alien-terraform binary
67    Terraform,
68    /// alien-agent binary
69    Agent,
70}
71
72impl SourceBinaryType {
73    /// Returns the binary filename (without extension)
74    pub fn binary_name(&self) -> &'static str {
75        match self {
76            SourceBinaryType::Cli => "alien-deploy",
77            SourceBinaryType::Terraform => "alien-terraform",
78            SourceBinaryType::Agent => "alien-agent",
79        }
80    }
81}
82
83impl std::fmt::Display for SourceBinaryType {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        match self {
86            SourceBinaryType::Cli => write!(f, "cli"),
87            SourceBinaryType::Terraform => write!(f, "terraform"),
88            SourceBinaryType::Agent => write!(f, "agent"),
89        }
90    }
91}
92
93/// Target OS and architecture for compiled binaries.
94///
95/// Used as keys in package output maps (CLI binaries, Terraform providers, etc.)
96/// and for cross-compilation target selection during builds.
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
98#[cfg_attr(feature = "openapi", derive(ToSchema))]
99#[serde(rename_all = "kebab-case")]
100pub enum BinaryTarget {
101    /// Windows x64 (x86_64-pc-windows-msvc)
102    WindowsX64,
103    /// Linux x86_64 (musl)
104    LinuxX64,
105    /// Linux ARM64 (musl)
106    LinuxArm64,
107    /// macOS ARM64 (Apple Silicon)
108    DarwinArm64,
109}
110
111impl BinaryTarget {
112    /// All supported binary targets
113    pub const ALL: &'static [BinaryTarget] = &[
114        BinaryTarget::WindowsX64,
115        BinaryTarget::LinuxX64,
116        BinaryTarget::LinuxArm64,
117        BinaryTarget::DarwinArm64,
118    ];
119
120    /// Linux-only targets (for container/operator builds)
121    pub const LINUX: &'static [BinaryTarget] = &[BinaryTarget::LinuxX64, BinaryTarget::LinuxArm64];
122
123    /// Get the Rust target triple for this platform
124    pub fn rust_target_triple(&self) -> &'static str {
125        match self {
126            Self::WindowsX64 => "x86_64-pc-windows-msvc",
127            Self::LinuxX64 => "x86_64-unknown-linux-musl",
128            Self::LinuxArm64 => "aarch64-unknown-linux-musl",
129            Self::DarwinArm64 => "aarch64-apple-darwin",
130        }
131    }
132
133    /// Returns the cargo subcommand and tool name needed to build for this target.
134    ///
135    /// - Linux musl: `cargo zigbuild` (zig provides the musl C toolchain)
136    /// - Windows MSVC from non-Windows: `cargo xwin build` (xwin provides MSVC CRT/SDK)
137    /// - Windows MSVC on Windows: `cargo build` (native MSVC toolchain)
138    /// - macOS on macOS: `cargo build` (native Apple toolchain)
139    pub fn cargo_build_strategy(&self) -> CargoBuildStrategy {
140        match self {
141            Self::LinuxX64 | Self::LinuxArm64 => CargoBuildStrategy::Zigbuild,
142            Self::WindowsX64 => {
143                if cfg!(target_os = "windows") {
144                    CargoBuildStrategy::Native
145                } else {
146                    CargoBuildStrategy::Xwin
147                }
148            }
149            Self::DarwinArm64 => CargoBuildStrategy::Native,
150        }
151    }
152
153    /// Get the binary extension for this platform
154    pub fn binary_extension(&self) -> &'static str {
155        match self {
156            Self::WindowsX64 => ".exe",
157            _ => "",
158        }
159    }
160
161    /// Get the platform identifier for runtime downloads (e.g., "linux-x64")
162    pub fn runtime_platform_id(&self) -> &'static str {
163        match self {
164            Self::WindowsX64 => "windows-x64",
165            Self::LinuxX64 => "linux-x64",
166            Self::LinuxArm64 => "linux-aarch64",
167            Self::DarwinArm64 => "darwin-aarch64",
168        }
169    }
170
171    /// Get the OCI os string for this target
172    pub fn oci_os(&self) -> &'static str {
173        match self {
174            Self::WindowsX64 => "windows",
175            Self::LinuxX64 | Self::LinuxArm64 => "linux",
176            Self::DarwinArm64 => "darwin",
177        }
178    }
179
180    /// Get the OCI architecture string for this target
181    pub fn oci_arch(&self) -> &'static str {
182        match self {
183            Self::WindowsX64 | Self::LinuxX64 => "amd64",
184            Self::LinuxArm64 | Self::DarwinArm64 => "arm64",
185        }
186    }
187
188    /// Get the Bun cross-compilation target for `bun build --compile --target`
189    pub fn bun_target(&self) -> &'static str {
190        match self {
191            Self::WindowsX64 => "bun-windows-x64",
192            Self::LinuxX64 => "bun-linux-x64",
193            Self::LinuxArm64 => "bun-linux-arm64",
194            Self::DarwinArm64 => "bun-darwin-arm64",
195        }
196    }
197
198    /// Terraform registry platform key (os_arch format)
199    pub fn terraform_key(&self) -> &'static str {
200        match self {
201            BinaryTarget::LinuxX64 => "linux_amd64",
202            BinaryTarget::LinuxArm64 => "linux_arm64",
203            BinaryTarget::DarwinArm64 => "darwin_arm64",
204            BinaryTarget::WindowsX64 => "windows_amd64",
205        }
206    }
207
208    /// Terraform OS string
209    pub fn terraform_os(&self) -> &'static str {
210        match self {
211            BinaryTarget::LinuxX64 | BinaryTarget::LinuxArm64 => "linux",
212            BinaryTarget::DarwinArm64 => "darwin",
213            BinaryTarget::WindowsX64 => "windows",
214        }
215    }
216
217    /// Terraform architecture string
218    pub fn terraform_arch(&self) -> &'static str {
219        match self {
220            BinaryTarget::LinuxX64 | BinaryTarget::WindowsX64 => "amd64",
221            BinaryTarget::LinuxArm64 | BinaryTarget::DarwinArm64 => "arm64",
222        }
223    }
224
225    /// Check if this target is a Darwin/macOS target
226    pub fn is_darwin(&self) -> bool {
227        matches!(self, Self::DarwinArm64)
228    }
229
230    /// Check if this is a Windows target
231    pub fn is_windows(&self) -> bool {
232        matches!(self, Self::WindowsX64)
233    }
234
235    /// Get the Linux container target matching the current host architecture.
236    /// Containers always run Linux (even on macOS via Docker's Linux VM),
237    /// so we map the host architecture to the corresponding Linux target.
238    pub fn linux_container_target() -> Self {
239        match Self::current_os() {
240            Self::DarwinArm64 | Self::LinuxArm64 => Self::LinuxArm64,
241            Self::LinuxX64 | Self::WindowsX64 => Self::LinuxX64,
242        }
243    }
244
245    /// Get all possible targets as a Vec
246    pub fn all() -> Vec<Self> {
247        Self::ALL.to_vec()
248    }
249
250    /// Get default targets for a platform
251    pub fn defaults_for_platform(platform: crate::Platform) -> Vec<Self> {
252        match platform {
253            crate::Platform::Aws => vec![Self::LinuxArm64],
254            crate::Platform::Gcp => vec![Self::LinuxX64],
255            crate::Platform::Azure => vec![Self::LinuxX64],
256            crate::Platform::Kubernetes => Self::LINUX.to_vec(),
257            crate::Platform::Local => vec![Self::current_os()],
258            crate::Platform::Test => vec![Self::LinuxX64],
259        }
260    }
261
262    /// Detect the current OS target
263    pub fn current_os() -> Self {
264        #[cfg(all(target_os = "windows", target_arch = "x86_64"))]
265        return Self::WindowsX64;
266
267        #[cfg(all(target_os = "linux", target_arch = "x86_64"))]
268        return Self::LinuxX64;
269
270        #[cfg(all(target_os = "linux", target_arch = "aarch64"))]
271        return Self::LinuxArm64;
272
273        #[cfg(all(target_os = "macos", target_arch = "aarch64"))]
274        return Self::DarwinArm64;
275
276        #[cfg(not(any(
277            all(target_os = "windows", target_arch = "x86_64"),
278            all(target_os = "linux", target_arch = "x86_64"),
279            all(target_os = "linux", target_arch = "aarch64"),
280            all(target_os = "macos", target_arch = "aarch64")
281        )))]
282        {
283            Self::LinuxX64
284        }
285    }
286}
287
288impl std::fmt::Display for BinaryTarget {
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        match self {
291            BinaryTarget::WindowsX64 => write!(f, "windows-x64"),
292            BinaryTarget::LinuxX64 => write!(f, "linux-x64"),
293            BinaryTarget::LinuxArm64 => write!(f, "linux-arm64"),
294            BinaryTarget::DarwinArm64 => write!(f, "darwin-arm64"),
295        }
296    }
297}
298
299impl std::str::FromStr for BinaryTarget {
300    type Err = String;
301
302    fn from_str(s: &str) -> Result<Self, Self::Err> {
303        match s {
304            "windows-x64" => Ok(BinaryTarget::WindowsX64),
305            "linux-x64" => Ok(BinaryTarget::LinuxX64),
306            "linux-arm64" => Ok(BinaryTarget::LinuxArm64),
307            "darwin-arm64" => Ok(BinaryTarget::DarwinArm64),
308            _ => Err(format!("Unknown binary target: {}", s)),
309        }
310    }
311}
312
313#[cfg(test)]
314mod tests {
315    use super::BinaryTarget;
316    use crate::Platform;
317
318    #[test]
319    fn local_platform_defaults_to_current_host_target() {
320        assert_eq!(
321            BinaryTarget::defaults_for_platform(Platform::Local),
322            vec![BinaryTarget::current_os()]
323        );
324    }
325
326    #[test]
327    fn cloud_platform_defaults_remain_stable() {
328        assert_eq!(
329            BinaryTarget::defaults_for_platform(Platform::Aws),
330            vec![BinaryTarget::LinuxArm64]
331        );
332        assert_eq!(
333            BinaryTarget::defaults_for_platform(Platform::Gcp),
334            vec![BinaryTarget::LinuxX64]
335        );
336        assert_eq!(
337            BinaryTarget::defaults_for_platform(Platform::Azure),
338            vec![BinaryTarget::LinuxX64]
339        );
340        assert_eq!(
341            BinaryTarget::defaults_for_platform(Platform::Kubernetes),
342            vec![BinaryTarget::LinuxX64, BinaryTarget::LinuxArm64]
343        );
344    }
345}