diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 70138369..3337e2c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,7 +27,7 @@ jobs: - uses: actions/checkout@v3 - uses: hecrj/setup-rust-action@v1 - run: cargo install --path xbuild --root . - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: ${{ matrix.host }}-x path: bin/x${{ matrix.host == 'windows-latest' && '.exe' || '' }} diff --git a/apk/src/compiler/table.rs b/apk/src/compiler/table.rs index aa199ebe..2b50fb33 100644 --- a/apk/src/compiler/table.rs +++ b/apk/src/compiler/table.rs @@ -144,7 +144,7 @@ pub struct Entry<'a> { entry: &'a ResTableEntry, } -impl<'a> Entry<'a> { +impl Entry<'_> { pub fn id(self) -> ResTableRef { self.id } diff --git a/mvn/src/package.rs b/mvn/src/package.rs index 01b72040..df6fa2b7 100644 --- a/mvn/src/package.rs +++ b/mvn/src/package.rs @@ -143,7 +143,7 @@ pub struct Artifact<'a> { pub version: &'a Version, } -impl<'a> Artifact<'a> { +impl Artifact<'_> { pub fn file_name(self, ext: &str) -> String { format!( "{}-{}-{}.{}", @@ -163,7 +163,7 @@ impl<'a> Artifact<'a> { } } -impl<'a> std::fmt::Display for Artifact<'a> { +impl std::fmt::Display for Artifact<'_> { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, diff --git a/xbuild/Cargo.toml b/xbuild/Cargo.toml index 24a7b7f9..06d2c85f 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", "rustls-tls-native-roots"] } 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 b0926c6c..1cc67e92 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 db36b8a7..f6ce0309 100644 --- a/xbuild/src/devices/adb.rs +++ b/xbuild/src/devices/adb.rs @@ -129,7 +129,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") @@ -138,6 +148,7 @@ impl Adb { .arg("android.intent.action.MAIN") .arg("-n") .arg(format!("{}/{}", package, activity)) + .arg(launch_args) .status()?; anyhow::ensure!( status.success(), @@ -373,6 +384,7 @@ impl Adb { &self, device: &str, path: &Path, + launch_args: &[String], debug_config: &AndroidDebugConfig, debug: bool, ) -> Result<()> { @@ -388,7 +400,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 7b8f91e3..9ad1439d 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/download.rs b/xbuild/src/download.rs index d40d53de..f7623fc6 100644 --- a/xbuild/src/download.rs +++ b/xbuild/src/download.rs @@ -15,7 +15,7 @@ pub struct DownloadManager<'a> { client: Client, } -impl<'a> Download for DownloadManager<'a> { +impl Download for DownloadManager<'_> { fn download(&self, url: &str, dest: &Path) -> Result<()> { let pb = ProgressBar::with_draw_target(Some(0), ProgressDrawTarget::stdout()) .with_style( @@ -234,7 +234,7 @@ impl WorkItem { } } -impl<'a> DownloadManager<'a> { +impl DownloadManager<'_> { pub fn android_jar(&self) -> Result<()> { let dir = self.env.android_sdk(); let sdk = self.env.target_sdk_version(); diff --git a/xbuild/src/gradle/mod.rs b/xbuild/src/gradle/mod.rs index 64da664a..ecb2574e 100644 --- a/xbuild/src/gradle/mod.rs +++ b/xbuild/src/gradle/mod.rs @@ -80,7 +80,7 @@ pub fn build(env: &BuildEnv, libraries: Vec<(Target, PathBuf)>, out: &Path) -> R let external_lib = format!(r#"task.dexDirs.from("{path}")"#); dexes.push_str(&external_lib); - dexes.push_str("\n"); + dexes.push('\n'); } let dexes = if !dexes.is_empty() { 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)?;