Skip to content

Commit

Permalink
cargo-apk: Configure APK signing keystore location through manifest
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
MarijnS95 committed Jun 17, 2022
1 parent 60e34d1 commit dfc3945
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 18 deletions.
2 changes: 2 additions & 0 deletions cargo-apk/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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))
Expand Down
7 changes: 7 additions & 0 deletions cargo-apk/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.<profile>]
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).
Expand Down
31 changes: 27 additions & 4 deletions cargo-apk/src/apk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -139,6 +139,12 @@ impl<'a> ApkBuilder<'a> {

let crate_path = self.cmd.manifest().parent().expect("invalid manifest path");

let is_debug_profile = match self.cmd.profile() {
Profile::Dev => true,
Profile::Release => false,
Profile::Custom(c) => todo!("Read `debug` field from `[profile.{}]`?", c),
};

let assets = self
.manifest
.assets
Expand Down Expand Up @@ -167,7 +173,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()?;

Expand All @@ -181,7 +187,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(),
Expand Down Expand Up @@ -212,7 +218,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> {
Expand Down
2 changes: 2 additions & 0 deletions cargo-apk/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
41 changes: 28 additions & 13 deletions cargo-apk/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
pub android_manifest: AndroidManifest,
pub build_targets: Vec<Target>,
pub assets: Option<String>,
pub resources: Option<String>,
pub runtime_libs: Option<String>,
pub(crate) struct Manifest {
pub(crate) version: String,
pub(crate) apk_name: Option<String>,
pub(crate) android_manifest: AndroidManifest,
pub(crate) build_targets: Vec<Target>,
pub(crate) assets: Option<PathBuf>,
pub(crate) resources: Option<PathBuf>,
pub(crate) runtime_libs: Option<PathBuf>,
/// Maps profiles to keystores
pub(crate) signing: HashMap<String, Signing>,
}

impl Manifest {
pub fn parse_from_toml(path: &Path) -> Result<Self, Error> {
pub(crate) fn parse_from_toml(path: &Path) -> Result<Self, Error> {
let contents = std::fs::read_to_string(path)?;
let toml: Root = toml::from_str(&contents)?;
let metadata = toml
Expand All @@ -32,6 +37,7 @@ impl Manifest {
assets: metadata.assets,
resources: metadata.resources,
runtime_libs: metadata.runtime_libs,
signing: metadata.signing,
})
}
}
Expand Down Expand Up @@ -59,7 +65,16 @@ struct AndroidMetadata {
android_manifest: AndroidManifest,
#[serde(default)]
build_targets: Vec<Target>,
assets: Option<String>,
resources: Option<String>,
runtime_libs: Option<String>,
assets: Option<PathBuf>,
resources: Option<PathBuf>,
runtime_libs: Option<PathBuf>,
/// Maps profiles to keystores
#[serde(default)]
signing: HashMap<String, Signing>,
}

#[derive(Clone, Debug, Default, Deserialize)]
pub(crate) struct Signing {
pub(crate) path: PathBuf,
pub(crate) keystore_password: String,
}
2 changes: 1 addition & 1 deletion ndk-build/src/ndk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ impl Ndk {
.arg("-keystore")
.arg(&path)
.arg("-storepass")
.arg("android")
.arg(&password)
.arg("-alias")
.arg("androiddebugkey")
.arg("-keypass")
Expand Down

0 comments on commit dfc3945

Please sign in to comment.