diff --git a/xbuild/src/command/doctor.rs b/xbuild/src/command/doctor.rs index f9421546..3451aa87 100644 --- a/xbuild/src/command/doctor.rs +++ b/xbuild/src/command/doctor.rs @@ -1,8 +1,10 @@ -use anyhow::Result; +use anyhow::{bail, Result}; use std::path::PathBuf; use std::process::Command; -#[derive(Clone, Debug)] +use crate::devices::adb::Adb; + +#[derive(Debug)] pub struct Doctor { groups: Vec, } @@ -35,7 +37,11 @@ impl Default for Doctor { Group { name: "android", checks: vec![ - Check::new("adb", Some(VersionCheck::new("--version", 0, 4))), + Check::with_path( + "adb", + Adb::which(), + Some(VersionCheck::new("--version", 0, 4)), + ), Check::new("javac", Some(VersionCheck::new("--version", 0, 1))), Check::new("java", Some(VersionCheck::new("--version", 0, 1))), Check::new("kotlin", Some(VersionCheck::new("-version", 0, 2))), @@ -77,7 +83,7 @@ impl std::fmt::Display for Doctor { } } -#[derive(Clone, Debug)] +#[derive(Debug)] struct Group { name: &'static str, checks: Vec, @@ -105,15 +111,32 @@ impl std::fmt::Display for Group { } } -#[derive(Clone, Copy, Debug)] +#[derive(Debug)] struct Check { name: &'static str, + location: Option>, version: Option, } impl Check { pub const fn new(name: &'static str, version: Option) -> Self { - Self { name, version } + Self { + name, + location: None, + version, + } + } + + pub const fn with_path( + name: &'static str, + path: Result, + version: Option, + ) -> Self { + Self { + name, + location: Some(path), + version, + } } } @@ -131,17 +154,22 @@ impl VersionCheck { } impl Check { - fn name(self) -> &'static str { + fn name(&self) -> &'static str { self.name } - fn path(self) -> Result { - Ok(which::which(self.name)?) + fn path(&self) -> Result { + Ok(match &self.location { + Some(Ok(path)) => path.clone(), + // Cannot clone the error: + Some(Err(e)) => bail!("{:?}", e), + None => which::which(self.name)?, + }) } - fn version(self) -> Result> { + fn version(&self) -> Result> { if let Some(version) = self.version { - let output = Command::new(self.name) + let output = Command::new(self.path()?) .args(version.arg.split(' ')) .output()?; anyhow::ensure!(output.status.success(), "failed to run {}", self.name); diff --git a/xbuild/src/devices/adb.rs b/xbuild/src/devices/adb.rs index 409a1e5e..83274a62 100644 --- a/xbuild/src/devices/adb.rs +++ b/xbuild/src/devices/adb.rs @@ -12,8 +12,44 @@ use std::time::Duration; pub(crate) struct Adb(PathBuf); impl Adb { - pub fn which() -> Result { - Ok(Self(which::which(exe!("adb"))?)) + pub fn which() -> Result { + const ADB: &str = exe!("adb"); + + match which::which(ADB) { + Err(which::Error::CannotFindBinaryPath) => { + let sdk_path = { + let sdk_path = std::env::var("ANDROID_SDK_ROOT").ok(); + if sdk_path.is_some() { + eprintln!( + "Warning: Environment variable ANDROID_SDK_ROOT is deprecated \ + (https://developer.android.com/studio/command-line/variables#envar). \ + It will be used until it is unset and replaced by ANDROID_HOME." + ); + } + + PathBuf::from( + sdk_path + .or_else(|| std::env::var("ANDROID_HOME").ok()) + .context( + "Cannot find `adb` on in PATH nor is ANDROID_HOME/ANDROID_SDK_ROOT set", + )?, + ) + }; + + let adb_path = sdk_path.join("platform-tools").join(ADB); + anyhow::ensure!( + adb_path.exists(), + "Expected `adb` at `{}`", + adb_path.display() + ); + Ok(adb_path) + } + r => r.context("Could not find `adb` in PATH"), + } + } + + pub fn new() -> Result { + Ok(Self(Self::which()?)) } fn adb(&self, device: &str) -> Command { diff --git a/xbuild/src/devices/mod.rs b/xbuild/src/devices/mod.rs index 4c25b610..7b8f91e3 100644 --- a/xbuild/src/devices/mod.rs +++ b/xbuild/src/devices/mod.rs @@ -5,9 +5,9 @@ use crate::{Arch, BuildEnv, Platform}; use anyhow::Result; use std::path::Path; -mod adb; -mod host; -mod imd; +pub(crate) mod adb; +pub(crate) mod host; +pub(crate) mod imd; #[derive(Clone, Debug)] enum Backend { @@ -31,7 +31,7 @@ impl std::str::FromStr for Device { } if let Some((backend, id)) = device.split_once(':') { let backend = match backend { - "adb" => Backend::Adb(Adb::which()?), + "adb" => Backend::Adb(Adb::new()?), "imd" => Backend::Imd(IMobileDevice::which()?), _ => anyhow::bail!("unsupported backend {}", backend), }; @@ -58,7 +58,7 @@ impl std::fmt::Display for Device { impl Device { pub fn list() -> Result> { let mut devices = vec![Self::host()]; - if let Ok(adb) = Adb::which() { + if let Ok(adb) = Adb::new() { adb.devices(&mut devices)?; } if let Ok(imd) = IMobileDevice::which() {