1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use crate::{error::*, tools::Aapt2};
use std::path::{Path, PathBuf};
use std::process::Command as ProcessCommand;
pub struct AndroidSdk {
sdk_path: PathBuf,
build_deps_path: PathBuf,
build_deps_version: String,
platforms_path: PathBuf,
platforms: Vec<u32>,
}
impl AndroidSdk {
pub fn from_env() -> Result<Self> {
let sdk_path = {
let sdk_path = std::env::var("ANDROID_SDK_ROOT")
.ok()
.or_else(|| std::env::var("ANDROID_SDK_PATH").ok())
.or_else(|| std::env::var("ANDROID_HOME").ok());
PathBuf::from(sdk_path.ok_or(AndroidError::AndroidSdkNotFound)?)
};
let build_deps_path = sdk_path.join("build-tools");
let build_deps_version = std::fs::read_dir(&build_deps_path)
.map_err(|_| Error::PathNotFound(build_deps_path.clone()))?
.filter_map(|path| path.ok())
.filter(|path| path.path().is_dir())
.filter_map(|path| path.file_name().into_string().ok())
.filter(|name| name.chars().next().unwrap().is_digit(10))
.max()
.ok_or(AndroidError::BuildToolsNotFound)?;
let platforms_path = sdk_path.join("platforms");
let platforms: Vec<u32> = std::fs::read_dir(&platforms_path)
.map_err(|_| Error::PathNotFound(platforms_path.clone()))?
.filter_map(|path| path.ok())
.filter(|path| path.path().is_dir())
.filter_map(|path| path.file_name().into_string().ok())
.filter_map(|name| {
name.strip_prefix("android-")
.and_then(|api| api.parse::<u32>().ok())
})
.collect();
if platforms.is_empty() {
return Err(AndroidError::NoPlatformsFound.into());
};
Ok(Self {
sdk_path,
build_deps_path,
build_deps_version,
platforms_path,
platforms,
})
}
pub fn sdk_path(&self) -> &Path {
&self.sdk_path
}
pub fn build_deps_path(&self) -> &Path {
&self.build_deps_path
}
pub fn build_deps_version(&self) -> &str {
&self.build_deps_version
}
pub fn platforms_path(&self) -> &Path {
&self.platforms_path
}
pub fn platforms(&self) -> &[u32] {
&self.platforms
}
pub fn build_tool(&self, tool: &str, current_dir: Option<&Path>) -> Result<ProcessCommand> {
let path = self
.build_deps_path
.join(&self.build_deps_version)
.join(tool);
if !path.exists() {
return Err(Error::CmdNotFound(tool.to_string()));
}
let mut command = ProcessCommand::new(dunce::canonicalize(path)?);
if let Some(current_dir) = current_dir {
command.current_dir(current_dir);
};
Ok(command)
}
pub fn aapt2(&self) -> Result<Aapt2> {
self.build_tool(bin!("aapt2"), None)?;
Ok(Aapt2)
}
pub fn platform_tool(&self, tool: &str) -> Result<ProcessCommand> {
let path = self.sdk_path.join("platform-tools").join(tool);
if !path.exists() {
return Err(Error::CmdNotFound(tool.to_string()));
}
Ok(ProcessCommand::new(dunce::canonicalize(path)?))
}
pub fn default_platform(&self) -> u32 {
self.platforms().iter().max().cloned().unwrap()
}
pub fn platform_dir(&self, platform: u32) -> Result<PathBuf> {
let dir = self.platforms_path.join(format!("android-{}", platform));
if !dir.exists() {
return Err(AndroidError::PlatformNotFound(platform).into());
}
Ok(dir)
}
pub fn android_jar(&self, platform: u32) -> Result<PathBuf> {
let android_jar = self.platform_dir(platform)?.join("android.jar");
if !android_jar.exists() {
return Err(Error::PathNotFound(android_jar));
}
Ok(android_jar)
}
}