creator_tools/commands/android/
add_libs_into_apk.rs1use crate::{
2 error::*,
3 tools::{AndroidNdk, AndroidSdk},
4 types::{AndroidTarget, IntoRustTriple, Profile},
5};
6use std::{
7 fs::File,
8 io::{BufRead, BufReader},
9 path::{Path, PathBuf},
10};
11
12pub fn add_libs_into_apk(
15 sdk: &AndroidSdk,
16 ndk: &AndroidNdk,
17 apk_path: &Path,
18 lib_path: &Path,
19 build_target: AndroidTarget,
20 profile: Profile,
21 min_sdk_version: u32,
22 build_dir: &Path,
23 target_dir: &Path,
24) -> Result<()> {
25 let mut system_libs = Vec::new();
27 let sysroot_platform_lib_dir = ndk.sysroot_platform_lib_dir(build_target, min_sdk_version)?;
28 for lib in get_libs_in_dir(&sysroot_platform_lib_dir)? {
29 system_libs.push(lib);
30 }
31 let build_path = target_dir
33 .join(build_target.rust_triple())
34 .join(profile.as_ref());
35 let mut dylibs_paths = search_dylibs(&build_path.join("build"))?;
36 dylibs_paths.push(build_path.join("tools"));
37 let lib_name = lib_path.file_name().unwrap().to_str().unwrap().to_owned();
39 let mut needed_libs = vec![];
40 recursively_define_needed_libs(
41 (lib_name, lib_path.to_owned()),
42 &ndk.toolchain_bin("readelf", build_target)?,
43 &ndk.sysroot_lib_dir(build_target)?.join("libc++_shared.so"),
44 &system_libs,
45 &dylibs_paths,
46 &mut needed_libs,
47 )?;
48 let abi = build_target.android_abi();
50 let out_dir = build_dir.join("lib").join(abi);
51 for (_lib_name, lib_path) in needed_libs {
52 aapt_add_lib(sdk, apk_path, &lib_path, &out_dir, abi)?;
53 }
54 Ok(())
55}
56
57fn aapt_add_lib(
59 sdk: &AndroidSdk,
60 apk_path: &Path,
61 lib_path: &Path,
62 out_dir: &Path,
63 abi: &str,
64) -> Result<()> {
65 if !lib_path.exists() {
66 return Err(Error::PathNotFound(lib_path.to_owned()));
67 }
68 std::fs::create_dir_all(&out_dir)?;
69 let file_name = lib_path.file_name().unwrap();
70 std::fs::copy(lib_path, &out_dir.join(&file_name))?;
71 let mut aapt = sdk.build_tool(bin!("aapt"), Some(apk_path.parent().unwrap()))?;
74 aapt.arg("add")
75 .arg(apk_path)
76 .arg(format!("lib/{}/{}", abi, file_name.to_str().unwrap()));
77 aapt.output_err(true)?;
78 Ok(())
79}
80
81fn search_dylibs(deps_dir: &Path) -> Result<Vec<PathBuf>> {
83 let mut paths = Vec::new();
84 for dep_dir in deps_dir.read_dir()? {
85 let output_file = dep_dir?.path().join("output");
86 if output_file.is_file() {
87 for line in BufReader::new(File::open(output_file)?).lines() {
88 let line = line?;
89 if let Some(link_search) = line.strip_prefix("cargo:rustc-link-search=") {
90 let mut pie = link_search.split('=');
91 let (kind, path) = match (pie.next(), pie.next()) {
92 (Some(kind), Some(path)) => (kind, path),
93 (Some(path), None) => ("all", path),
94 _ => unreachable!(),
95 };
96 match kind {
97 "dependency" | "native" | "all" => paths.push(path.into()),
99 _ => (),
100 };
101 }
102 }
103 }
104 }
105 Ok(paths)
106}
107
108fn recursively_define_needed_libs(
112 (lib_name, lib_path): (String, PathBuf),
113 readelf_path: &Path,
114 libcpp_shared_path: &Path,
115 system_libs: &[String],
116 dylibs_paths: &[PathBuf],
117 needed_libs: &mut Vec<(String, PathBuf)>,
118) -> Result<()> {
119 let shared_libs = readelf_list_shared_libs(readelf_path, &lib_path)?;
120 needed_libs.push((lib_name, lib_path));
121 for lib_name in shared_libs {
122 if lib_name == "libc++_shared.so" {
123 needed_libs.push((lib_name, libcpp_shared_path.to_owned()));
124 } else if system_libs.contains(&lib_name) {
125 continue;
126 } else if !needed_libs.iter().any(|(name, _)| name == &lib_name) {
127 if let Some(lib_path) = find_library_path(dylibs_paths, &lib_name)? {
128 recursively_define_needed_libs(
129 (lib_name, lib_path),
130 readelf_path,
131 libcpp_shared_path,
132 system_libs,
133 dylibs_paths,
134 needed_libs,
135 )?;
136 } else {
137 eprintln!("Shared library \"{}\" not found.", lib_name);
138 }
139 };
140 }
141 Ok(())
142}
143
144fn readelf_list_shared_libs(readelf_path: &Path, lib_path: &Path) -> Result<Vec<String>> {
146 let mut readelf = std::process::Command::new(readelf_path);
147 readelf.arg("-d").arg(lib_path);
148 let output = readelf.output_err(false)?;
149 let mut needed = Vec::new();
150 for line in output.stdout.lines() {
151 let line = line?;
152 if line.contains("(NEEDED)") {
153 let lib = line
154 .split("Shared library: [")
155 .last()
156 .and_then(|line| line.split(']').next());
157 if let Some(lib) = lib {
158 needed.push(lib.to_owned());
159 }
160 }
161 }
162 Ok(needed)
163}
164
165fn find_library_path<S: AsRef<Path>>(paths: &[PathBuf], lib_name: S) -> Result<Option<PathBuf>> {
167 for path in paths {
168 let lib_path = path.join(&lib_name);
169 if lib_path.exists() {
170 return Ok(Some(dunce::canonicalize(lib_path)?));
171 }
172 }
173 Ok(None)
174}
175
176fn get_libs_in_dir(dir: &Path) -> std::io::Result<Vec<String>> {
178 let mut libs = Vec::new();
179 if dir.is_dir() {
180 for entry in std::fs::read_dir(dir)? {
181 let entry = entry?;
182 if !entry.path().is_dir() {
183 if let Some(file_name) = entry.file_name().to_str() {
184 if file_name.ends_with(".so") {
185 libs.push(file_name.to_owned());
186 }
187 }
188 }
189 }
190 };
191 Ok(libs)
192}