Skip to content

Commit 4da644e

Browse files
committed
Make it work as a runner
1 parent c427156 commit 4da644e

9 files changed

Lines changed: 190 additions & 190 deletions

File tree

example/basic/.cargo/config.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[target.aarch64-linux-android]
2+
runner = "cargo ndk-runner"

example/basic/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "example"
33
version = "0.1.0"
44
authors = ["Brendan Molloy <brendan@bbqsrc.net>"]
5-
edition = "2018"
5+
edition = "2024"
66

77
[lib]
88
# This must contain at least cdylib for Android libraries to be generated.

example/basic/src/lib.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
#[no_mangle]
1+
/// A simple function to demonstrate a basic Rust library for Android.
2+
///
3+
/// ```
4+
/// use example::example;
5+
/// example();
6+
/// ```
7+
#[unsafe(no_mangle)]
28
pub extern "C" fn example() {
39
println!("Hello Android!");
410
}
@@ -11,4 +17,4 @@ fn test_example() {
1117
#[test]
1218
fn failing_test() {
1319
assert_eq!(1, 2, "This test is supposed to fail");
14-
}
20+
}

src/bin/cargo-ndk-runner.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
use std::env;
2+
use std::process::exit;
3+
4+
fn main() -> anyhow::Result<()> {
5+
if env::var("CARGO").is_err() {
6+
eprintln!("This binary may only be called via `cargo ndk-runner`.");
7+
exit(1);
8+
}
9+
10+
let args = std::env::args().collect::<Vec<_>>();
11+
12+
cargo_ndk::cli::runner::run(args)
13+
}

