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