From caeb591038a4c3642188cd17db19dca69a2f9086 Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Tue, 5 Jul 2022 00:05:14 +0200 Subject: [PATCH] cargo-apk: Configure APK signing keystore location through manifest (#299) All builds currently use a fixed debug keystore. This is cumbersome on machines where `keytool` isn't installed, and odd for `release` builds. To make sharing debug APKs more consistent across developers this allows them to check in their `debug.keystore` in a repository and reuse it for everyone. At the same time there's no sensible default-debug keystore for release builds; these require explicit configuration through the manifest (yet nothing withholds the user from passing their debug keystore here, if they so desire - but at least it's explicit). --- cargo-apk/CHANGELOG.md | 2 ++ cargo-apk/README.md | 7 +++++++ cargo-apk/src/apk.rs | 27 ++++++++++++++++++++++---- cargo-apk/src/error.rs | 2 ++ cargo-apk/src/manifest.rs | 41 ++++++++++++++++++++++++++------------- ndk-build/src/ndk.rs | 2 +- 6 files changed, 63 insertions(+), 18 deletions(-) diff --git a/cargo-apk/CHANGELOG.md b/cargo-apk/CHANGELOG.md index 57819aa1..bddfc142 100644 --- a/cargo-apk/CHANGELOG.md +++ b/cargo-apk/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Allow configuration of alternate debug keystore location; require keystore location for release builds. ([#299](https://github.com/rust-windowing/android-ndk-rs/pull/299)) + # 0.9.2 (2022-06-11) - Move NDK r23 `-lgcc` workaround to `ndk_build::cargo::cargo_ndk()`, to also apply to our `cargo apk --` invocations. ([#286](https://github.com/rust-windowing/android-ndk-rs/pull/286)) diff --git a/cargo-apk/README.md b/cargo-apk/README.md index c4861efa..8edca48c 100644 --- a/cargo-apk/README.md +++ b/cargo-apk/README.md @@ -50,6 +50,13 @@ apk_name = "myapp" # according to the specified build_targets. runtime_libs = "path/to/libs_folder" +# Defaults to `$HOME/.android/debug.keystore` for the `dev` profile. Will ONLY generate a new +# debug.keystore if this file does NOT exist. +# A keystore path is always required on the `release` profile. +[package.metadata.android.signing.] +path = "$HOME/.android/debug.keystore" +keystore_password = "android" + # See https://developer.android.com/guide/topics/manifest/uses-sdk-element # # Defaults to a `min_sdk_version` of 23 and `target_sdk_version` of 30 (or lower if the detected NDK doesn't support this). diff --git a/cargo-apk/src/apk.rs b/cargo-apk/src/apk.rs index 6706fb54..4b9f38dc 100644 --- a/cargo-apk/src/apk.rs +++ b/cargo-apk/src/apk.rs @@ -6,7 +6,7 @@ use ndk_build::cargo::{cargo_ndk, VersionCode}; use ndk_build::dylibs::get_libs_search_paths; use ndk_build::error::NdkError; use ndk_build::manifest::{IntentFilter, MetaData}; -use ndk_build::ndk::Ndk; +use ndk_build::ndk::{Key, Ndk}; use ndk_build::target::Target; use std::path::PathBuf; use std::process::Command; @@ -139,6 +139,8 @@ impl<'a> ApkBuilder<'a> { let crate_path = self.cmd.manifest().parent().expect("invalid manifest path"); + let is_debug_profile = *self.cmd.profile() == Profile::Dev; + let assets = self .manifest .assets @@ -167,7 +169,7 @@ impl<'a> ApkBuilder<'a> { assets, resources, manifest, - disable_aapt_compression: self.cmd.profile() == &Profile::Dev, + disable_aapt_compression: is_debug_profile, }; let apk = config.create_apk()?; @@ -181,7 +183,7 @@ impl<'a> ApkBuilder<'a> { .join(artifact.file_name(CrateType::Cdylib, triple)); let mut cargo = cargo_ndk( - &config.ndk, + &self.ndk, *target, self.min_sdk_version(), self.cmd.target_dir(), @@ -212,7 +214,24 @@ impl<'a> ApkBuilder<'a> { } } - Ok(apk.align()?.sign(config.ndk.debug_key()?)?) + let profile_name = match self.cmd.profile() { + Profile::Dev => "dev", + Profile::Release => "release", + Profile::Custom(c) => c.as_str(), + }; + + let signing_key = self.manifest.signing.get(profile_name); + + let signing_key = match (signing_key, is_debug_profile) { + (Some(signing), _) => Key { + path: crate_path.join(&signing.path), + password: signing.keystore_password.clone(), + }, + (None, true) => self.ndk.debug_key()?, + (None, false) => return Err(Error::MissingReleaseKey(profile_name.to_owned())), + }; + + Ok(apk.align()?.sign(signing_key)?) } pub fn run(&self, artifact: &Artifact) -> Result<(), Error> { diff --git a/cargo-apk/src/error.rs b/cargo-apk/src/error.rs index a65fefa6..8dc864f9 100644 --- a/cargo-apk/src/error.rs +++ b/cargo-apk/src/error.rs @@ -14,6 +14,8 @@ pub enum Error { Ndk(#[from] NdkError), #[error(transparent)] Io(#[from] IoError), + #[error("Configure a release keystore via `[package.metadata.android.signing.{0}]`")] + MissingReleaseKey(String), } impl Error { diff --git a/cargo-apk/src/manifest.rs b/cargo-apk/src/manifest.rs index 50c2ea0d..02a42af7 100644 --- a/cargo-apk/src/manifest.rs +++ b/cargo-apk/src/manifest.rs @@ -2,20 +2,25 @@ use crate::error::Error; use ndk_build::manifest::AndroidManifest; use ndk_build::target::Target; use serde::Deserialize; -use std::path::Path; +use std::{ + collections::HashMap, + path::{Path, PathBuf}, +}; -pub struct Manifest { - pub version: String, - pub apk_name: Option, - pub android_manifest: AndroidManifest, - pub build_targets: Vec, - pub assets: Option, - pub resources: Option, - pub runtime_libs: Option, +pub(crate) struct Manifest { + pub(crate) version: String, + pub(crate) apk_name: Option, + pub(crate) android_manifest: AndroidManifest, + pub(crate) build_targets: Vec, + pub(crate) assets: Option, + pub(crate) resources: Option, + pub(crate) runtime_libs: Option, + /// Maps profiles to keystores + pub(crate) signing: HashMap, } impl Manifest { - pub fn parse_from_toml(path: &Path) -> Result { + pub(crate) fn parse_from_toml(path: &Path) -> Result { let contents = std::fs::read_to_string(path)?; let toml: Root = toml::from_str(&contents)?; let metadata = toml @@ -32,6 +37,7 @@ impl Manifest { assets: metadata.assets, resources: metadata.resources, runtime_libs: metadata.runtime_libs, + signing: metadata.signing, }) } } @@ -59,7 +65,16 @@ struct AndroidMetadata { android_manifest: AndroidManifest, #[serde(default)] build_targets: Vec, - assets: Option, - resources: Option, - runtime_libs: Option, + assets: Option, + resources: Option, + runtime_libs: Option, + /// Maps profiles to keystores + #[serde(default)] + signing: HashMap, +} + +#[derive(Clone, Debug, Default, Deserialize)] +pub(crate) struct Signing { + pub(crate) path: PathBuf, + pub(crate) keystore_password: String, } diff --git a/ndk-build/src/ndk.rs b/ndk-build/src/ndk.rs index 3310e1ed..d1c60cde 100644 --- a/ndk-build/src/ndk.rs +++ b/ndk-build/src/ndk.rs @@ -314,7 +314,7 @@ impl Ndk { .arg("-keystore") .arg(&path) .arg("-storepass") - .arg("android") + .arg(&password) .arg("-alias") .arg("androiddebugkey") .arg("-keypass")