From 34c60326d5803b0aad2794a621d060e2507a3245 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 28 Jan 2025 16:28:24 +0100 Subject: [PATCH] Pass trailing `x run --` arguments to launch command If you want to pass string "extras" into an Android intent via `am start`, call `x run ... -- -e extraKey "extra string"` (and similar for other types like `--ez key bool` for booleans, or `-d URI` for data URIs). [All arguments listed by `am help` under the `start` command] (including the `INTENT` specification) are supported. On the host, this passes the arguments as-is to the running executable, similar to `cargo run .. -- `. For iOS, these are passed to [`idevicedebug run`]. [All arguments listed by `am help` under the `start` command]: https://developer.android.com/tools/adb#am [`idevicedebug run`]: https://manpages.debian.org/testing/libimobiledevice-utils/idevicedebug.1.en.html --- xbuild/Cargo.toml | 1 + xbuild/src/command/mod.rs | 4 ++-- xbuild/src/devices/adb.rs | 16 ++++++++++++++-- xbuild/src/devices/host.rs | 4 ++-- xbuild/src/devices/imd.rs | 13 ++++++++++--- xbuild/src/devices/mod.rs | 14 ++++++++++---- xbuild/src/main.rs | 14 ++++++++++++-- 7 files changed, 51 insertions(+), 15 deletions(-) diff --git a/xbuild/Cargo.toml b/xbuild/Cargo.toml index 1ba745ca..816b4cce 100644 --- a/xbuild/Cargo.toml +++ b/xbuild/Cargo.toml @@ -32,6 +32,7 @@ quick-xml = { version = "0.26.0", features = ["serialize"] } reqwest = { version = "0.11.13", default-features = false, features = ["blocking", "rustls-tls"] } serde = { version = "1.0.151", features = ["derive"] } serde_yaml = "0.9.16" +shlex = "1" symlink = "0.1.0" tar = "0.4.38" toml = "0.5.10" diff --git a/xbuild/src/command/mod.rs b/xbuild/src/command/mod.rs index 1d2f5615..eb1f8ddc 100644 --- a/xbuild/src/command/mod.rs +++ b/xbuild/src/command/mod.rs @@ -26,10 +26,10 @@ pub fn devices() -> Result<()> { Ok(()) } -pub fn run(env: &BuildEnv) -> Result<()> { +pub fn run(env: &BuildEnv, launch_args: &[String]) -> Result<()> { let out = env.executable(); if let Some(device) = env.target().device() { - device.run(env, &out)?; + device.run(env, &out, launch_args)?; } else { anyhow::bail!("no device specified"); } diff --git a/xbuild/src/devices/adb.rs b/xbuild/src/devices/adb.rs index 356612e1..9eef19e4 100644 --- a/xbuild/src/devices/adb.rs +++ b/xbuild/src/devices/adb.rs @@ -93,7 +93,17 @@ impl Adb { } /// To run a native activity use "android.app.NativeActivity" as the activity name - fn start(&self, device: &str, package: &str, activity: &str) -> Result<()> { + fn start( + &self, + device: &str, + package: &str, + activity: &str, + launch_args: &[String], + ) -> Result<()> { + // Quote arguments for `am` so that they don't already get parsed by `adb`. + let launch_args = shlex::try_join(launch_args.iter().map(String::as_str)) + .context("Failed to re-quote launch arguments")?; + let status = self .shell(device, None) .arg("am") @@ -102,6 +112,7 @@ impl Adb { .arg("android.intent.action.MAIN") .arg("-n") .arg(format!("{}/{}", package, activity)) + .arg(launch_args) .status()?; anyhow::ensure!( status.success(), @@ -340,6 +351,7 @@ impl Adb { &self, device: &str, path: &Path, + launch_args: &[String], debug_config: &AndroidDebugConfig, debug: bool, ) -> Result<()> { @@ -355,7 +367,7 @@ impl Adb { self.install(device, path)?; self.forward_reverse(device, debug_config)?; let last_timestamp = self.logcat_last_timestamp(device)?; - self.start(device, package, activity)?; + self.start(device, package, activity, launch_args)?; let uid = self.uidof(device, package)?; let logcat = self.logcat(device, uid, &last_timestamp)?; for line in logcat { diff --git a/xbuild/src/devices/host.rs b/xbuild/src/devices/host.rs index fe4dcce8..dc38b885 100644 --- a/xbuild/src/devices/host.rs +++ b/xbuild/src/devices/host.rs @@ -46,8 +46,8 @@ impl Host { } } - pub fn run(&self, path: &Path) -> Result<()> { - Command::new(path).status()?; + pub fn run(&self, path: &Path, launch_args: &[String]) -> Result<()> { + Command::new(path).args(launch_args).status()?; Ok(()) } diff --git a/xbuild/src/devices/imd.rs b/xbuild/src/devices/imd.rs index 9e2dee74..b6e49a35 100644 --- a/xbuild/src/devices/imd.rs +++ b/xbuild/src/devices/imd.rs @@ -48,12 +48,13 @@ impl IMobileDevice { Ok(()) } - fn start(&self, device: &str, bundle_identifier: &str) -> Result<()> { + fn start(&self, device: &str, bundle_identifier: &str, launch_args: &[String]) -> Result<()> { let status = Command::new(&self.idevicedebug) .arg("--udid") .arg(device) .arg("run") .arg(bundle_identifier) + .args(launch_args) .status()?; anyhow::ensure!(status.success(), "failed to run idevicedebug"); Ok(()) @@ -92,11 +93,17 @@ impl IMobileDevice { Ok(()) } - pub fn run(&self, env: &BuildEnv, device: &str, path: &Path) -> Result<()> { + pub fn run( + &self, + env: &BuildEnv, + device: &str, + path: &Path, + launch_args: &[String], + ) -> Result<()> { let bundle_identifier = appbundle::app_bundle_identifier(path)?; self.mount_disk_image(env, device)?; self.install(device, path)?; - self.start(device, &bundle_identifier)?; + self.start(device, &bundle_identifier, launch_args)?; Ok(()) } diff --git a/xbuild/src/devices/mod.rs b/xbuild/src/devices/mod.rs index 4c25b610..083b66b3 100644 --- a/xbuild/src/devices/mod.rs +++ b/xbuild/src/devices/mod.rs @@ -110,11 +110,17 @@ impl Device { } } - pub fn run(&self, env: &BuildEnv, path: &Path) -> Result<()> { + pub fn run(&self, env: &BuildEnv, path: &Path, launch_args: &[String]) -> Result<()> { match &self.backend { - Backend::Adb(adb) => adb.run(&self.id, path, &env.config.android().debug, false), - Backend::Host(host) => host.run(path), - Backend::Imd(imd) => imd.run(env, &self.id, path), + Backend::Adb(adb) => adb.run( + &self.id, + path, + launch_args, + &env.config.android().debug, + false, + ), + Backend::Host(host) => host.run(path, launch_args), + Backend::Imd(imd) => imd.run(env, &self.id, path, launch_args), }?; Ok(()) } diff --git a/xbuild/src/main.rs b/xbuild/src/main.rs index 9e1acfdf..e820349e 100644 --- a/xbuild/src/main.rs +++ b/xbuild/src/main.rs @@ -46,6 +46,16 @@ enum Commands { Run { #[clap(flatten)] args: BuildArgs, + + /// Platform-specific arguments to pass to the launch command: + /// - **Host**: Passed to the running executable, similar to `cargo run -- `. + /// - **Android**: Passed to [`am start`], after the `-a MAIN` and `-n package/.Activity` flags. + /// - **iOS**: Passed to [`idevicedebug`] after `run `. + /// + /// [`am start`]: https://developer.android.com/tools/adb#am + /// [`idevicedebug`]: https://manpages.debian.org/testing/libimobiledevice-utils/idevicedebug.1.en.html + #[clap(last = true)] + launch_args: Vec, }, /// Launch app in a debugger on an attached device Lldb { @@ -104,10 +114,10 @@ impl Commands { let env = BuildEnv::new(args)?; command::build(&env)?; } - Self::Run { args } => { + Self::Run { args, launch_args } => { let env = BuildEnv::new(args)?; command::build(&env)?; - command::run(&env)?; + command::run(&env, &launch_args)?; } Self::Lldb { args } => { let env = BuildEnv::new(args)?;