src/cargo.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,16 @@ pub(crate) fn build_env(
9696
clang_target: &str,
9797
link_builtins: bool,
9898
) -> BTreeMap<String, OsString> {
99-
let self_path = dunce::canonicalize(env::args().next().unwrap())
99+
let cargo_ndk_path = dunce::canonicalize(env::args().next().unwrap())
100100
.expect("Failed to canonicalize absolute path to cargo-ndk")
101101
.parent()
102102
.unwrap()
103103
.join("cargo-ndk");
104+
let cargo_ndk_runner_path = dunce::canonicalize(env::args().next().unwrap())
105+
.expect("Failed to canonicalize absolute path to cargo-ndk-runner")
106+
.parent()
107+
.unwrap()
108+
.join("cargo-ndk-runner");
104109

105110
// Environment variables for the `cc` crate
106111
let (cc_key, _) = cc_env("CC", triple);
@@ -113,6 +118,7 @@ pub(crate) fn build_env(
113118
// Environment variables for cargo
114119
let cargo_ar_key = cargo_env_target_cfg(triple, "ar");
115120
let cargo_linker_key = cargo_env_target_cfg(triple, "linker");
121+
let cargo_runner_key = cargo_env_target_cfg(triple, "runner");
116122
let bindgen_clang_args_key = format!("BINDGEN_EXTRA_CLANG_ARGS_{}", &triple.replace('-', "_"));
117123

118124
let target_cc = ndk_home.join(ndk_tool(ARCH, "clang"));
@@ -133,7 +139,6 @@ pub(crate) fn build_env(
133139
.join(cargo_ndk_sysroot_target);
134140
let target_ar = ndk_home.join(ndk_tool(ARCH, "llvm-ar"));
135141
let target_ranlib = ndk_home.join(ndk_tool(ARCH, "llvm-ranlib"));
136-
let target_linker = self_path;
137142

138143
let extra_include = format!(
139144
"{}/usr/include/{}",
@@ -149,7 +154,8 @@ pub(crate) fn build_env(
149154
(ar_key, target_ar.clone().into()),
150155
(ranlib_key, target_ranlib.into_os_string()),
151156
(cargo_ar_key, target_ar.into_os_string()),
152-
(cargo_linker_key, target_linker.into_os_string()),
157+
(cargo_linker_key, cargo_ndk_path.into_os_string()),
158+
(cargo_runner_key, cargo_ndk_runner_path.into_os_string()),
153159
(
154160
CARGO_NDK_SYSROOT_PATH_KEY.to_string(),
155161
cargo_ndk_sysroot_path.clone().into_os_string(),

src/cli/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
pub mod env;
2+
pub mod runner;
23
pub mod test;
34

45
use std::{

src/cli/runner.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use std::{path::PathBuf, process::Command};
2+
3+
use clap::Parser;
4+
5+
use crate::{
6+
cli::{CommandExt as _, derive_adb_path},
7+
shell::{Shell, Verbosity},
8+
};
9+
10+
#[derive(Debug, Parser, Clone)]
11+
struct RunnerArgs {
12+
#[arg(long, env = "CARGO_NDK_ADB_SERIAL")]
13+
/// "Serial number" of the device to use for testing (e.g. "emulator-5554" or "0123456789ABCDEF")
14+
///
15+
/// You can find the serial number of your device by running `adb devices`.
16+
///
17+
/// If not set, the first available device will be used.
18+
adb_serial: Option<String>,
19+
20+
#[arg(short)]
21+
/// Enable verbose output
22+
verbose: bool,
23+
24+
#[arg(short)]
25+
/// Enable quiet output (no output except errors)
26+
quiet: bool,
27+
28+
/// Path to the binary to run on the device
29+
executable: PathBuf,
30+
31+
#[arg(allow_hyphen_values = true)]
32+
/// Arguments to be run
33+
runner_args: Vec<String>,
34+
}
35+
36+
pub fn run(args: Vec<String>) -> anyhow::Result<()> {
37+
let mut shell = Shell::new();
38+
39+
let args = RunnerArgs::try_parse_from(args).unwrap_or_else(|e| {
40+
shell.error(e).unwrap();
41+
std::process::exit(2);
42+
});
43+
44+
let adb_serial = args.adb_serial.as_deref();
45+
let verbosity = if args.verbose {
46+
Verbosity::Verbose
47+
} else if args.quiet {
48+
Verbosity::Quiet
49+
} else {
50+
Verbosity::Normal
51+
};
52+
53+
shell.set_verbosity(verbosity);
54+
55+
// Get adb path
56+
let adb_path = match derive_adb_path(&mut shell) {
57+
Ok(path) => path,
58+
Err(e) => {
59+
shell.error(e)?;
60+
std::process::exit(1);
61+
}
62+
};
63+
64+
// Push binary to device
65+
let device_path = format!(
66+
"/data/local/tmp/{}",
67+
args.executable.file_name().unwrap().to_string_lossy()
68+
);
69+
70+
// Ugly but works
71+
shell.verbose(|shell| {
72+
shell.status_header("Pushing")?;
73+
shell.reset_err()?;
74+
shell
75+
.err()
76+
.write_fmt(format_args!("binary to device: {device_path}\r"))?;
77+
shell.set_needs_clear(true);
78+
Ok(())
79+
})?;
80+
81+
let push_status = Command::new(&adb_path)
82+
.with_serial(adb_serial.as_deref())
83+
.arg("push")
84+
.arg(&args.executable)
85+
.arg(&device_path)
86+
.output()?;
87+
88+
if !push_status.status.success() {
89+
shell.error("Failed to push test binary to device")?;
90+
eprintln!("{}", std::str::from_utf8(&push_status.stderr)?.trim());
91+
shell.note("If multiple devices, use --adb-serial to specify one.")?;
92+
shell.note("Run `adb devices` to see connected devices.")?;
93+
std::process::exit(push_status.status.code().unwrap_or(1));
94+
}
95+
96+
shell.verbose(|shell| shell.status("Pushing", format!("binary to device ({device_path})")))?;
97+
98+
// Make binary executable
99+
let chmod_status = Command::new(&adb_path)
100+
.with_serial(adb_serial.as_deref())
101+
.arg("shell")
102+
.arg("chmod")
103+
.arg("755")
104+
.arg(&device_path)
105+
.status()?;
106+
107+
if !chmod_status.success() {
108+
shell.error("Failed to make binary executable")?;
109+
std::process::exit(chmod_status.code().unwrap_or(1));
110+
}
111+
112+
shell.reset_err()?;
113+
114+
let verbosity_arg = match verbosity {
115+
Verbosity::Quiet => "-q",
116+
_ => "",
117+
};
118+
119+
let run_status = Command::new(&adb_path)
120+
.with_serial(adb_serial.as_deref())
121+
.arg("shell")
122+
.arg(&device_path)
123+
.arg(verbosity_arg)
124+
.args(&args.runner_args)
125+
.status()?;
126+
127+
// Clean up the binary from device
128+
let _ = Command::new(&adb_path)
129+
.with_serial(adb_serial.as_deref())
130+
.arg("shell")
131+
.arg("rm")
132+
.arg(&device_path)
133+
.status();
134+
135+
std::process::exit(run_status.code().unwrap_or(1))
136+
}

0 commit comments

Comments
 (0)