diff --git a/tooling/cli/Cargo.lock b/tooling/cli/Cargo.lock index 20e2d86e5..2d0cec6b2 100644 --- a/tooling/cli/Cargo.lock +++ b/tooling/cli/Cargo.lock @@ -293,7 +293,7 @@ checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" [[package]] name = "cargo-mobile" version = "0.1.0" -source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#909edf9337ec0cb42c76e0a34af61e99b15ccfe0" +source = "git+https://github.com/tauri-apps/cargo-mobile?branch=dev#ff88cc91fbf008ee0b9b33c8e4bcb5c9b5b743e6" dependencies = [ "cocoa", "colored 1.9.3", diff --git a/tooling/cli/Cargo.toml b/tooling/cli/Cargo.toml index a7c40d28c..f10c14a6c 100644 --- a/tooling/cli/Cargo.toml +++ b/tooling/cli/Cargo.toml @@ -28,7 +28,7 @@ name = "cargo-tauri" path = "src/main.rs" [dependencies] -#cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } +# cargo-mobile = { path = "../../../cargo-mobile/", default-features = false } cargo-mobile = { git = "https://github.com/tauri-apps/cargo-mobile", branch = "dev", default-features = false } textwrap = { version = "0.11.0", features = ["term_size"] } interprocess = "1" diff --git a/tooling/cli/src/mobile/android.rs b/tooling/cli/src/mobile/android.rs index a65ee2995..9b6a00632 100644 --- a/tooling/cli/src/mobile/android.rs +++ b/tooling/cli/src/mobile/android.rs @@ -209,7 +209,7 @@ fn emulator_prompt( 0 }; - Ok(emulator_list.iter().nth(index).cloned().unwrap()) + Ok(emulator_list.into_iter().nth(index).unwrap()) } } diff --git a/tooling/cli/src/mobile/ios.rs b/tooling/cli/src/mobile/ios.rs index f9c8d3cc1..0cc0e1a71 100644 --- a/tooling/cli/src/mobile/ios.rs +++ b/tooling/cli/src/mobile/ios.rs @@ -9,7 +9,7 @@ use cargo_mobile::{ Raw as RawAppleConfig, }, device::Device, - ios_deploy, + ios_deploy, simctl, target::Target, }, config::app::App, @@ -31,6 +31,11 @@ use crate::{ Result, }; +use std::{ + thread::{sleep, spawn}, + time::Duration, +}; + mod build; mod dev; mod open; @@ -121,19 +126,30 @@ fn with_config( f(&app, &config, &metadata, cli_options) } -fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError> { +fn ios_deploy_device_prompt<'a>( + env: &'_ Env, + target: Option<&str>, +) -> Result, PromptError> { let device_list = ios_deploy::device_list(env).map_err(|cause| PromptError::detection_failed("iOS", cause))?; if !device_list.is_empty() { let index = if device_list.len() > 1 { - prompt::list( - concat!("Detected ", "iOS", " devices"), - device_list.iter(), - "device", - None, - "Device", - ) - .map_err(|cause| PromptError::prompt_failed("iOS", cause))? + if let Some(t) = target { + let t = t.to_lowercase(); + device_list + .iter() + .position(|d| d.name().to_lowercase().starts_with(&t)) + .unwrap_or_default() + } else { + prompt::list( + concat!("Detected ", "iOS", " devices"), + device_list.iter(), + "device", + None, + "Device", + ) + .map_err(|cause| PromptError::prompt_failed("iOS", cause))? + } } else { 0 }; @@ -149,8 +165,55 @@ fn device_prompt<'a>(env: &'_ Env) -> Result, PromptError, +) -> Result> { + let simulator_list = simctl::device_list(env) + .map_err(|cause| PromptError::detection_failed("iOS Simulator", cause))?; + if !simulator_list.is_empty() { + let index = if simulator_list.len() > 1 { + if let Some(t) = target { + let t = t.to_lowercase(); + simulator_list + .iter() + .position(|d| d.name().to_lowercase().starts_with(&t)) + .unwrap_or_default() + } else { + prompt::list( + concat!("Detected ", "iOS", " simulators"), + simulator_list.iter(), + "simulator", + None, + "Simulator", + ) + .map_err(|cause| PromptError::prompt_failed("iOS Simulator", cause))? + } + } else { + 0 + }; + let device = simulator_list.into_iter().nth(index).unwrap(); + Ok(device) + } else { + Err(PromptError::none_detected("iOS Simulator")) + } +} + +fn device_prompt<'a>(env: &'_ Env, target: Option<&str>) -> Result> { + if let Ok(device) = ios_deploy_device_prompt(env, target) { + Ok(device) + } else { + let simulator = simulator_prompt(env, target)?; + let handle = simulator.start(env)?; + spawn(move || { + let _ = handle.wait(); + }); + Ok(simulator.into()) + } +} + fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> { - device_prompt(env).map(|device| device.target()).ok() + device_prompt(env, None).map(|device| device.target()).ok() } fn open_and_wait(config: &AppleConfig, env: &Env) -> ! { @@ -159,6 +222,6 @@ fn open_and_wait(config: &AppleConfig, env: &Env) -> ! { log::error!("{}", e); } loop { - std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60)); + sleep(Duration::from_secs(24 * 60 * 60)); } } diff --git a/tooling/cli/src/mobile/ios/dev.rs b/tooling/cli/src/mobile/ios/dev.rs index 3f8001de1..4ab79826c 100644 --- a/tooling/cli/src/mobile/ios/dev.rs +++ b/tooling/cli/src/mobile/ios/dev.rs @@ -10,13 +10,17 @@ use crate::{ use clap::Parser; use cargo_mobile::{ - apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, ios_deploy}, + apple::{config::Config as AppleConfig, device::RunError as DeviceRunError, simctl}, config::app::App, - device::PromptError, env::Env, opts::{NoiseLevel, Profile}, }; +use std::{ + thread::{sleep, spawn}, + time::Duration, +}; + #[derive(Debug, Clone, Parser)] #[clap(about = "iOS dev")] pub struct Options { @@ -38,6 +42,8 @@ pub struct Options { /// Open Xcode instead of trying to run on a connected device #[clap(short, long)] pub open: bool, + /// Runs on the given device name + pub device: Option, } impl From for crate::dev::Options { @@ -92,9 +98,29 @@ fn run_dev( let env = env()?; init_dot_cargo(app, None)?; + if let Some(device) = &options.device { + let simulators = simctl::device_list(&env).unwrap_or_default(); + for simulator in simulators { + if simulator + .name() + .to_lowercase() + .starts_with(&device.to_lowercase()) + { + log::info!("Starting simulator {}", simulator.name()); + let handle = simulator.start(&env)?; + spawn(move || { + let _ = handle.wait(); + }); + sleep(Duration::from_secs(3)); + break; + } + } + } + let open = options.open; let exit_on_panic = options.exit_on_panic; let no_watch = options.no_watch; + let device = options.device; interface.mobile_dev( MobileOptions { debug: true, @@ -115,7 +141,7 @@ fn run_dev( if open { open_and_wait(config, &env) } else { - match run(options, config, &env, noise_level) { + match run(device.as_deref(), options, config, &env, noise_level) { Ok(c) => { crate::dev::wait_dev_process(c.clone(), move |status, reason| { crate::dev::on_app_exit(status, reason, exit_on_panic, no_watch) @@ -135,12 +161,13 @@ fn run_dev( #[derive(Debug, thiserror::Error)] enum RunError { - #[error(transparent)] - FailedToPromptForDevice(PromptError), + #[error("{0}")] + FailedToPromptForDevice(String), #[error(transparent)] RunFailed(DeviceRunError), } fn run( + device: Option<&str>, options: MobileOptions, config: &AppleConfig, env: &Env, @@ -154,8 +181,8 @@ fn run( let non_interactive = true; // ios-deploy --noninteractive (quit when app crashes or exits) - device_prompt(env) - .map_err(RunError::FailedToPromptForDevice)? + device_prompt(env, device) + .map_err(|e| RunError::FailedToPromptForDevice(e.to_string()))? .run(config, env, noise_level, non_interactive, profile) .map(DevChild::new) .map_err(RunError::RunFailed) diff --git a/tooling/cli/src/mobile/ios/project.rs b/tooling/cli/src/mobile/ios/project.rs index e1ae7213c..5a44bb11b 100644 --- a/tooling/cli/src/mobile/ios/project.rs +++ b/tooling/cli/src/mobile/ios/project.rs @@ -50,7 +50,11 @@ pub fn gen( let ios_pods = metadata.ios().pods().unwrap_or_default(); let macos_pods = metadata.macos().pods().unwrap_or_default(); - let default_archs = [String::from("arm64"), String::from("x86_64")]; + let default_archs = [ + String::from("arm64"), + String::from("arm64-sim"), + String::from("x86_64"), + ]; map.insert("file-groups", &source_dirs); map.insert("ios-frameworks", metadata.ios().frameworks()); diff --git a/tooling/cli/src/mobile/ios/xcode_script.rs b/tooling/cli/src/mobile/ios/xcode_script.rs index 870edf52d..ded473c65 100644 --- a/tooling/cli/src/mobile/ios/xcode_script.rs +++ b/tooling/cli/src/mobile/ios/xcode_script.rs @@ -100,6 +100,7 @@ pub fn command(options: Options) -> Result<()> { // Set target-specific flags let triple = match arch.as_str() { "arm64" => "aarch64_apple_ios", + "arm64-sim" => "aarch64_apple_ios_sim", "x86_64" => "x86_64_apple_ios", _ => { return Err(anyhow::anyhow!( diff --git a/tooling/cli/templates/mobile/ios/project.yml b/tooling/cli/templates/mobile/ios/project.yml index 9f9c8742d..fb72413cb 100644 --- a/tooling/cli/templates/mobile/ios/project.yml +++ b/tooling/cli/templates/mobile/ios/project.yml @@ -72,7 +72,7 @@ targets: ARCHS: [{{join ios-valid-archs}}] VALID_ARCHS: {{~#each ios-valid-archs}} {{this}} {{/each}} LIBRARY_SEARCH_PATHS[sdk=iphoneos*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios/$(CONFIGURATION)"}}" - LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]: $(inherited) "{{prefix-path "target/x86_64-apple-ios/$(CONFIGURATION)"}}" + LIBRARY_SEARCH_PATHS[sdk=iphonesimulator*]: $(inherited) "{{prefix-path "target/aarch64-apple-ios-sim/$(CONFIGURATION)"}}" ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES: true groups: [app] dependencies: