use std::str::FromStr;
use target_lexicon::{
Aarch64Architecture, Architecture, DefaultToHost, Environment, OperatingSystem,
Riscv32Architecture, Triple, Vendor,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TargetPlatform {
MacOS,
IOS,
IOSSimulator,
TvOS,
TvOSSimulator,
WatchOS,
WatchOSSimulator,
VisionOS,
VisionOSSimulator,
Android,
Linux,
Windows,
Web,
Esp32S3,
Esp32C3,
Esp32P4,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum TargetBackend {
Apple,
Android,
Gtk4,
Hydrolysis,
Dew,
}
impl TargetPlatform {
#[must_use]
pub fn triple(&self) -> Triple {
match self {
Self::MacOS => Triple {
architecture: DefaultToHost::default().0.architecture,
vendor: Vendor::Apple,
operating_system: OperatingSystem::Darwin(None),
environment: Environment::Unknown,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::IOS => Triple {
architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
vendor: Vendor::Apple,
operating_system: OperatingSystem::IOS(None),
environment: Environment::Unknown,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::IOSSimulator => {
let arch = DefaultToHost::default().0.architecture;
let env = match arch {
Architecture::X86_64 => Environment::Unknown,
_ => Environment::Sim,
};
Triple {
architecture: arch,
vendor: Vendor::Apple,
operating_system: OperatingSystem::IOS(None),
environment: env,
binary_format: target_lexicon::BinaryFormat::Macho,
}
}
Self::TvOS => Triple {
architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
vendor: Vendor::Apple,
operating_system: OperatingSystem::TvOS(None),
environment: Environment::Unknown,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::TvOSSimulator => Triple {
architecture: DefaultToHost::default().0.architecture,
vendor: Vendor::Apple,
operating_system: OperatingSystem::TvOS(None),
environment: Environment::Sim,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::WatchOS => Triple {
architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
vendor: Vendor::Apple,
operating_system: OperatingSystem::WatchOS(None),
environment: Environment::Unknown,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::WatchOSSimulator => Triple {
architecture: DefaultToHost::default().0.architecture,
vendor: Vendor::Apple,
operating_system: OperatingSystem::WatchOS(None),
environment: Environment::Sim,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::VisionOS => Triple {
architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
vendor: Vendor::Apple,
operating_system: OperatingSystem::VisionOS(None),
environment: Environment::Unknown,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::VisionOSSimulator => Triple {
architecture: DefaultToHost::default().0.architecture,
vendor: Vendor::Apple,
operating_system: OperatingSystem::VisionOS(None),
environment: Environment::Sim,
binary_format: target_lexicon::BinaryFormat::Macho,
},
Self::Android => Triple {
architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
vendor: Vendor::Unknown,
operating_system: OperatingSystem::Linux,
environment: Environment::Android,
binary_format: target_lexicon::BinaryFormat::Elf,
},
Self::Linux | Self::Windows => Triple::host(),
Self::Web => Triple::from_str("wasm32-unknown-unknown")
.expect("web target triple must remain valid"),
Self::Esp32S3 => Triple::from_str("xtensa-esp32s3-espidf")
.expect("esp32s3 target triple must remain valid"),
Self::Esp32C3 => Triple::from_str("riscv32imc-esp-espidf")
.expect("esp32c3 target triple must remain valid"),
Self::Esp32P4 => Triple::from_str("riscv32imafc-esp-espidf")
.expect("esp32p4 target triple must remain valid"),
}
}
#[must_use]
pub const fn available_backends(&self) -> &[TargetBackend] {
match self {
Self::MacOS => &[TargetBackend::Apple, TargetBackend::Hydrolysis],
Self::IOS
| Self::IOSSimulator
| Self::TvOS
| Self::TvOSSimulator
| Self::WatchOS
| Self::WatchOSSimulator
| Self::VisionOS
| Self::VisionOSSimulator => &[TargetBackend::Apple],
Self::Android => &[TargetBackend::Android],
Self::Linux => &[TargetBackend::Gtk4, TargetBackend::Hydrolysis],
Self::Windows | Self::Web => &[TargetBackend::Hydrolysis],
Self::Esp32S3 | Self::Esp32C3 | Self::Esp32P4 => &[TargetBackend::Dew],
}
}
#[must_use]
pub const fn default_backend(&self) -> TargetBackend {
match self {
Self::MacOS
| Self::IOS
| Self::IOSSimulator
| Self::TvOS
| Self::TvOSSimulator
| Self::WatchOS
| Self::WatchOSSimulator
| Self::VisionOS
| Self::VisionOSSimulator => TargetBackend::Apple,
Self::Android => TargetBackend::Android,
Self::Linux => TargetBackend::Gtk4,
Self::Windows | Self::Web => TargetBackend::Hydrolysis,
Self::Esp32S3 | Self::Esp32C3 | Self::Esp32P4 => TargetBackend::Dew,
}
}
#[must_use]
pub const fn is_simulator(&self) -> bool {
matches!(
self,
Self::IOSSimulator
| Self::TvOSSimulator
| Self::WatchOSSimulator
| Self::VisionOSSimulator
)
}
#[must_use]
pub const fn sdk_name(&self) -> Option<&'static str> {
match self {
Self::MacOS => Some("macosx"),
Self::IOS => Some("iphoneos"),
Self::IOSSimulator => Some("iphonesimulator"),
Self::TvOS => Some("appletvos"),
Self::TvOSSimulator => Some("appletvsimulator"),
Self::WatchOS => Some("watchos"),
Self::WatchOSSimulator => Some("watchsimulator"),
Self::VisionOS => Some("xros"),
Self::VisionOSSimulator => Some("xrsimulator"),
Self::Android
| Self::Linux
| Self::Windows
| Self::Web
| Self::Esp32S3
| Self::Esp32C3
| Self::Esp32P4 => None,
}
}
#[must_use]
pub fn arch(&self) -> Architecture {
match self {
Self::MacOS
| Self::IOSSimulator
| Self::TvOSSimulator
| Self::WatchOSSimulator
| Self::VisionOSSimulator
| Self::Linux
| Self::Windows => DefaultToHost::default().0.architecture,
Self::IOS | Self::TvOS | Self::WatchOS | Self::VisionOS | Self::Android => {
Architecture::Aarch64(Aarch64Architecture::Aarch64)
}
Self::Web => Architecture::Wasm32,
Self::Esp32S3 => Architecture::XTensa,
Self::Esp32C3 => Architecture::Riscv32(Riscv32Architecture::Riscv32imc),
Self::Esp32P4 => Architecture::Riscv32(Riscv32Architecture::Riscv32imafc),
}
}
}
#[derive(Debug, Clone)]
pub struct PackageOptions {
distribution: bool,
debug: bool,
shared_rust_runtime: bool,
}
impl PackageOptions {
#[must_use]
pub const fn development() -> Self {
Self {
distribution: false,
debug: true,
shared_rust_runtime: true,
}
}
#[must_use]
pub const fn packaging(distribution: bool, debug: bool) -> Self {
Self {
distribution,
debug,
shared_rust_runtime: false,
}
}
#[must_use]
pub const fn is_distribution(&self) -> bool {
self.distribution
}
#[must_use]
pub const fn is_debug(&self) -> bool {
self.debug
}
#[must_use]
pub const fn uses_shared_rust_runtime(&self) -> bool {
self.shared_rust_runtime
}
}
#[cfg(test)]
mod package_options_tests {
use super::PackageOptions;
#[test]
fn development_embeds_shared_runtime_and_packaging_does_not() {
let development = PackageOptions::development();
assert!(development.is_debug());
assert!(!development.is_distribution());
assert!(development.uses_shared_rust_runtime());
for options in [
PackageOptions::packaging(false, true),
PackageOptions::packaging(true, false),
] {
assert!(!options.uses_shared_rust_runtime());
}
}
}