use super::*;
use crate::{error::*, types::*};
pub fn rust_compile(
ndk: &AndroidNdk,
build_target: AndroidTarget,
project_path: &std::path::Path,
profile: Profile,
features: Vec<String>,
all_features: bool,
no_default_features: bool,
target_sdk_version: u32,
lib_name: &str,
app_wrapper: AppWrapper,
) -> Result<()> {
let rust_triple = build_target.rust_triple();
let (clang, clang_pp) = ndk.clang(build_target, target_sdk_version)?;
std::env::set_var(format!("CC_{}", rust_triple), &clang);
std::env::set_var(format!("CXX_{}", rust_triple), &clang_pp);
std::env::set_var(cargo_env_target_cfg("LINKER", rust_triple), &clang);
let ar = ndk.toolchain_bin("ar", build_target)?;
std::env::set_var(format!("AR_{}", rust_triple), &ar);
let cargo_config = cargo::util::Config::default()?;
let workspace = cargo::core::Workspace::new(&project_path.join("Cargo.toml"), &cargo_config)?;
let build_target_dir = workspace
.root()
.join("target")
.join(rust_triple)
.join(profile);
std::fs::create_dir_all(&build_target_dir).unwrap();
set_cmake_vars(build_target, ndk, target_sdk_version, &build_target_dir)?;
std::env::set_var("CXXSTDLIB", "c++");
let opts = compile_options::compile_options(
&workspace,
build_target,
&features,
all_features,
no_default_features,
&build_target_dir,
lib_name,
profile,
)?;
let executor: std::sync::Arc<dyn cargo::core::compiler::Executor> =
std::sync::Arc::new(SharedLibraryExecutor {
target_sdk_version,
build_target_dir,
build_target,
ndk: ndk.clone(),
profile,
nostrip: false,
app_wrapper,
});
cargo::ops::compile_with_exec(&workspace, &opts, &executor)?;
Ok(())
}
struct SharedLibraryExecutor {
target_sdk_version: u32,
build_target_dir: std::path::PathBuf,
build_target: AndroidTarget,
ndk: AndroidNdk,
profile: Profile,
nostrip: bool,
app_wrapper: AppWrapper,
}
impl cargo::core::compiler::Executor for SharedLibraryExecutor {
fn exec(
&self,
cmd: &cargo_util::ProcessBuilder,
_id: cargo::core::PackageId,
target: &cargo::core::Target,
mode: cargo::core::compiler::CompileMode,
on_stdout_line: &mut dyn FnMut(&str) -> cargo::util::errors::CargoResult<()>,
on_stderr_line: &mut dyn FnMut(&str) -> cargo::util::errors::CargoResult<()>,
) -> cargo::util::errors::CargoResult<()> {
if mode == cargo::core::compiler::CompileMode::Build
&& (target.kind() == &cargo::core::manifest::TargetKind::Bin
|| target.kind() == &cargo::core::manifest::TargetKind::ExampleBin)
{
let mut new_args = cmd.get_args().cloned().collect::<Vec<_>>();
let extra_code = match self.app_wrapper {
AppWrapper::Quad => consts::QUAD_EXTRA_CODE,
AppWrapper::NdkGlue => consts::NDK_GLUE_EXTRA_CODE,
};
let path =
if let cargo::core::manifest::TargetSourcePath::Path(path) = target.src_path() {
path.to_owned()
} else {
return Ok(());
};
let tmp_file = match self.app_wrapper {
AppWrapper::Quad => gen_tmp_lib_file::generate_lib_file(&path, extra_code)?,
AppWrapper::NdkGlue => gen_tmp_lib_file::generate_lib_file(&path, extra_code)?,
};
let filename = path.file_name().unwrap().to_owned();
let source_arg = new_args.iter_mut().find_map(|arg| {
let tmp = std::path::Path::new(&arg).file_name().unwrap();
if filename == tmp {
Some(arg)
} else {
None
}
});
if let Some(source_arg) = source_arg {
let mut path_arg = std::path::PathBuf::from(&source_arg);
path_arg.set_file_name(tmp_file.path().file_name().unwrap());
*source_arg = path_arg.into_os_string();
} else {
return Err(anyhow::Error::msg(format!(
"Unable to replace source argument when building target: {}",
target.name()
)));
}
std::fs::create_dir_all(&self.build_target_dir)
.map_err(|_| anyhow::Error::msg("Failed to create build target directory"))?;
let mut iter = new_args.iter_mut().rev().peekable();
while let Some(arg) = iter.next() {
if let Some(prev_arg) = iter.peek() {
if *prev_arg == "--crate-type" && arg == "bin" {
*arg = "cdylib".into();
} else if *prev_arg == "--out-dir" {
*arg = self.build_target_dir.clone().into();
}
}
}
let mut cmd = cmd.clone();
let build_tag = self.ndk.build_tag();
let tool_root = self.ndk.toolchain_dir().map_err(|_| {
anyhow::Error::msg("Failed to get access to the toolchain directory")
})?;
if build_tag > 7272597 {
let error_msg = anyhow::Error::msg("Failed to write content into libgcc.a file");
let mut args = match self.app_wrapper {
AppWrapper::Quad => {
new_ndk_quad_args(tool_root, &self.build_target, self.target_sdk_version)
.map_err(|_| error_msg)?
}
AppWrapper::NdkGlue => linker_args(&tool_root).map_err(|_| error_msg)?,
};
new_args.append(&mut args);
cmd.args_replace(&new_args);
cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
.map(drop)?;
} else if self.app_wrapper == AppWrapper::Quad {
let mut linker_args =
add_clinker_args(&self.ndk, &self.build_target, self.target_sdk_version)?;
new_args.append(&mut linker_args);
if !self.nostrip && self.profile == Profile::Release {
new_args.push("-Clink-arg=-strip-all".into());
}
let mut cmd = cmd.clone();
cmd.args_replace(&new_args);
cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
.map(drop)?;
} else if self.app_wrapper == AppWrapper::NdkGlue {
cmd.args_replace(&new_args);
cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
.map(drop)?;
}
} else if mode == cargo::core::compiler::CompileMode::Test {
return Err(anyhow::Error::msg(format!(
"Ignoring CompileMode::Test for target: {}",
target.name()
)));
} else if mode == cargo::core::compiler::CompileMode::Build {
let mut new_args = cmd.get_args().cloned().collect::<Vec<_>>();
let mut iter = new_args.iter_mut().rev().peekable();
while let Some(arg) = iter.next() {
if let Some(prev_arg) = iter.peek() {
if *prev_arg == "--crate-type" && arg == "cdylib" {
*arg = "rlib".into();
}
}
}
let mut cmd = cmd.clone();
cmd.args_replace(&new_args);
cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
.map(drop)?
} else {
cmd.exec_with_streaming(on_stdout_line, on_stderr_line, false)
.map(drop)?
}
Ok(())
}
}
pub fn cargo_env_target_cfg(tool: &str, target: &str) -> String {
let utarget = target.replace('-', "_");
let env = format!("CARGO_TARGET_{}_{}", &utarget, tool);
env.to_uppercase()
}