Преглед на файлове

refactor(cli): improve security of android dev/build (#5043)

Lucas Fernandes Nogueira преди 2 години
родител
ревизия
927ccc465d

+ 1 - 1
examples/api/src-tauri/Cargo.lock

@@ -4222,7 +4222,7 @@ dependencies = [
 [[package]]
 name = "wry"
 version = "0.20.2"
-source = "git+https://github.com/tauri-apps/wry?branch=dev#b1e8560c3f13f2674528f6ca440ba476ddbef7c2"
+source = "git+https://github.com/tauri-apps/wry?branch=dev#83b6a8d06be8b1687296d2dfb4daeff3cc587714"
 dependencies = [
  "block",
  "cocoa",

+ 211 - 5
tooling/cli/Cargo.lock

@@ -85,6 +85,29 @@ version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
 
+[[package]]
+name = "async-channel"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28"
+dependencies = [
+ "concurrent-queue",
+ "event-listener",
+ "futures-core",
+]
+
+[[package]]
+name = "async-task"
+version = "4.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524"
+
+[[package]]
+name = "atomic-waker"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
+
 [[package]]
 name = "attohttpc"
 version = "0.22.0"
@@ -187,6 +210,20 @@ dependencies = [
  "byte-tools",
 ]
 
+[[package]]
+name = "blocking"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6ccb65d468978a086b69884437ded69a90faab3bbe6e67f242173ea728acccc"
+dependencies = [
+ "async-channel",
+ "async-task",
+ "atomic-waker",
+ "fastrand",
+ "futures-lite",
+ "once_cell",
+]
+
 [[package]]
 name = "bstr"
 version = "0.2.17"
@@ -247,6 +284,12 @@ dependencies = [
  "pkg-config",
 ]
 
+[[package]]
+name = "cache-padded"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c"
+
 [[package]]
 name = "cargo-mobile"
 version = "0.1.0"
@@ -444,6 +487,15 @@ dependencies = [
  "memchr",
 ]
 
+[[package]]
+name = "concurrent-queue"
+version = "1.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c"
+dependencies = [
+ "cache-padded",
+]
+
 [[package]]
 name = "console"
 version = "0.15.0"
@@ -979,6 +1031,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "event-listener"
+version = "2.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0"
+
 [[package]]
 name = "exr"
 version = "1.4.2"
@@ -1131,17 +1189,109 @@ dependencies = [
  "new_debug_unreachable",
 ]
 
+[[package]]
+name = "futures"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab30e97ab6aacfe635fad58f22c2bb06c8b685f7421eb1e064a729e2a5f481fa"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bfc52cbddcfd745bf1740338492bb0bd83d76c67b445f91c5fb29fae29ecaa1"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
 [[package]]
 name = "futures-core"
-version = "0.3.21"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
+checksum = "1d11aa21b5b587a64682c0094c2bdd4df0076c5324961a40cc3abd7f37930528"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5"
+
+[[package]]
+name = "futures-lite"
+version = "1.12.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
+dependencies = [
+ "fastrand",
+ "futures-core",
+ "futures-io",
+ "memchr",
+ "parking",
+ "pin-project-lite",
+ "waker-fn",
+]
+
+[[package]]
+name = "futures-macro"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0db9cce532b0eae2ccf2766ab246f114b56b9cf6d445e00c2549fbc100ca045d"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
 
 [[package]]
 name = "futures-sink"
-version = "0.3.21"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765"
+
+[[package]]
+name = "futures-task"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306"
+
+[[package]]
+name = "futures-util"
+version = "0.3.23"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
+checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
 
 [[package]]
 name = "fxhash"
@@ -1494,6 +1644,29 @@ dependencies = [
  "cfg-if 1.0.0",
 ]
 
+[[package]]
+name = "interprocess"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1c58ec7fbda1df9a93f587b780659db3c99f61f4be27f9c82c9b37684ffd0366"
+dependencies = [
+ "blocking",
+ "cfg-if 1.0.0",
+ "futures",
+ "intmap",
+ "libc",
+ "once_cell",
+ "spinning",
+ "thiserror",
+ "winapi 0.3.9",
+]
+
+[[package]]
+name = "intmap"
+version = "0.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae52f28f45ac2bc96edb7714de995cffc174a395fb0abf5bff453587c980d7b9"
+
 [[package]]
 name = "io-lifetimes"
 version = "0.7.2"
@@ -2184,6 +2357,12 @@ version = "6.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "21326818e99cfe6ce1e524c2a805c189a99b5ae555a35d19f9a284b427d86afa"
 
+[[package]]
+name = "parking"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72"
+
 [[package]]
 name = "parking_lot"
 version = "0.12.1"
@@ -2409,6 +2588,18 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "pin-project-lite"
+version = "0.2.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
 [[package]]
 name = "pkg-config"
 version = "0.3.25"
@@ -3120,6 +3311,15 @@ dependencies = [
  "lock_api",
 ]
 
+[[package]]
+name = "spinning"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2d4f0e86297cad2658d92a707320d87bf4e6ae1050287f51d19b67ef3f153a7b"
+dependencies = [
+ "lock_api",
+]
+
 [[package]]
 name = "stable_deref_trait"
 version = "1.2.0"
@@ -3305,13 +3505,13 @@ dependencies = [
  "colored 2.0.0",
  "ctrlc",
  "dialoguer",
- "dirs-next",
  "env_logger 0.9.0",
  "glob",
  "handlebars 4.3.1",
  "heck 0.4.0",
  "ignore",
  "include_dir",
+ "interprocess",
  "json-patch",
  "lazy_static",
  "libc",
@@ -3798,6 +3998,12 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "waker-fn"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
+
 [[package]]
 name = "walkdir"
 version = "2.3.2"

+ 1 - 1
tooling/cli/Cargo.toml

@@ -30,7 +30,7 @@ path = "src/main.rs"
 # 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"] }
-dirs-next = "2.0"
+interprocess = "1"
 thiserror = "1"
 clap = { version = "3.2", features = [ "derive" ] }
 anyhow = "1.0"

+ 16 - 2
tooling/cli/src/mobile/android.rs

@@ -21,7 +21,7 @@ use clap::{Parser, Subcommand};
 use super::{
   ensure_init, get_config,
   init::{command as init_command, init_dot_cargo, Options as InitOptions},
-  log_finished, Target as MobileTarget,
+  log_finished, read_options, CliOptions, Target as MobileTarget,
 };
 use crate::{helpers::config::get as get_tauri_config, Result};
 
@@ -96,6 +96,7 @@ pub fn command(cli: Cli) -> Result<()> {
 }
 
 fn with_config<T>(
+  cli_options: Option<CliOptions>,
   f: impl FnOnce(&Config, &AndroidConfig, &AndroidMetadata) -> Result<T, Error>,
 ) -> Result<T, Error> {
   let (config, metadata) = {
@@ -103,7 +104,10 @@ fn with_config<T>(
       get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
     let tauri_config_guard = tauri_config.lock().unwrap();
     let tauri_config_ = tauri_config_guard.as_ref().unwrap();
-    get_config(tauri_config_)
+    get_config(
+      tauri_config_,
+      cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Android)),
+    )
   };
   f(&config, config.android(), metadata.android())
 }
@@ -152,3 +156,13 @@ fn device_prompt<'a>(env: &'_ Env) -> Result<Device<'a>, PromptError<adb::device
 fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> {
   device_prompt(env).map(|device| device.target()).ok()
 }
+
+fn open_and_wait(config: &AndroidConfig) -> ! {
+  log::info!("Opening Android Studio");
+  if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) {
+    log::error!("{}", e);
+  }
+  loop {
+    std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60));
+  }
+}

