Skip to content

Commit 6d068c4

Browse files
committed
devices/adb: Find adb executable in $ANDROID_HOME/$ANDROID_SDK_ROOT
`adb` is not always available on `PATH`, sometimes it is installed only via the SDK. Make sure we find it there too - after checking `PATH` - via well-known SDK variables.
1 parent 875f933 commit 6d068c4

File tree

3 files changed

+84
-20
lines changed

3 files changed

+84
-20
lines changed

xbuild/src/command/doctor.rs

+40-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
use anyhow::Result;
1+
use anyhow::{bail, Result};
22
use std::path::PathBuf;
33
use std::process::Command;
44

5-
#[derive(Clone, Debug)]
5+
use crate::devices::adb::Adb;
6+
7+
#[derive(Debug)]
68
pub struct Doctor {
79
groups: Vec<Group>,
810
}
@@ -35,7 +37,11 @@ impl Default for Doctor {
3537
Group {
3638
name: "android",
3739
checks: vec![
38-
Check::new("adb", Some(VersionCheck::new("--version", 0, 4))),
40+
Check::with_path(
41+
"adb",
42+
Adb::which(),
43+
Some(VersionCheck::new("--version", 0, 4)),
44+
),
3945
Check::new("javac", Some(VersionCheck::new("--version", 0, 1))),
4046
Check::new("java", Some(VersionCheck::new("--version", 0, 1))),
4147
Check::new("kotlin", Some(VersionCheck::new("-version", 0, 2))),
@@ -77,7 +83,7 @@ impl std::fmt::Display for Doctor {
7783
}
7884
}
7985

80-
#[derive(Clone, Debug)]
86+
#[derive(Debug)]
8187
struct Group {
8288
name: &'static str,
8389
checks: Vec<Check>,
@@ -105,15 +111,32 @@ impl std::fmt::Display for Group {
105111
}
106112
}
107113

108-
#[derive(Clone, Copy, Debug)]
114+
#[derive(Debug)]
109115
struct Check {
110116
name: &'static str,
117+
location: Option<Result<PathBuf>>,
111118
version: Option<VersionCheck>,
112119
}
113120

114121
impl Check {
115122
pub const fn new(name: &'static str, version: Option<VersionCheck>) -> Self {
116-
Self { name, version }
123+
Self {
124+
name,
125+
location: None,
126+
version,
127+
}
128+
}
129+
130+
pub const fn with_path(
131+
name: &'static str,
132+
path: Result<PathBuf>,
133+
version: Option<VersionCheck>,
134+
) -> Self {
135+
Self {
136+
name,
137+
location: Some(path),
138+
version,
139+
}
117140
}
118141
}
119142

@@ -131,22 +154,27 @@ impl VersionCheck {
131154
}
132155

133156
impl Check {
134-
fn name(self) -> &'static str {
157+
fn name(&self) -> &'static str {
135158
self.name
136159
}
137160

138-
fn path(self) -> Result<PathBuf> {
139-
Ok(which::which(self.name)?)
161+
fn path(&self) -> Result<PathBuf> {
162+
Ok(match &self.location {
163+
Some(Ok(path)) => path.clone(),
164+
// Cannot clone the error:
165+
Some(Err(e)) => bail!("{:?}", e),
166+
None => which::which(self.name)?,
167+
})
140168
}
141169

142-
fn version(self) -> Result<Option<String>> {
170+
fn version(&self) -> Result<Option<String>> {
143171
if let Some(version) = self.version {
144-
let output = Command::new(self.name)
172+
let output = Command::new(self.path()?)
145173
.args(version.arg.split(' '))
146174
.output()?;
147175
anyhow::ensure!(output.status.success(), "failed to run {}", self.name);
148176
let output = std::str::from_utf8(&output.stdout)?;
149-
if let Some(line) = output.split('\n').nth(version.row as _) {
177+
if let Some(line) = output.lines().nth(version.row as _) {
150178
let mut col = version.col as usize;
151179
if line.starts_with("Apple ") || line.starts_with("Homebrew ") {
152180
col += 1;

xbuild/src/devices/adb.rs

+39-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::devices::{Backend, Device};
22
use crate::{Arch, Platform};
3-
use anyhow::Result;
3+
use anyhow::{Context, Result};
44
use apk::Apk;
55
use std::io::{BufRead, BufReader};
66
use std::path::{Path, PathBuf};
@@ -11,8 +11,44 @@ use std::time::Duration;
1111
pub(crate) struct Adb(PathBuf);
1212

1313
impl Adb {
14-
pub fn which() -> Result<Self> {
15-
Ok(Self(which::which(exe!("adb"))?))
14+
pub fn which() -> Result<PathBuf> {
15+
const ADB: &str = exe!("adb");
16+
17+
match which::which(ADB) {
18+
Err(which::Error::CannotFindBinaryPath) => {
19+
let sdk_path = {
20+
let sdk_path = std::env::var("ANDROID_SDK_ROOT").ok();
21+
if sdk_path.is_some() {
22+
eprintln!(
23+
"Warning: Environment variable ANDROID_SDK_ROOT is deprecated \
24+
(https://developer.android.com/studio/command-line/variables#envar). \
25+
It will be used until it is unset and replaced by ANDROID_HOME."
26+
);
27+
}
28+
29+
PathBuf::from(
30+
sdk_path
31+
.or_else(|| std::env::var("ANDROID_HOME").ok())
32+
.context(
33+
"Cannot find `adb` on in PATH nor is ANDROID_HOME/ANDROID_SDK_ROOT set",
34+
)?,
35+
)
36+
};
37+
38+
let adb_path = sdk_path.join("platform-tools").join(ADB);
39+
anyhow::ensure!(
40+
adb_path.exists(),
41+
"Expected `adb` at `{}`",
42+
adb_path.display()
43+
);
44+
Ok(adb_path)
45+
}
46+
r => r.context("Could not find `adb` in PATH"),
47+
}
48+
}
49+
50+
pub fn new() -> Result<Self> {
51+
Ok(Self(Self::which()?))
1652
}
1753

1854
fn adb(&self, device: &str) -> Command {

xbuild/src/devices/mod.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ use crate::{Arch, BuildEnv, Platform};
55
use anyhow::Result;
66
use std::path::Path;
77

8-
mod adb;
9-
mod host;
10-
mod imd;
8+
pub(crate) mod adb;
9+
pub(crate) mod host;
10+
pub(crate) mod imd;
1111

1212
#[derive(Clone, Debug)]
1313
enum Backend {
@@ -31,7 +31,7 @@ impl std::str::FromStr for Device {
3131
}
3232
if let Some((backend, id)) = device.split_once(':') {
3333
let backend = match backend {
34-
"adb" => Backend::Adb(Adb::which()?),
34+
"adb" => Backend::Adb(Adb::new()?),
3535
"imd" => Backend::Imd(IMobileDevice::which()?),
3636
_ => anyhow::bail!("unsupported backend {}", backend),
3737
};
@@ -58,7 +58,7 @@ impl std::fmt::Display for Device {
5858
impl Device {
5959
pub fn list() -> Result<Vec<Self>> {
6060
let mut devices = vec![Self::host()];
61-
if let Ok(adb) = Adb::which() {
61+
if let Ok(adb) = Adb::new() {
6262
adb.devices(&mut devices)?;
6363
}
6464
if let Ok(imd) = IMobileDevice::which() {

0 commit comments

Comments
 (0)