ndk_tool/
lib.rs

1use std::path::{Path, PathBuf};
2use std::process::Command;
3
4use ndk_build::cargo::cargo_ndk;
5use ndk_build::target::Target;
6
7use error::*;
8
9pub mod error;
10
11pub struct BuildConfig {
12    pub lib_name: String,
13    pub arch_list: Vec<String>,
14    pub is_release: bool,
15    pub target_dir: String,
16    pub project_dir: PathBuf,
17    pub sdk_version: u32,
18    pub rustc_param: String,
19    pub features_def: Vec<String>,
20}
21
22pub fn build(config: &BuildConfig) -> Result<()> {
23    println!("building android bridge project");
24
25    let ndk = ndk_build::ndk::Ndk::from_env()?;
26
27    let archs = &config.arch_list;
28
29    for arch in archs.iter() {
30        let target = Target::from_rust_triple(arch)?;
31        let mut cargo = cargo_ndk(&ndk, target, config.sdk_version)?;
32        cargo
33            .arg("rustc")
34            .arg("--target")
35            .arg(arch)
36            .arg("--lib")
37            .arg("--target-dir")
38            .arg(&config.target_dir);
39
40        if config.is_release {
41            cargo.arg("--release");
42        }
43
44        if !config.features_def.is_empty() {
45            let features = config.features_def.join(",");
46            cargo.arg("--features").arg(features);
47        }
48
49        // Workaround for https://github.com/rust-windowing/android-ndk-rs/issues/149:
50        // Rust (1.56 as of writing) still requires libgcc during linking, but this does
51        // not ship with the NDK anymore since NDK r23 beta 3.
52        // See https://github.com/rust-lang/rust/pull/85806 for a discussion on why libgcc
53        // is still required even after replacing it with libunwind in the source.
54        // XXX: Add an upper-bound on the Rust version whenever this is not necessary anymore.
55        if ndk.build_tag() > 7272597 {
56            cargo.arg("--");
57            let cargo_apk_link_dir = Path::new(&config.project_dir)
58                .join("target")
59                .join("cargo-apk-temp-extra-link-libraries");
60            std::fs::create_dir_all(&cargo_apk_link_dir)?;
61            let libgccfile = cargo_apk_link_dir.join("libgcc.a");
62            if !libgccfile.exists() {
63                std::fs::write(libgccfile, "INPUT(-lunwind)").expect("Failed to write");
64            }
65
66            cargo.arg("-L").arg(
67                PathBuf::new()
68                    .join("target")
69                    .join("cargo-apk-temp-extra-link-libraries"),
70            );
71
72            if !config.rustc_param.is_empty() {
73                cargo.arg(&config.rustc_param);
74            }
75        }
76
77        cargo.current_dir(&config.project_dir);
78
79        let status = cargo.status()?;
80        println!("process '{:?}' finished with: {}", cargo, status);
81
82        // let cargo_output = cargo.output()?;
83        // io::stdout().write_all(&cargo_output.stderr)?;
84        // io::stdout().write_all(&cargo_output.stdout)?;
85
86        let debug_release = if config.is_release {
87            "release"
88        } else {
89            "debug"
90        };
91        let strip = ndk.toolchain_bin("strip", target)?;
92        let mut strip_comm = Command::new(strip);
93        let _strip_output = strip_comm
94            .arg("-s")
95            .arg(format!(
96                "{}/{}/{}/{}",
97                &config.target_dir, arch, debug_release, config.lib_name
98            ))
99            .current_dir(&config.project_dir)
100            .output()?;
101
102        // io::stdout().write_all(&strip_output.stderr)?;
103        // io::stdout().write_all(&strip_output.stdout)?;
104
105        let strip_status = strip_comm.status()?;
106        println!("process '{:?}' finished with: {}", strip_comm, strip_status);
107    }
108    Ok(())
109}
110
111#[cfg(test)]
112mod tests {
113    use std::path::Path;
114
115    use ndk_build::target::Target;
116
117    use crate::{build, BuildConfig};
118
119    #[test]
120    fn build_works() {
121        println!("current dir = {:?}", std::env::current_dir());
122        let config = BuildConfig {
123            lib_name: "testcode".to_string(),
124            arch_list: vec![
125                Target::Arm64V8a.rust_triple().to_owned(),
126                Target::ArmV7a.rust_triple().to_owned(),
127                Target::X86.rust_triple().to_owned(),
128                Target::X86_64.rust_triple().to_owned(),
129            ],
130            is_release: true,
131            target_dir: "target".to_string(),
132            project_dir: Path::new(".").to_path_buf(),
133            sdk_version: 21,
134            rustc_param: "--features rsbind".to_owned(),
135            features_def: vec![],
136        };
137        build(&config).unwrap()
138    }
139}