crossbundle_tools/commands/android/common/rust_compile/
rust_compiler.rs1use super::*;
2use crate::{error::*, types::*};
3
4pub fn rust_compile(
5 ndk: &AndroidNdk,
6 build_target: AndroidTarget,
7 project_path: &std::path::Path,
8 profile: Profile,
9 features: Vec<String>,
10 all_features: bool,
11 no_default_features: bool,
12 target_sdk_version: u32,
13 lib_name: &str,
14 app_wrapper: AppWrapper,
15) -> Result<()> {
16 let rust_triple = build_target.rust_triple();
18
19 let (clang, clang_pp) = ndk.clang(build_target, target_sdk_version)?;
21 std::env::set_var(format!("CC_{}", rust_triple), &clang);
22 std::env::set_var(format!("CXX_{}", rust_triple), &clang_pp);
23 std::env::set_var(cargo_env_target_cfg("LINKER", rust_triple), &clang);
24 let ar = ndk.toolchain_bin("ar", build_target)?;
25 std::env::set_var(format!("AR_{}", rust_triple), &ar);
26
27 let cargo_config = cargo::util::Config::default()?;
28 let workspace = cargo::core::Workspace::new(&project_path.join("Cargo.toml"), &cargo_config)?;
29
30 let build_target_dir = workspace
32 .root()
33 .join("target")
34 .join(rust_triple)
35 .join(profile);
36 std::fs::create_dir_all(&build_target_dir).unwrap();
37
38 set_cmake_vars(build_target, ndk, target_sdk_version, &build_target_dir)?;
39
40 std::env::set_var("CXXSTDLIB", "c++");
42
43 let opts = compile_options::compile_options(
45 &workspace,
46 build_target,
47 &features,
48 all_features,
49 no_default_features,
50 &build_target_dir,
51 lib_name,
52 profile,
53 )?;
54
55 let executor: std::sync::Arc<dyn cargo::core::compiler::Executor> =
57 std::sync::Arc::new(SharedLibraryExecutor {
58 target_sdk_version,
59 build_target_dir,
60 build_target,
61 ndk: ndk.clone(),
62 profile,
63 nostrip: false,
64 app_wrapper,
65 });
66
67 cargo::ops::compile_with_exec(&workspace, &opts, &executor)?;
69 Ok(())
70}
71
72struct SharedLibraryExecutor {
74 target_sdk_version: u32,
75 build_target_dir: std::path::PathBuf,
76 build_target: AndroidTarget,
77 ndk: AndroidNdk,
78 profile: Profile,
79 nostrip: bool,
80 app_wrapper: AppWrapper,
81}
82
83impl cargo::core::compiler::Executor for SharedLibraryExecutor {
84 fn exec(
85 &self,
86 cmd: &cargo_util::ProcessBuilder,
87 _id: cargo::core::PackageId,
88 target: &cargo::core::Target,
89 mode: cargo::core::compiler::CompileMode,
90 on_stdout_line: &mut dyn FnMut(&str) -> cargo::util::errors::CargoResult<()>,
91 on_stderr_line: &mut dyn FnMut(&str) -> cargo::util::errors::CargoResult<()>,
92 ) -> cargo::util::errors::CargoResult<()> {
93 if mode == cargo::core::compiler::CompileMode::Build
94 && (target.kind() == &cargo::core::manifest::TargetKind::Bin
95 || target.kind() == &cargo::core::manifest::TargetKind::ExampleBin)
96 {
97 let mut new_args = cmd.get_args().cloned().collect::<Vec<_>>();
98
99 let extra_code = match self.app_wrapper {
100 AppWrapper::Quad => consts::QUAD_EXTRA_CODE,
101 AppWrapper::NdkGlue => consts::NDK_GLUE_EXTRA_CODE,
102 };
103
104 let path =
105 if let cargo::core::manifest::TargetSourcePath::Path(path) = target.src_path() {
106 path.to_owned()
107 } else {
108 return Ok(());
110 };
111
112 let tmp_file = match self.app_wrapper {
115 AppWrapper::Quad => gen_tmp_lib_file::generate_lib_file(&path, extra_code)?,
116 AppWrapper::NdkGlue => gen_tmp_lib_file::generate_lib_file(&path, extra_code)?,
117 };
118
119 let filename = path.file_name().unwrap().to_owned();
121 let source_arg = new_args.iter_mut().find_map(|arg| {
122 let tmp = std::path::Path::new(&arg).file_name().unwrap();
123 if filename == tmp {
124 Some(arg)
125 } else {
126 None
127 }
128 });
129
130 if let Some(source_arg) = source_arg {
131 let mut path_arg = std::path::PathBuf::from(&source_arg);
137 path_arg.set_file_name(tmp_file.path().file_name().unwrap());
138 *source_arg = path_arg.into_os_string();
139 } else {
140 return Err(anyhow::Error::msg(format!(
141 "Unable to replace source argument when building target: {}",
142 target.name()
143 )));
144 }
145
146 std::fs::create_dir_all(&self.build_target_dir)
148 .map_err(|_| anyhow::Error::msg("Failed to create build target directory"))?;
149
150 let mut iter = new_args.iter_mut().rev().peekable();
152 while let Some(arg) = iter.next() {
153 if let Some(prev_arg) = iter.peek() {
154 if *prev_arg == "--crate-type" && arg == "bin" {
155 *arg = "cdylib".into();
156 } else if *prev_arg == "--out-dir" {
157 *arg = self.build_target_dir.clone().into();
158 }
159 }
160 }
161
162 let mut cmd = cmd.clone();
169 let build_tag = self.ndk.build_tag();
170 let tool_root = self.ndk.toolchain_dir().map_err(|_| {
171 anyhow::Error::msg("Failed to get access to the toolchain directory")
172 })?;
173 if build_tag > 7272597 {
174 let error_msg = anyhow::Error::msg("Failed to write content into libgcc.a file");
175 let mut args = match self.app_wrapper {
176 AppWrapper::Quad => {
177 new_ndk_quad_args(tool_root, &self.build_target, self.target_sdk_version)
178 .map_err(|_| error_msg)?
179 }
180 AppWrapper::NdkGlue => linker_args(&tool_root).map_err(|_| error_msg)?,
181 };
182 new_args.append(&mut args);
183 cmd.args_replace(&new_args);
184 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
185 .map(drop)?;
186 } else if self.app_wrapper == AppWrapper::Quad {
187 let mut linker_args =
189 add_clinker_args(&self.ndk, &self.build_target, self.target_sdk_version)?;
190 new_args.append(&mut linker_args);
191
192 if !self.nostrip && self.profile == Profile::Release {
194 new_args.push("-Clink-arg=-strip-all".into());
195 }
196
197 let mut cmd = cmd.clone();
199 cmd.args_replace(&new_args);
200
201 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
203 .map(drop)?;
204 } else if self.app_wrapper == AppWrapper::NdkGlue {
205 cmd.args_replace(&new_args);
206
207 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
208 .map(drop)?;
209 }
210 } else if mode == cargo::core::compiler::CompileMode::Test {
211 return Err(anyhow::Error::msg(format!(
213 "Ignoring CompileMode::Test for target: {}",
214 target.name()
215 )));
216 } else if mode == cargo::core::compiler::CompileMode::Build {
217 let mut new_args = cmd.get_args().cloned().collect::<Vec<_>>();
218
219 let mut iter = new_args.iter_mut().rev().peekable();
221 while let Some(arg) = iter.next() {
222 if let Some(prev_arg) = iter.peek() {
223 if *prev_arg == "--crate-type" && arg == "cdylib" {
224 *arg = "rlib".into();
225 }
226 }
227 }
228 let mut cmd = cmd.clone();
229 cmd.args_replace(&new_args);
230 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
231 .map(drop)?
232 } else {
233 cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
234 .map(drop)?
235 }
236 Ok(())
237 }
238}
239
240pub fn cargo_env_target_cfg(tool: &str, target: &str) -> String {
242 let utarget = target.replace('-', "_");
243 let env = format!("CARGO_TARGET_{}_{}", &utarget, tool);
244 env.to_uppercase()
245}