+ 1 - 1
tooling/cli/src/mobile/android/android_studio_script.rs

@@ -33,7 +33,7 @@ pub fn command(options: Options) -> Result<()> {
   };
   let noise_level = NoiseLevel::Polite;
 
-  with_config(|root_conf, config, metadata| {
+  with_config(None, |root_conf, config, metadata| {
     ensure_init(config.project_dir(), MobileTarget::Android)
       .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
 

+ 14 - 4
tooling/cli/src/mobile/android/build.rs

@@ -1,6 +1,6 @@
 use super::{
-  delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, with_config, Error,
-  MobileTarget,
+  delete_codegen_vars, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config,
+  Error, MobileTarget,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -48,6 +48,9 @@ pub struct Options {
   /// Build AABs.
   #[clap(long)]
   pub aab: bool,
+  /// Open Android Studio
+  #[clap(short, long)]
+  pub open: bool,
 }
 
 impl From<Options> for crate::build::Options {
@@ -66,7 +69,7 @@ impl From<Options> for crate::build::Options {
 
 pub fn command(options: Options) -> Result<()> {
   delete_codegen_vars();
-  with_config(|root_conf, config, _metadata| {
+  with_config(Some(Default::default()), |root_conf, config, _metadata| {
     set_var("WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION", "");
     set_var("WRY_RUSTWEBVIEW_CLASS_INIT", "");
 
@@ -76,7 +79,14 @@ pub fn command(options: Options) -> Result<()> {
     let env = env()?;
     init_dot_cargo(root_conf, Some(&env)).map_err(Error::InitDotCargo)?;
 
-    run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))
+    let open = options.open;
+    run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))?;
+
+    if open {
+      open_and_wait(config);
+    }
+
+    Ok(())
   })
   .map_err(Into::into)
 }

+ 5 - 16
tooling/cli/src/mobile/android/dev.rs

@@ -1,6 +1,6 @@
 use super::{
-  delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, with_config, Error,
-  MobileTarget,
+  delete_codegen_vars, device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config,
+  Error, MobileTarget,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -14,7 +14,6 @@ use cargo_mobile::{
   android::config::{Config as AndroidConfig, Metadata as AndroidMetadata},
   config::Config,
   opts::{NoiseLevel, Profile},
-  os,
 };
 
 use std::env::set_var;
@@ -65,7 +64,7 @@ impl From<Options> for crate::dev::Options {
 
 pub fn command(options: Options) -> Result<()> {
   delete_codegen_vars();
-  with_config(|root_conf, config, metadata| {
+  with_config(Some(Default::default()), |root_conf, config, metadata| {
     set_var(
       "WRY_RUSTWEBVIEWCLIENT_CLASS_EXTENSION",
       WEBVIEW_CLIENT_CLASS_EXTENSION,
@@ -120,13 +119,13 @@ fn run_dev(
       write_options(cli_options, &bundle_identifier, MobileTarget::Android)?;
 
       if open {
-        open_dev(config)
+        open_and_wait(config)
       } else {
         match run(options, root_conf, config, metadata) {
           Ok(c) => Ok(Box::new(c) as Box<dyn DevProcess>),
           Err(Error::FailedToPromptForDevice(e)) => {
             log::error!("{}", e);
-            open_dev(config)
+            open_and_wait(config)
           }
           Err(e) => Err(e.into()),
         }
@@ -135,16 +134,6 @@ fn run_dev(
   )
 }
 
-fn open_dev(config: &AndroidConfig) -> ! {
-  log::info!("Opening Android Studio");
-  if let Err(e) = os::open_file_with("Android Studio", config.project_dir()) {
-    log::error!("{}", e);
-  }
-  loop {
-    std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60));
-  }
-}
-
 fn run(
   options: MobileOptions,
   root_conf: &Config,

+ 1 - 1
tooling/cli/src/mobile/android/open.rs

@@ -3,7 +3,7 @@ use crate::Result;
 use cargo_mobile::os;
 
 pub fn command() -> Result<()> {
-  with_config(|_, config, _metadata| {
+  with_config(Some(Default::default()), |_, config, _metadata| {
     ensure_init(config.project_dir(), MobileTarget::Android)
       .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
     os::open_file_with("Android Studio", config.project_dir()).map_err(Error::OpenFailed)

+ 1 - 1
tooling/cli/src/mobile/init.rs

@@ -107,7 +107,7 @@ pub fn exec(
   let tauri_config_guard = tauri_config.lock().unwrap();
   let tauri_config_ = tauri_config_guard.as_ref().unwrap();
 
-  let (config, metadata) = get_config(tauri_config_);
+  let (config, metadata) = get_config(tauri_config_, Default::default());
 
   let asset_dir = config.app().asset_dir();
   if !asset_dir.is_dir() {

+ 16 - 2
tooling/cli/src/mobile/ios.rs

@@ -20,7 +20,7 @@ use clap::{Parser, Subcommand};
 use super::{
   ensure_init, env, get_config,
   init::{command as init_command, init_dot_cargo, Options as InitOptions},
-  log_finished, Target as MobileTarget,
+  log_finished, read_options, CliOptions, Target as MobileTarget,
 };
 use crate::{helpers::config::get as get_tauri_config, Result};
 
@@ -104,6 +104,7 @@ pub fn command(cli: Cli) -> Result<()> {
 }
 
 fn with_config<T>(
+  cli_options: Option<CliOptions>,
   f: impl FnOnce(&Config, &AppleConfig, &AppleMetadata) -> Result<T, Error>,
 ) -> Result<T, Error> {
   let (config, metadata) = {
@@ -111,7 +112,10 @@ fn with_config<T>(
       get_tauri_config(None).map_err(|e| Error::InvalidTauriConfig(e.to_string()))?;
     let tauri_config_guard = tauri_config.lock().unwrap();
     let tauri_config_ = tauri_config_guard.as_ref().unwrap();
-    get_config(tauri_config_)
+    get_config(
+      tauri_config_,
+      cli_options.unwrap_or_else(|| read_options(tauri_config_, MobileTarget::Ios)),
+    )
   };
   f(&config, config.apple(), metadata.apple())
 }
@@ -147,3 +151,13 @@ fn device_prompt<'a>(env: &'_ Env) -> Result<Device<'a>, PromptError<ios_deploy:
 fn detect_target_ok<'a>(env: &Env) -> Option<&'a Target<'a>> {
   device_prompt(env).map(|device| device.target()).ok()
 }
+
+fn open_and_wait(config: &AppleConfig) -> ! {
+  log::info!("Opening Xcode");
+  if let Err(e) = os::open_file_with("Xcode", config.project_dir()) {
+    log::error!("{}", e);
+  }
+  loop {
+    std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60));
+  }
+}

+ 14 - 4
tooling/cli/src/mobile/ios/build.rs

@@ -1,6 +1,6 @@
 use super::{
-  detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, with_config, Error,
-  MobileTarget,
+  detect_target_ok, ensure_init, env, init_dot_cargo, log_finished, open_and_wait, with_config,
+  Error, MobileTarget,
 };
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
@@ -44,6 +44,9 @@ pub struct Options {
   /// Build number to append to the app version.
   #[clap(long)]
   pub build_number: Option<u32>,
+  /// Open Xcode
+  #[clap(short, long)]
+  pub open: bool,
 }
 
 impl From<Options> for crate::build::Options {
@@ -61,14 +64,21 @@ impl From<Options> for crate::build::Options {
 }
 
 pub fn command(options: Options) -> Result<()> {
-  with_config(|root_conf, config, _metadata| {
+  with_config(Some(Default::default()), |root_conf, config, _metadata| {
     ensure_init(config.project_dir(), MobileTarget::Ios)
       .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
 
     let env = env()?;
     init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?;
 
-    run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))
+    let open = options.open;
+    run_build(options, config, env).map_err(|e| Error::BuildFailed(format!("{:#}", e)))?;
+
+    if open {
+      open_and_wait(config);
+    }
+
+    Ok(())
   })
   .map_err(Into::into)
 }

+ 6 - 15
tooling/cli/src/mobile/ios/dev.rs

@@ -1,4 +1,6 @@
-use super::{device_prompt, ensure_init, env, init_dot_cargo, with_config, Error, MobileTarget};
+use super::{
+  device_prompt, ensure_init, env, init_dot_cargo, open_and_wait, with_config, Error, MobileTarget,
+};
 use crate::{
   helpers::{config::get as get_tauri_config, flock},
   interface::{AppSettings, Interface, MobileOptions, Options as InterfaceOptions},
@@ -11,7 +13,6 @@ use cargo_mobile::{
   apple::config::Config as AppleConfig,
   config::Config,
   opts::{NoiseLevel, Profile},
-  os,
 };
 
 #[derive(Debug, Clone, Parser)]
@@ -53,7 +54,7 @@ impl From<Options> for crate::dev::Options {
 }
 
 pub fn command(options: Options) -> Result<()> {
-  with_config(|root_conf, config, _metadata| {
+  with_config(Some(Default::default()), |root_conf, config, _metadata| {
     ensure_init(config.project_dir(), MobileTarget::Ios)
       .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
     run_dev(options, root_conf, config).map_err(|e| Error::DevFailed(format!("{:#}", e)))
@@ -98,13 +99,13 @@ fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result
       };
       write_options(cli_options, &bundle_identifier, MobileTarget::Ios)?;
       if open {
-        open_dev(config)
+        open_and_wait(config)
       } else {
         match run(options, root_conf, config) {
           Ok(c) => Ok(Box::new(c) as Box<dyn DevProcess>),
           Err(Error::FailedToPromptForDevice(e)) => {
             log::error!("{}", e);
-            open_dev(config)
+            open_and_wait(config)
           }
           Err(e) => Err(e.into()),
         }
@@ -113,16 +114,6 @@ fn run_dev(options: Options, root_conf: &Config, config: &AppleConfig) -> Result
   )
 }
 
-fn open_dev(config: &AppleConfig) -> ! {
-  log::info!("Opening Xcode");
-  if let Err(e) = os::open_file_with("Xcode", config.project_dir()) {
-    log::error!("{}", e);
-  }
-  loop {
-    std::thread::sleep(std::time::Duration::from_secs(24 * 60 * 60));
-  }
-}
-
 fn run(
   options: MobileOptions,
   root_conf: &Config,

+ 1 - 1
tooling/cli/src/mobile/ios/open.rs

@@ -3,7 +3,7 @@ use crate::Result;
 use cargo_mobile::os;
 
 pub fn command() -> Result<()> {
-  with_config(|_, config, _metadata| {
+  with_config(Some(Default::default()), |_, config, _metadata| {
     ensure_init(config.project_dir(), MobileTarget::Ios)
       .map_err(|e| Error::ProjectNotInitialized(e.to_string()))?;
     os::open_file_with("Xcode", config.project_dir()).map_err(Error::OpenFailed)

+ 1 - 1
tooling/cli/src/mobile/ios/xcode_script.rs

@@ -46,7 +46,7 @@ pub fn command(options: Options) -> Result<()> {
   let macos = macos_from_platform(&options.platform);
   let noise_level = NoiseLevel::Polite;
 
-  with_config(|root_conf, config, metadata| {
+  with_config(None, |root_conf, config, metadata| {
     let env = env()?;
     init_dot_cargo(root_conf, None).map_err(Error::InitDotCargo)?;
     // The `PATH` env var Xcode gives us is missing any additions

+ 51 - 18
tooling/cli/src/mobile/mod.rs

@@ -17,9 +17,16 @@ use cargo_mobile::{
   config::{app::Raw as RawAppConfig, metadata::Metadata, Config, Raw},
   env::Error as EnvError,
 };
+use interprocess::local_socket::{LocalSocketListener, LocalSocketStream};
 use serde::{Deserialize, Serialize};
 use std::{
-  collections::HashMap, env::set_var, ffi::OsString, fmt::Write, path::PathBuf, process::ExitStatus,
+  collections::HashMap,
+  env::set_var,
+  ffi::OsString,
+  fmt::Write,
+  io::{BufRead, BufReader, Write as _},
+  path::PathBuf,
+  process::ExitStatus,
 };
 
 #[cfg(not(windows))]
@@ -63,7 +70,7 @@ impl DevProcess for DevChild {
   }
 }
 
-#[derive(PartialEq, Eq)]
+#[derive(PartialEq, Eq, Copy, Clone)]
 pub enum Target {
   Android,
   #[cfg(target_os = "macos")]
@@ -96,17 +103,15 @@ impl Target {
   }
 }
 
-#[derive(Debug, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
 pub struct CliOptions {
   pub features: Option<Vec<String>>,
   pub args: Vec<String>,
   pub vars: HashMap<String, OsString>,
 }
 
-fn options_path(bundle_identifier: &str, target: Target) -> PathBuf {
-  let out_dir = dirs_next::cache_dir()
-    .or_else(dirs_next::home_dir)
-    .unwrap_or_else(std::env::temp_dir);
+fn options_local_socket_name(bundle_identifier: &str, target: Target) -> PathBuf {
+  let out_dir = std::env::temp_dir();
   let out_dir = out_dir.join(".tauri").join(bundle_identifier);
   let _ = std::fs::create_dir_all(&out_dir);
   out_dir
@@ -120,6 +125,7 @@ fn env_vars() -> HashMap<String, OsString> {
     let k = k.to_string_lossy();
     if (k.starts_with("TAURI") && k != "TAURI_PRIVATE_KEY" && k != "TAURI_KEY_PASSWORD")
       || k.starts_with("WRY")
+      || k == "TMPDIR"
     {
       vars.insert(k.into_owned(), v);
     }
@@ -139,23 +145,50 @@ pub fn write_options(
   target: Target,
 ) -> crate::Result<()> {
   options.vars.extend(env_vars());
-  std::fs::write(
-    options_path(bundle_identifier, target),
-    &serde_json::to_string(&options)?,
-  )?;
+  let name = options_local_socket_name(bundle_identifier, target);
+  let _ = std::fs::remove_file(&name);
+  let value = serde_json::to_string(&options)?;
+
+  std::thread::spawn(move || {
+    let listener = LocalSocketListener::bind(name).expect("failed to start local socket");
+    for mut conn in listener.incoming().flatten() {
+      let _ = conn.write_all(value.as_bytes());
+      let _ = conn.write_all(b"\n");
+    }
+  });
+
   Ok(())
 }
 
-fn read_options(config: &TauriConfig, target: Target) -> crate::Result<CliOptions> {
-  let data = std::fs::read_to_string(options_path(&config.tauri.bundle.identifier, target))?;
-  let options: CliOptions = serde_json::from_str(&data)?;
+fn read_options(config: &TauriConfig, target: Target) -> CliOptions {
+  let name = options_local_socket_name(&config.tauri.bundle.identifier, target);
+  let conn = LocalSocketStream::connect(name).unwrap_or_else(|_| {
+    log::error!(
+      "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.",
+      cmd = target.command_name()
+    );
+    std::process::exit(1);
+  });
+  conn
+    .set_nonblocking(true)
+    .expect("failed to set local socket stream to nonblocking");
+  let mut conn = BufReader::new(conn);
+  let mut buffer = String::new();
+  conn.read_line(&mut buffer).unwrap_or_else(|_| {
+    log::error!(
+      "failed to connect to local socket. You must keep the Tauri CLI alive with the `{cmd} dev` or `{cmd} build --open` commands.",
+      cmd = target.command_name()
+    );
+    std::process::exit(1);
+  });
+  let options: CliOptions = serde_json::from_str(&buffer).expect("invalid CLI options");
   for (k, v) in &options.vars {
     set_var(k, v);
   }
-  Ok(options)
+  options
 }
 
-fn get_config(config: &TauriConfig) -> (Config, Metadata) {
+fn get_config(config: &TauriConfig, cli_options: CliOptions) -> (Config, Metadata) {
   let mut s = config.tauri.bundle.identifier.rsplit('.');
   let app_name = s.next().unwrap_or("app").to_string();
   let mut domain = String::new();
@@ -191,8 +224,8 @@ fn get_config(config: &TauriConfig) -> (Config, Metadata) {
   };
 
   #[cfg(target_os = "macos")]
-  let ios_options = read_options(config, Target::Ios).unwrap_or_default();
-  let android_options = read_options(config, Target::Android).unwrap_or_default();
+  let ios_options = cli_options.clone();
+  let android_options = cli_options;
 
   let raw = Raw {
     app: RawAppConfig {