cli/commands/build/
android.rs

1use super::{BuildContext, SharedBuildCommand};
2use crate::error::*;
3use android_manifest::AndroidManifest;
4use clap::Clap;
5use creator_tools::{
6    commands::android,
7    tools::{AndroidNdk, AndroidSdk},
8    types::*,
9    utils::Config,
10};
11use std::path::PathBuf;
12
13#[derive(Clap, Clone, Debug)]
14pub struct AndroidBuildCommand {
15    #[clap(flatten)]
16    pub shared: SharedBuildCommand,
17    /// Build for the given android architecture.
18    /// Supported targets are: `armv7-linux-androideabi`, `aarch64-linux-android`, `i686-linux-android`, `x86_64-linux-android`
19    #[clap(long, default_value = "aarch64-linux-android")]
20    pub target: Vec<AndroidTarget>,
21    /// Path to the signing key
22    #[clap(long)]
23    pub sign_key_path: Option<PathBuf>,
24    /// Signing key password
25    #[clap(long)]
26    pub sign_key_pass: Option<String>,
27}
28
29impl AndroidBuildCommand {
30    pub fn run(&self, config: &Config) -> Result<()> {
31        if self.sign_key_path.is_some() && self.sign_key_pass.is_none() {
32            config
33                .shell()
34                .warn("You provided a signing key but not password - set password please by providing `sign_key_pass` flag")?;
35        }
36
37        let context = BuildContext::new(config, self.shared.target_dir.clone())?;
38        self.execute(config, &context)?;
39        Ok(())
40    }
41
42    pub fn execute(
43        &self,
44        config: &Config,
45        context: &BuildContext,
46    ) -> Result<(AndroidManifest, AndroidSdk, PathBuf)> {
47        let project_path = context.project_path.clone();
48        let target_dir = context.target_dir.clone();
49        let profile = self.shared.profile();
50        let (target, package_name) = if let Some(example) = &self.shared.example {
51            (Target::Example(example.clone()), example.clone())
52        } else {
53            (Target::Lib, context.package_name())
54        };
55        config.status_message("Starting build process", &package_name)?;
56        let sdk = AndroidSdk::from_env()?;
57        let ndk = AndroidNdk::from_env(Some(sdk.sdk_path()))?;
58        let build_targets = context.android_build_targets(&self.target);
59        let target_sdk_version = context.target_sdk_version(&sdk);
60        config.status_message("Generating", "AndroidManifest.xml")?;
61        let android_manifest =
62            context.gen_android_manifest(&sdk, &package_name, profile.is_debug())?;
63        let apk_build_dir = target_dir.join("android").join(&profile);
64        let manifest_path = android::save_android_manifest(&apk_build_dir, &android_manifest)?;
65        let mut compiled_libs = Vec::new();
66        for build_target in build_targets.iter() {
67            let lib_name = format!("lib{}.so", package_name.replace("-", "_"));
68            let rust_triple = build_target.rust_triple();
69            config.status_message("Compiling for architecture", rust_triple)?;
70            android::compile_rust_for_android(
71                &ndk,
72                target.clone(),
73                *build_target,
74                &project_path,
75                profile,
76                self.shared.features.clone(),
77                self.shared.all_features,
78                self.shared.no_default_features,
79                target_sdk_version,
80            )?;
81            let out_dir = target_dir.join(build_target.rust_triple()).join(&profile);
82            let compiled_lib = out_dir.join(lib_name);
83            compiled_libs.push((compiled_lib, build_target));
84        }
85        config.status_message("Generating", "unaligned APK file")?;
86        let unaligned_apk_path = android::gen_unaligned_apk(
87            &sdk,
88            &project_path,
89            &apk_build_dir,
90            &manifest_path,
91            context.android_assets(),
92            context.android_res(),
93            android_manifest
94                .application
95                .label
96                .clone()
97                .unwrap()
98                .to_string(),
99            target_sdk_version,
100        )?;
101        config.status("Adding libs into APK file")?;
102        for (compiled_lib, build_target) in compiled_libs {
103            android::add_libs_into_apk(
104                &sdk,
105                &ndk,
106                &unaligned_apk_path,
107                &compiled_lib,
108                *build_target,
109                profile,
110                android_manifest
111                    .uses_sdk
112                    .as_ref()
113                    .unwrap()
114                    .min_sdk_version
115                    .unwrap_or(9),
116                &apk_build_dir,
117                &target_dir,
118            )?;
119        }
120        config.status("Aligning APK file")?;
121        let aligned_apk_path = android::align_apk(
122            &sdk,
123            &unaligned_apk_path,
124            &android_manifest
125                .application
126                .label
127                .clone()
128                .unwrap()
129                .to_string(),
130            &apk_build_dir,
131        )?;
132        let key = if let Some(path) = self.sign_key_path.clone() {
133            android::Key {
134                path,
135                password: self.sign_key_pass.clone().unwrap(),
136            }
137        } else {
138            config.status_message("Generating", "debug signing key")?;
139            android::gen_debug_key().unwrap()
140        };
141        config.status("Signing APK file")?;
142        android::sign_apk(&sdk, &aligned_apk_path, key).unwrap();
143        config.status("Build finished successfully")?;
144        Ok((android_manifest, sdk, aligned_apk_path))
145    }
146}