creator_tools/tools/
android_ndk.rs1use crate::error::*;
2use crate::types::AndroidTarget;
3use std::path::{Path, PathBuf};
4use std::process::Command;
5
6#[derive(Debug)]
7pub struct AndroidNdk {
8 ndk_path: PathBuf,
9}
10
11impl AndroidNdk {
12 pub fn from_env(sdk_path: Option<&Path>) -> Result<Self> {
13 let ndk_path = {
14 let ndk_path = std::env::var("ANDROID_NDK_ROOT")
15 .ok()
16 .or_else(|| std::env::var("ANDROID_NDK_PATH").ok())
17 .or_else(|| std::env::var("ANDROID_NDK_HOME").ok())
18 .or_else(|| std::env::var("NDK_HOME").ok());
19 if ndk_path.is_none()
21 && sdk_path.is_some()
22 && sdk_path.as_ref().unwrap().join("ndk-bundle").exists()
23 {
24 sdk_path.unwrap().join("ndk-bundle")
25 } else {
26 PathBuf::from(ndk_path.ok_or(AndroidError::AndroidNdkNotFound)?)
27 }
28 };
29 Ok(Self { ndk_path })
30 }
31
32 pub fn ndk_path(&self) -> &Path {
33 &self.ndk_path
34 }
35
36 pub fn toolchain_dir(&self) -> Result<PathBuf> {
37 let host_os = std::env::var("HOST").ok();
38 let host_contains = |s| host_os.as_ref().map(|h| h.contains(s)).unwrap_or(false);
39 let arch = if host_contains("linux") {
40 "linux"
41 } else if host_contains("macos") {
42 "darwin"
43 } else if host_contains("windows") {
44 "windows"
45 } else if cfg!(target_os = "linux") {
46 "linux"
47 } else if cfg!(target_os = "macos") {
48 "darwin"
49 } else if cfg!(target_os = "windows") {
50 "windows"
51 } else {
52 return match host_os {
53 Some(host_os) => Err(AndroidError::UnsupportedHost(host_os)),
54 _ => Err(AndroidError::UnsupportedTarget),
55 }?;
56 };
57 let mut toolchain_dir = self
58 .ndk_path
59 .join("toolchains")
60 .join("llvm")
61 .join("prebuilt")
62 .join(format!("{}-x86_64", arch));
63 if !toolchain_dir.exists() {
64 toolchain_dir.set_file_name(arch);
65 }
66 if !toolchain_dir.exists() {
67 return Err(Error::PathNotFound(toolchain_dir));
68 }
69 Ok(toolchain_dir)
70 }
71
72 pub fn clang(&self, target: AndroidTarget, platform: u32) -> Result<(PathBuf, PathBuf)> {
73 #[cfg(target_os = "windows")]
74 let ext = ".cmd";
75 #[cfg(not(target_os = "windows"))]
76 let ext = "";
77 let bin_name = format!("{}{}-clang", target.ndk_llvm_triple(), platform);
78 let bin_path = self.toolchain_dir()?.join("bin");
79 let clang = bin_path.join(format!("{}{}", &bin_name, ext));
80 if !clang.exists() {
81 return Err(Error::PathNotFound(clang));
82 }
83 let clang_pp = bin_path.join(format!("{}++{}", &bin_name, ext));
84 if !clang_pp.exists() {
85 return Err(Error::PathNotFound(clang_pp));
86 }
87 Ok((clang, clang_pp))
88 }
89
90 pub fn toolchain_bin(&self, name: &str, build_target: AndroidTarget) -> Result<PathBuf> {
91 #[cfg(target_os = "windows")]
92 let ext = ".exe";
93 #[cfg(not(target_os = "windows"))]
94 let ext = "";
95 let toolchain_path = self.toolchain_dir()?.join("bin");
96 let gnu_bin = format!("{}-{}{}", build_target.ndk_triple(), name, ext);
102 let gnu_path = toolchain_path.join(&gnu_bin);
103 if gnu_path.exists() {
104 Ok(gnu_path)
105 } else {
106 let llvm_bin = format!("llvm-{}{}", name, ext);
107 let llvm_path = toolchain_path.join(&llvm_bin);
108 llvm_path
109 .exists()
110 .then(|| llvm_path)
111 .ok_or(Error::ToolchainBinaryNotFound {
112 toolchain_path,
113 gnu_bin,
114 llvm_bin,
115 })
116 }
117 }
118
119 pub fn readelf(&self, build_target: AndroidTarget) -> Result<Command> {
120 let readelf_path = self.toolchain_bin("readelf", build_target)?;
121 Ok(Command::new(readelf_path))
122 }
123
124 pub fn sysroot_lib_dir(&self, build_target: AndroidTarget) -> Result<PathBuf> {
125 let sysroot_lib_dir = self
126 .toolchain_dir()?
127 .join("sysroot")
128 .join("usr")
129 .join("lib")
130 .join(build_target.ndk_triple());
131 if !sysroot_lib_dir.exists() {
132 return Err(Error::PathNotFound(sysroot_lib_dir));
133 }
134 Ok(sysroot_lib_dir)
135 }
136
137 pub fn sysroot_platform_lib_dir(
138 &self,
139 build_target: AndroidTarget,
140 min_sdk_version: u32,
141 ) -> Result<PathBuf> {
142 let sysroot_lib_dir = self.sysroot_lib_dir(build_target)?;
143 let mut tmp_platform = min_sdk_version;
145 while tmp_platform > 1 {
146 let path = sysroot_lib_dir.join(tmp_platform.to_string());
147 if path.exists() {
148 return Ok(path);
149 }
150 tmp_platform += 1;
151 }
152 let mut tmp_platform = min_sdk_version;
154 while tmp_platform < 100 {
155 let path = sysroot_lib_dir.join(tmp_platform.to_string());
156 if path.exists() {
157 return Ok(path);
158 }
159 tmp_platform += 1;
160 }
161 Err(AndroidError::PlatformNotFound(min_sdk_version).into())
162 }
163}