waterui_cli/apple/
platform.rs

1//! Apple platform implementations for macOS, iOS, iOS Simulator, etc.
2
3use std::path::PathBuf;
4use std::{env, fmt::Write};
5
6use color_eyre::eyre::{self, bail};
7use smol::fs;
8use target_lexicon::{
9    Aarch64Architecture, Architecture, DefaultToHost, Environment, OperatingSystem, Triple, Vendor,
10};
11
12use crate::{
13    apple::{
14        backend::AppleBackend,
15        device::{AppleDevice, AppleSimulator},
16        toolchain::{AppleSdk, AppleToolchain, Xcode},
17    },
18    build::{BuildOptions, RustBuild},
19    device::Artifact,
20    platform::{PackageOptions, Platform},
21    project::Project,
22    utils::{copy_file, run_command},
23};
24
25// ============================================================================
26// Common Apple Platform Trait
27// ============================================================================
28
29/// Trait for Apple-specific platform functionality.
30///
31/// This trait provides Apple-specific methods that are shared across all Apple platforms.
32/// Each platform type (`MacOS`, Ios, `IosSimulator`, etc.) implements this trait.
33pub trait ApplePlatformExt: Platform {
34    /// Get the SDK name for xcodebuild (e.g., "macosx", "iphoneos", "iphonesimulator").
35    fn sdk_name(&self) -> &'static str;
36
37    /// Check if this platform is a simulator.
38    fn is_simulator(&self) -> bool;
39
40    /// Get the architecture for this platform.
41    fn arch(&self) -> Architecture;
42}
43
44// ============================================================================
45// Shared Implementation Helpers
46// ============================================================================
47
48/// Build Rust library for an Apple platform.
49async fn build_rust_lib(
50    project: &Project,
51    triple: Triple,
52    options: BuildOptions,
53) -> eyre::Result<PathBuf> {
54    let build = RustBuild::new(project.root(), triple, options.is_hot_reload());
55    let lib_dir = build.build_lib(options.is_release()).await?;
56
57    // If output_dir is specified, copy the library there
58    if let Some(output_dir) = options.output_dir() {
59        let lib_name = project.crate_name().replace('-', "_");
60        let source_lib = lib_dir.join(format!("lib{lib_name}.a"));
61
62        if source_lib.exists() {
63            fs::create_dir_all(output_dir).await?;
64            let dest_lib = output_dir.join("libwaterui_app.a");
65            copy_file(&source_lib, &dest_lib).await?;
66        }
67    }
68
69    Ok(lib_dir)
70}
71
72async fn validate_local_apple_backend(project: &Project) -> eyre::Result<()> {
73    let Some(waterui_path) = project.manifest().waterui_path.as_deref() else {
74        return Ok(());
75    };
76
77    let waterui_root = {
78        let candidate = PathBuf::from(waterui_path);
79        if candidate.is_absolute() {
80            candidate
81        } else {
82            project.root().join(candidate)
83        }
84    };
85
86    let package_manifest = waterui_root.join("backends/apple/Package.swift");
87    if package_manifest.exists() {
88        return Ok(());
89    }
90
91    let gitmodules_path = waterui_root.join(".gitmodules");
92    let submodule_hint = if gitmodules_path.exists() {
93        fs::read_to_string(&gitmodules_path)
94            .await
95            .ok()
96            .filter(|c| c.contains("backends/apple"))
97            .map(|_| {
98                format!(
99                    "It looks like `backends/apple` is a git submodule; run `git submodule update --init --recursive` in `{}`.",
100                    waterui_root.display()
101                )
102            })
103    } else {
104        None
105    };
106
107    let mut message = format!(
108        "Local Apple backend Swift package manifest not found at `{}`.\n\
109         This is typically caused by an incomplete local WaterUI checkout (e.g. missing submodules) or an incorrect `waterui_path` in `Water.toml`.\n",
110        package_manifest.display()
111    );
112
113    if let Some(hint) = submodule_hint {
114        writeln!(&mut message, "{hint}\n").unwrap();
115    } else {
116        writeln!(
117            &mut message,
118            "If you're using a local WaterUI checkout, ensure `backends/apple/` exists and contains `Package.swift`."
119        ).unwrap();
120    }
121
122    bail!("{message}");
123}
124
125/// Clean Xcode build artifacts for an Apple platform.
126async fn clean_apple(project: &Project) -> eyre::Result<()> {
127    let Some(backend) = project.apple_backend() else {
128        return Ok(()); // Nothing to clean if no backend configured
129    };
130
131    let project_path = project.backend_path::<AppleBackend>();
132    let xcodeproj = project_path.join(format!("{}.xcodeproj", backend.scheme));
133
134    if !xcodeproj.exists() {
135        return Ok(());
136    }
137
138    run_command(
139        "xcodebuild",
140        [
141            "-project",
142            xcodeproj.to_str().unwrap_or_default(),
143            "-scheme",
144            &backend.scheme,
145            "clean",
146        ],
147    )
148    .await?;
149
150    let build_dir = project_path.join("build");
151    if build_dir.exists() {
152        fs::remove_dir_all(&build_dir).await?;
153    }
154
155    Ok(())
156}
157
158/// Package an Apple app using xcodebuild.
159async fn package_apple<P: ApplePlatformExt>(
160    platform: &P,
161    project: &Project,
162    options: PackageOptions,
163) -> eyre::Result<Artifact> {
164    let backend = project
165        .apple_backend()
166        .ok_or_else(|| eyre::eyre!("Apple backend must be configured"))?;
167
168    let project_path = project.backend_path::<AppleBackend>();
169    let xcodeproj = project_path.join(format!("{}.xcodeproj", backend.scheme));
170
171    if !xcodeproj.exists() {
172        bail!(
173            "Xcode project not found at {}. Did you run 'water create'?",
174            xcodeproj.display()
175        );
176    }
177
178    validate_local_apple_backend(project).await?;
179
180    // Tell Xcode not to call `water build` again (we already built)
181    // SAFETY: CLI runs on main thread before spawning build processes
182    unsafe {
183        env::set_var("WATERUI_SKIP_RUST_BUILD", "1");
184    }
185
186    let configuration = if options.is_debug() {
187        "Debug"
188    } else {
189        "Release"
190    };
191
192    let derived_data = project_path.join(".water/DerivedData");
193
194    let target_dir = project.target_dir();
195
196    // Copy the built Rust library to where Xcode expects it
197    let profile = if options.is_debug() {
198        "debug"
199    } else {
200        "release"
201    };
202    let lib_dir = target_dir.join(platform.triple().to_string()).join(profile);
203    let lib_name = project.crate_name().replace('-', "_");
204    let source_lib = lib_dir.join(format!("lib{lib_name}.a"));
205
206    // Xcode uses "Debug-iphonesimulator" for simulators, "Debug" for macOS
207    let products_config = if platform.sdk_name() == "macosx" {
208        configuration.to_string()
209    } else {
210        format!("{configuration}-{}", platform.sdk_name())
211    };
212    let products_dir = derived_data.join("Build/Products").join(&products_config);
213    fs::create_dir_all(&products_dir).await?;
214    let dest_lib = products_dir.join("libwaterui_app.a");
215    copy_file(&source_lib, &dest_lib).await?;
216
217    // Build with xcodebuild
218    // Determine the Xcode arch name from the platform architecture
219    let arch_name = match platform.arch() {
220        Architecture::Aarch64(_) => "arm64",
221        Architecture::X86_64 => "x86_64",
222        _ => unimplemented!(),
223    };
224    let archs_arg = format!("ARCHS={arch_name}");
225
226    let mut args = vec![
227        "-project",
228        xcodeproj.to_str().unwrap_or_default(),
229        "-scheme",
230        &backend.scheme,
231        "-configuration",
232        configuration,
233        "-sdk",
234        platform.sdk_name(),
235        "-derivedDataPath",
236        derived_data.to_str().unwrap_or_default(),
237        &archs_arg,
238        "ONLY_ACTIVE_ARCHITECTURE=YES",
239        "build",
240    ];
241
242    if platform.is_simulator() || options.is_debug() {
243        args.extend([
244            "CODE_SIGNING_ALLOWED=NO",
245            "CODE_SIGNING_REQUIRED=NO",
246            "CODE_SIGN_IDENTITY=-",
247        ]);
248    }
249
250    run_command("xcodebuild", args.iter().copied()).await?;
251
252    // Reset the environment variable
253    unsafe {
254        env::set_var("WATERUI_SKIP_RUST_BUILD", "0");
255    }
256
257    let app_path = products_dir.join(format!("{}.app", backend.scheme));
258
259    if !app_path.exists() {
260        bail!(
261            "Built app not found at {}. Check xcodebuild output for errors.",
262            app_path.display()
263        );
264    }
265
266    Ok(Artifact::new(project.bundle_identifier(), app_path))
267}
268
269// ============================================================================
270// macOS Platform
271// ============================================================================
272
273/// macOS platform for building and running on the current Mac.
274#[derive(Debug, Clone, Copy, Default)]
275pub struct MacOS;
276
277impl MacOS {
278    /// Create a new macOS platform instance.
279    #[must_use]
280    pub const fn new() -> Self {
281        Self
282    }
283}
284
285impl Platform for MacOS {
286    type Device = AppleDevice;
287    type Toolchain = AppleToolchain;
288
289    async fn scan(&self) -> eyre::Result<Vec<Self::Device>> {
290        // macOS doesn't have simulators to scan
291        Ok(vec![])
292    }
293
294    async fn build(&self, project: &Project, options: BuildOptions) -> eyre::Result<PathBuf> {
295        build_rust_lib(project, self.triple(), options).await
296    }
297
298    fn toolchain(&self) -> Self::Toolchain {
299        (Xcode, AppleSdk::Macos)
300    }
301
302    fn triple(&self) -> Triple {
303        Triple {
304            architecture: self.arch(),
305            vendor: Vendor::Apple,
306            operating_system: OperatingSystem::Darwin(None),
307            environment: Environment::Unknown,
308            binary_format: target_lexicon::BinaryFormat::Macho,
309        }
310    }
311
312    async fn clean(&self, project: &Project) -> eyre::Result<()> {
313        clean_apple(project).await
314    }
315
316    async fn package(&self, project: &Project, options: PackageOptions) -> eyre::Result<Artifact> {
317        package_apple(self, project, options).await
318    }
319}
320
321impl ApplePlatformExt for MacOS {
322    fn sdk_name(&self) -> &'static str {
323        "macosx"
324    }
325
326    fn is_simulator(&self) -> bool {
327        false
328    }
329
330    fn arch(&self) -> Architecture {
331        DefaultToHost::default().0.architecture
332    }
333}
334
335// ============================================================================
336// iOS Platform (Physical Device)
337// ============================================================================
338
339/// iOS platform for building and running on physical iOS devices.
340#[derive(Debug, Clone, Copy, Default)]
341pub struct Ios;
342
343impl Ios {
344    /// Create a new iOS platform instance.
345    #[must_use]
346    pub const fn new() -> Self {
347        Self
348    }
349}
350
351impl Platform for Ios {
352    type Device = AppleDevice;
353    type Toolchain = AppleToolchain;
354
355    async fn scan(&self) -> eyre::Result<Vec<Self::Device>> {
356        // TODO: Scan for physical iOS devices
357        Ok(vec![])
358    }
359
360    async fn build(&self, project: &Project, options: BuildOptions) -> eyre::Result<PathBuf> {
361        build_rust_lib(project, self.triple(), options).await
362    }
363
364    fn toolchain(&self) -> Self::Toolchain {
365        (Xcode, AppleSdk::Ios)
366    }
367
368    fn triple(&self) -> Triple {
369        Triple {
370            architecture: Architecture::Aarch64(Aarch64Architecture::Aarch64),
371            vendor: Vendor::Apple,
372            operating_system: OperatingSystem::IOS(None),
373            environment: Environment::Unknown,
374            binary_format: target_lexicon::BinaryFormat::Macho,
375        }
376    }
377
378    async fn clean(&self, project: &Project) -> eyre::Result<()> {
379        clean_apple(project).await
380    }
381
382    async fn package(&self, project: &Project, options: PackageOptions) -> eyre::Result<Artifact> {
383        package_apple(self, project, options).await
384    }
385}
386
387impl ApplePlatformExt for Ios {
388    fn sdk_name(&self) -> &'static str {
389        "iphoneos"
390    }
391
392    fn is_simulator(&self) -> bool {
393        false
394    }
395
396    fn arch(&self) -> Architecture {
397        Architecture::Aarch64(Aarch64Architecture::Aarch64)
398    }
399}
400
401// ============================================================================
402// iOS Simulator Platform
403// ============================================================================
404
405/// iOS Simulator platform for building and running on the iOS Simulator.
406#[derive(Debug, Clone, Copy, Default)]
407pub struct IosSimulator;
408
409impl IosSimulator {
410    /// Create a new iOS Simulator platform instance.
411    #[must_use]
412    pub const fn new() -> Self {
413        Self
414    }
415}
416
417impl Platform for IosSimulator {
418    type Device = AppleDevice;
419    type Toolchain = AppleToolchain;
420
421    async fn scan(&self) -> eyre::Result<Vec<Self::Device>> {
422        let simulators = AppleSimulator::scan().await?;
423
424        let filtered: Vec<AppleDevice> = simulators
425            .into_iter()
426            .filter(|sim| {
427                // Filter to only iOS simulators (iPhone, iPad)
428                !sim.device_type_identifier.contains("Apple-TV")
429                    && !sim.device_type_identifier.contains("Apple-Watch")
430                    && !sim.device_type_identifier.contains("Apple-Vision")
431            })
432            .map(AppleDevice::Simulator)
433            .collect();
434
435        Ok(filtered)
436    }
437
438    async fn build(&self, project: &Project, options: BuildOptions) -> eyre::Result<PathBuf> {
439        build_rust_lib(project, self.triple(), options).await
440    }
441
442    fn toolchain(&self) -> Self::Toolchain {
443        (Xcode, AppleSdk::Ios)
444    }
445
446    fn triple(&self) -> Triple {
447        let arch = self.arch();
448        let env = match arch {
449            Architecture::X86_64 => Environment::Unknown,
450            _ => Environment::Sim,
451        };
452
453        Triple {
454            architecture: arch,
455            vendor: Vendor::Apple,
456            operating_system: OperatingSystem::IOS(None),
457            environment: env,
458            binary_format: target_lexicon::BinaryFormat::Macho,
459        }
460    }
461
462    async fn clean(&self, project: &Project) -> eyre::Result<()> {
463        clean_apple(project).await
464    }
465
466    async fn package(&self, project: &Project, options: PackageOptions) -> eyre::Result<Artifact> {
467        package_apple(self, project, options).await
468    }
469}
470
471impl ApplePlatformExt for IosSimulator {
472    fn sdk_name(&self) -> &'static str {
473        "iphonesimulator"
474    }
475
476    fn is_simulator(&self) -> bool {
477        true
478    }
479
480    fn arch(&self) -> Architecture {
481        DefaultToHost::default().0.architecture
482    }
483}
484
485// ============================================================================
486// Legacy ApplePlatform (for backwards compatibility)
487// ============================================================================
488
489/// Legacy Apple platform enum for backwards compatibility.
490///
491/// This struct wraps the platform kind and delegates to the appropriate
492/// platform implementation. New code should use `MacOS`, `Ios`, or `IosSimulator` directly.
493#[derive(Debug, Clone)]
494pub struct ApplePlatform {
495    arch: Architecture,
496    kind: ApplePlatformKind,
497}
498
499/// Apple platform kinds.
500#[derive(Debug, Clone, Copy, PartialEq, Eq)]
501pub enum ApplePlatformKind {
502    /// macOS
503    MacOS,
504    /// iOS (physical device)
505    Ios,
506    /// iOS Simulator
507    IosSimulator,
508    /// tvOS (physical device)
509    TvOs,
510    /// tvOS Simulator
511    TvOsSimulator,
512    /// watchOS (physical device)
513    WatchOs,
514    /// watchOS Simulator
515    WatchOsSimulator,
516    /// visionOS (physical device)
517    VisionOs,
518    /// visionOS Simulator
519    VisionOsSimulator,
520}
521
522impl ApplePlatform {
523    /// Create a new Apple platform with the specified architecture and kind.
524    #[must_use]
525    pub const fn new(arch: Architecture, kind: ApplePlatformKind) -> Self {
526        Self { arch, kind }
527    }
528
529    /// Create an Apple platform for the current macOS host.
530    #[must_use]
531    pub fn macos() -> Self {
532        Self {
533            arch: DefaultToHost::default().0.architecture,
534            kind: ApplePlatformKind::MacOS,
535        }
536    }
537
538    /// Create an Apple platform for iOS (physical device).
539    #[must_use]
540    pub const fn ios() -> Self {
541        Self {
542            arch: Architecture::Aarch64(Aarch64Architecture::Aarch64),
543            kind: ApplePlatformKind::Ios,
544        }
545    }
546
547    /// Create an Apple platform for iOS Simulator (native architecture).
548    #[must_use]
549    pub fn ios_simulator() -> Self {
550        Self {
551            arch: DefaultToHost::default().0.architecture,
552            kind: ApplePlatformKind::IosSimulator,
553        }
554    }
555
556    /// Create an Apple platform for iOS Simulator on ARM64 (Apple Silicon).
557    #[must_use]
558    pub const fn ios_simulator_arm64() -> Self {
559        Self {
560            arch: Architecture::Aarch64(Aarch64Architecture::Aarch64),
561            kind: ApplePlatformKind::IosSimulator,
562        }
563    }
564
565    /// Create an Apple platform for iOS Simulator on `x86_64` (Intel).
566    #[must_use]
567    pub const fn ios_simulator_x86_64() -> Self {
568        Self {
569            arch: Architecture::X86_64,
570            kind: ApplePlatformKind::IosSimulator,
571        }
572    }
573
574    /// Create an Apple platform for macOS on ARM64 (Apple Silicon).
575    #[must_use]
576    pub const fn macos_arm64() -> Self {
577        Self {
578            arch: Architecture::Aarch64(Aarch64Architecture::Aarch64),
579            kind: ApplePlatformKind::MacOS,
580        }
581    }
582
583    /// Create an Apple platform for macOS on `x86_64` (Intel).
584    #[must_use]
585    pub const fn macos_x86_64() -> Self {
586        Self {
587            arch: Architecture::X86_64,
588            kind: ApplePlatformKind::MacOS,
589        }
590    }
591
592    /// Parse a device type identifier to determine the platform.
593    #[must_use]
594    pub fn from_device_type_identifier(id: &str) -> Self {
595        let is_simulator = id.contains("CoreSimulator");
596        let arch = if is_simulator {
597            DefaultToHost::default().0.architecture
598        } else {
599            Architecture::Aarch64(Aarch64Architecture::Aarch64)
600        };
601
602        let kind = if id.contains("Apple-TV") {
603            if is_simulator {
604                ApplePlatformKind::TvOsSimulator
605            } else {
606                ApplePlatformKind::TvOs
607            }
608        } else if id.contains("Apple-Watch") {
609            if is_simulator {
610                ApplePlatformKind::WatchOsSimulator
611            } else {
612                ApplePlatformKind::WatchOs
613            }
614        } else if id.contains("Apple-Vision") {
615            if is_simulator {
616                ApplePlatformKind::VisionOsSimulator
617            } else {
618                ApplePlatformKind::VisionOs
619            }
620        } else if id.contains("Mac") {
621            ApplePlatformKind::MacOS
622        } else if is_simulator {
623            ApplePlatformKind::IosSimulator
624        } else {
625            ApplePlatformKind::Ios
626        };
627
628        Self { arch, kind }
629    }
630
631    /// Get the platform kind.
632    #[must_use]
633    pub const fn kind(&self) -> &ApplePlatformKind {
634        &self.kind
635    }
636
637    /// Get the architecture.
638    #[must_use]
639    pub const fn arch(&self) -> &Architecture {
640        &self.arch
641    }
642
643    /// Get the SDK name for xcodebuild.
644    #[must_use]
645    pub const fn sdk_name(&self) -> &'static str {
646        match self.kind {
647            ApplePlatformKind::MacOS => "macosx",
648            ApplePlatformKind::Ios => "iphoneos",
649            ApplePlatformKind::IosSimulator => "iphonesimulator",
650            ApplePlatformKind::TvOs => "appletvos",
651            ApplePlatformKind::TvOsSimulator => "appletvsimulator",
652            ApplePlatformKind::WatchOs => "watchos",
653            ApplePlatformKind::WatchOsSimulator => "watchsimulator",
654            ApplePlatformKind::VisionOs => "xros",
655            ApplePlatformKind::VisionOsSimulator => "xrsimulator",
656        }
657    }
658
659    /// Check if this platform is a simulator.
660    #[must_use]
661    pub const fn is_simulator(&self) -> bool {
662        matches!(
663            self.kind,
664            ApplePlatformKind::IosSimulator
665                | ApplePlatformKind::TvOsSimulator
666                | ApplePlatformKind::WatchOsSimulator
667                | ApplePlatformKind::VisionOsSimulator
668        )
669    }
670}
671
672impl Platform for ApplePlatform {
673    type Device = AppleDevice;
674    type Toolchain = AppleToolchain;
675
676    async fn scan(&self) -> eyre::Result<Vec<Self::Device>> {
677        let simulators = AppleSimulator::scan().await?;
678
679        let filtered: Vec<AppleDevice> = simulators
680            .into_iter()
681            .filter(|sim| {
682                let sim_platform = Self::from_device_type_identifier(&sim.device_type_identifier);
683                matches!(
684                    (&self.kind, &sim_platform.kind),
685                    (
686                        ApplePlatformKind::IosSimulator,
687                        ApplePlatformKind::IosSimulator
688                    ) | (
689                        ApplePlatformKind::TvOsSimulator,
690                        ApplePlatformKind::TvOsSimulator
691                    ) | (
692                        ApplePlatformKind::WatchOsSimulator,
693                        ApplePlatformKind::WatchOsSimulator
694                    ) | (
695                        ApplePlatformKind::VisionOsSimulator,
696                        ApplePlatformKind::VisionOsSimulator,
697                    )
698                )
699            })
700            .map(AppleDevice::Simulator)
701            .collect();
702
703        Ok(filtered)
704    }
705
706    async fn build(&self, project: &Project, options: BuildOptions) -> eyre::Result<PathBuf> {
707        build_rust_lib(project, self.triple(), options).await
708    }
709
710    fn toolchain(&self) -> Self::Toolchain {
711        let sdk = match self.kind {
712            ApplePlatformKind::MacOS => AppleSdk::Macos,
713            ApplePlatformKind::Ios | ApplePlatformKind::IosSimulator => AppleSdk::Ios,
714            ApplePlatformKind::TvOs | ApplePlatformKind::TvOsSimulator => AppleSdk::TvOs,
715            ApplePlatformKind::WatchOs | ApplePlatformKind::WatchOsSimulator => AppleSdk::WatchOs,
716            ApplePlatformKind::VisionOs | ApplePlatformKind::VisionOsSimulator => {
717                AppleSdk::VisionOs
718            }
719        };
720        (Xcode, sdk)
721    }
722
723    fn triple(&self) -> Triple {
724        let (os, env) = match self.kind {
725            ApplePlatformKind::MacOS => (OperatingSystem::Darwin(None), Environment::Unknown),
726            ApplePlatformKind::Ios => (OperatingSystem::IOS(None), Environment::Unknown),
727            ApplePlatformKind::IosSimulator => match self.arch {
728                Architecture::X86_64 => (OperatingSystem::IOS(None), Environment::Unknown),
729                _ => (OperatingSystem::IOS(None), Environment::Sim),
730            },
731            ApplePlatformKind::TvOs => (OperatingSystem::TvOS(None), Environment::Unknown),
732            ApplePlatformKind::TvOsSimulator => (OperatingSystem::TvOS(None), Environment::Sim),
733            ApplePlatformKind::WatchOs => (OperatingSystem::WatchOS(None), Environment::Unknown),
734            ApplePlatformKind::WatchOsSimulator => {
735                (OperatingSystem::WatchOS(None), Environment::Sim)
736            }
737            ApplePlatformKind::VisionOs => (OperatingSystem::VisionOS(None), Environment::Unknown),
738            ApplePlatformKind::VisionOsSimulator => {
739                (OperatingSystem::VisionOS(None), Environment::Sim)
740            }
741        };
742
743        Triple {
744            architecture: self.arch,
745            vendor: Vendor::Apple,
746            operating_system: os,
747            environment: env,
748            binary_format: target_lexicon::BinaryFormat::Macho,
749        }
750    }
751
752    async fn clean(&self, project: &Project) -> eyre::Result<()> {
753        clean_apple(project).await
754    }
755
756    async fn package(&self, project: &Project, options: PackageOptions) -> eyre::Result<Artifact> {
757        package_apple(self, project, options).await
758    }
759}
760
761impl ApplePlatformExt for ApplePlatform {
762    fn sdk_name(&self) -> &'static str {
763        self.sdk_name()
764    }
765
766    fn is_simulator(&self) -> bool {
767        self.is_simulator()
768    }
769
770    fn arch(&self) -> Architecture {
771        self.arch
772    }
773}