Skip to content

Commit

Permalink
支持aapt2工具对apk资源的编译和处理;
Browse files Browse the repository at this point in the history
发布1.0.0版本。
  • Loading branch information
SmileSky committed Nov 14, 2024
1 parent 6e5e74b commit 84a7cc0
Show file tree
Hide file tree
Showing 18 changed files with 462 additions and 278 deletions.
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
[workspace.package]
version = "1.0.0"
authors = [
"SmileSky <[email protected]>",
"The Rust Windowing contributors",
]
edition = "2021"
license = "MIT OR Apache-2.0"
keywords = ["android", "ndk", "apk"]
repository = "https://github.com/mzdk100/cargo-apk2.git"
rust-version = "1.82.0"

[workspace.dependencies]
dunce = "1.0.5"
serde = "1.0.214"
Expand Down
6 changes: 6 additions & 0 deletions cargo-apk2/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# 未发布

# 1.0.0 (2024-11-14)

- 将MSRV提升到1.82.0,以反映依赖项更新。
- 支持aapt2工具对apk资源的编译和处理。
- 从cargo-apk更名为cargo-apk2,继续维护和支持。

# 0.10.0 (2023-11-30)

- 将MSRV提升到1.70,以反映依赖项更新。
Expand Down
19 changes: 8 additions & 11 deletions cargo-apk2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
[package]
name = "cargo-apk2"
version = "0.10.0"
authors = [
"SmileSky <[email protected]>",
"The Rust Windowing contributors"
]
edition = "2021"
version.workspace = true
authors.workspace = true
edition.workspace = true
description = "Rust轻松构建安卓应用的Cargo扩展"
license = "MIT OR Apache-2.0"
keywords = ["android", "ndk", "apk"]
repository = "https://github.com/mzdk100/cargo-apk2.git"
rust-version = "1.82.0"
license.workspace = true
keywords.workspace = true
repository.workspace = true
rust-version.workspace = true

[dependencies]
anyhow = "1.0.93"
Expand All @@ -24,7 +21,7 @@ toml = "0.8.19"

[dependencies.ndk-build2]
path = "../ndk-build2"
version = "0.10.0"
version = "1.0.0"

[dependencies.clap]
version = "4.5.20"
Expand Down
13 changes: 7 additions & 6 deletions cargo-apk2/src/apk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use ndk_build2::{
ndk::{Key, Ndk},
target::Target,
};
use std::path::PathBuf;
use std::{env::var_os, path::PathBuf};

pub struct ApkBuilder<'a> {
cmd: &'a Subcommand,
Expand Down Expand Up @@ -77,7 +77,7 @@ impl<'a> ApkBuilder<'a> {
};
let version_code = VersionCode::from_semver(&package_version)?.to_code(1);

// Set default Android manifest values
// 设置默认 Android 清单值
if manifest
.android_manifest
.version_name
Expand Down Expand Up @@ -123,8 +123,7 @@ impl<'a> ApkBuilder<'a> {
});
}

// Export the sole Rust activity on Android S and up, if the user didn't explicitly do so.
// Without this, apps won't start on S+.
// 如果用户未明确执行此操作,则在 Android S 及更高版本上导出 Activity。如果没有此操作,应用将无法在 S+ 上启动。
// https://developer.android.com/about/versions/12/behavior-changes-12#exported
if target_sdk_version >= 31 {
activity.exported.get_or_insert(true);
Expand Down Expand Up @@ -162,7 +161,7 @@ impl<'a> ApkBuilder<'a> {
}

pub fn build(&self, artifact: &Artifact) -> Result<Apk, Error> {
// Set artifact specific manifest default values.
// 设置工件特定的清单默认值。
let mut manifest = self.manifest.android_manifest.clone();

if manifest.package.is_empty() {
Expand Down Expand Up @@ -207,11 +206,13 @@ impl<'a> ApkBuilder<'a> {
.apk_name
.clone()
.unwrap_or_else(|| artifact.name.to_string());
let use_aapt2 = self.manifest.use_aapt2.unwrap_or(true);

let config = ApkConfig {
ndk: self.ndk.clone(),
build_dir: self.build_dir.join(artifact.build_dir()),
apk_name,
use_aapt2,
assets,
resources,
manifest,
Expand Down Expand Up @@ -270,7 +271,7 @@ impl<'a> ApkBuilder<'a> {
);
let password_env = format!("{}_PASSWORD", keystore_env);

let path = std::env::var_os(&keystore_env).map(PathBuf::from);
let path = var_os(&keystore_env).map(PathBuf::from);
let password = std::env::var(&password_env).ok();

let signing_key = match (path, password) {
Expand Down
8 changes: 7 additions & 1 deletion cargo-apk2/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ pub enum Inheritable<T> {
Inherited { workspace: bool },
}

#[derive(Debug)]
pub(crate) struct Manifest {
pub(crate) version: Inheritable<String>,
pub(crate) apk_name: Option<String>,
/// 使用aapt2编译和处理资源(默认开启)
pub use_aapt2: Option<bool>,
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>,
pub(crate) reverse_port_forward: HashMap<String, String>,
pub(crate) strip: StripConfig,
Expand All @@ -43,6 +46,7 @@ impl Manifest {
Ok(Self {
version: package.version,
apk_name: metadata.apk_name,
use_aapt2: metadata.use_aapt2,
android_manifest: metadata.android_manifest,
build_targets: metadata.build_targets,
assets: metadata.assets,
Expand Down Expand Up @@ -94,6 +98,8 @@ pub(crate) struct PackageMetadata {
#[derive(Clone, Debug, Default, Deserialize)]
struct AndroidMetadata {
apk_name: Option<String>,
/// 使用aapt2编译和处理资源(默认开启)
use_aapt2: Option<bool>,
#[serde(flatten)]
android_manifest: AndroidManifest,
#[serde(default)]
Expand Down
4 changes: 4 additions & 0 deletions ndk-build2/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# 未发布

# 1.0.0 (2024-11-14)

- 支持aapt2工具对apk资源的编译和处理。

# 0.10.0 (2023-11-30)

- 在清单的 `Application` 元素中添加 `android:extractNativeLibs``android:usesCleartextTraffic` 属性,并在 `Activity` 元素中添加 `android:alwaysRetainTaskState`。([#15](https://github.com/rust-mobile/cargo-apk/pull/15))
Expand Down
17 changes: 7 additions & 10 deletions ndk-build2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
[package]
name = "ndk-build2"
version = "0.10.0"
authors = [
"SmileSky <[email protected]>",
"The Rust Windowing contributors",
]
edition = "2021"
version.workspace = true
authors.workspace = true
edition.workspace = true
description = "用于构建 Android 二进制文件的实用程序"
license = "MIT OR Apache-2.0"
keywords = ["android", "ndk", "apk"]
repository = "https://github.com/mzdk100/cargo-apk2.git"
rust-version = "1.82.0"
license.workspace = true
keywords.workspace = true
repository.workspace = true
rust-version.workspace = true

[dependencies]
android-build = "0.1.0"
Expand Down
122 changes: 98 additions & 24 deletions ndk-build2/src/apk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,39 @@ use crate::{
ndk::{Key, Ndk},
target::Target,
};
use serde::Serialize;
use std::{
collections::{HashMap, HashSet},
ffi::OsStr,
fs::{copy, create_dir_all, read_dir},
fs::{copy, create_dir_all, read_dir, remove_file, rename},
path::{Path, PathBuf},
process::Command,
};

/// The options for how to treat debug symbols that are present in any `.so`
/// files that are added to the APK.
//noinspection SpellCheckingInspection
/// 如何处理添加到 APK 的任何 `.so` 文件中的调试符号的选项。
///
/// Using [`strip`](https://doc.rust-lang.org/cargo/reference/profiles.html#strip)
/// or [`split-debuginfo`](https://doc.rust-lang.org/cargo/reference/profiles.html#split-debuginfo)
/// in your cargo manifest(s) may cause debug symbols to not be present in a
/// `.so`, which would cause these options to do nothing.
#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Deserialize)]
/// 在您的货物清单中使用
/// [`strip`](https://doc.rust-lang.org/cargo/reference/profiles.html#strip)
/// 或 [`split-debuginfo`](https://doc.rust-lang.org/cargo/reference/profiles.html#split-debuginfo)
/// 可能会导致调试符号不存在于 `.so` 中,从而导致这些选项不执行任何操作。
#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, serde::Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum StripConfig {
/// Does not treat debug symbols specially
/// 不对调试符号进行特殊处理
#[default]
Default,
/// Removes debug symbols from the library before copying it into the APK
/// 在将库复制到 APK 之前,从库中删除调试符号
Strip,
/// Splits the library into into an ELF (`.so`) and DWARF (`.dwarf`). Only the
/// `.so` is copied into the APK
/// 将库拆分为 ELF(`.so`)和 DWARF(`.dwarf`)。只有 `.so` 会被复制到 APK 中
Split,
}

impl Default for StripConfig {
fn default() -> Self {
Self::Default
}
}

pub struct ApkConfig {
pub ndk: Ndk,
pub build_dir: PathBuf,
pub apk_name: String,
pub use_aapt2: bool,
pub assets: Option<PathBuf>,
pub resources: Option<PathBuf>,
pub manifest: AndroidManifest,
Expand All @@ -61,8 +57,7 @@ impl ApkConfig {
.join(format!("{}-unaligned.apk", self.apk_name))
}

/// Retrieves the path of the APK that will be written when [`UnsignedApk::sign`]
/// is invoked
/// 调用 [`UnsignedApk::sign`] 时将写入的 APK 的路径
#[inline]
pub fn apk(&self) -> PathBuf {
self.build_dir.join(format!("{}.apk", self.apk_name))
Expand All @@ -77,7 +72,30 @@ impl ApkConfig {
.sdk
.target_sdk_version
.unwrap_or_else(|| self.ndk.default_target_platform());

if self.use_aapt2 {
let out_dir = self
.build_dir
.join(format!("compiled_{}_resource", self.manifest.package).as_str());
self.aapt2_compile(&out_dir)?;
self.aapt2_link(target_sdk_version)?;
if !self.manifest.application.debuggable.unwrap_or(false) {
self.aapt2_optimize()?;
}
remove_file(out_dir)?;
} else {
self.aapt_package(target_sdk_version)?;
}

Ok(UnalignedApk {
config: self,
pending_libs: HashSet::default(),
})
}

fn aapt_package(&self, target_sdk_version: u32) -> Result<(), NdkError> {
let mut aapt = self.build_tool(bin!("aapt"))?;
println!("Packing apk resources......");
aapt.arg("package")
.arg("-f")
.arg("-F")
Expand All @@ -102,11 +120,67 @@ impl ApkConfig {
if !aapt.status()?.success() {
return Err(NdkError::CmdFailed(aapt));
}
Ok(())
}

Ok(UnalignedApk {
config: self,
pending_libs: HashSet::default(),
})
fn aapt2_compile(&self, out_dir: impl AsRef<OsStr>) -> Result<(), NdkError> {
let mut aapt = self.build_tool(bin!("aapt2"))?;
println!("Compiling apk resources...");
aapt.arg("compile").arg("-o").arg(out_dir);

if let Some(res) = &self.resources {
aapt.arg("--dir").arg(res);
}

if !aapt.status()?.success() {
return Err(NdkError::CmdFailed(aapt));
}
Ok(())
}

fn aapt2_link(&self, target_sdk_version: u32) -> Result<(), NdkError> {
let mut aapt = self.build_tool(bin!("aapt2"))?;
println!("Linking apk resources...");
aapt.arg("link")
.arg("-o")
.arg(self.unaligned_apk())
.arg("--manifest")
.arg("AndroidManifest.xml")
.arg("-I")
.arg(self.ndk.android_jar(target_sdk_version)?);

if self.disable_aapt_compression {
aapt.arg("--no-compress").arg("-0").arg("");
}

if let Some(assets) = &self.assets {
aapt.arg("-A").arg(assets);
}

if !aapt.status()?.success() {
return Err(NdkError::CmdFailed(aapt));
}
Ok(())
}

fn aapt2_optimize(&self) -> Result<(), NdkError> {
let mut aapt = self.build_tool(bin!("aapt2"))?;
println!("Optimizing apk resources...");
let path = self.unaligned_apk();
let input_path = path.parent().unwrap().join(&self.manifest.package);
rename(&path, &input_path)?;
aapt.arg("optimize")
.arg("-o")
.arg(path)
.arg(&input_path)
.arg("--enable-sparse-encoding");

if !aapt.status()?.success() {
remove_file(input_path)?;
return Err(NdkError::CmdFailed(aapt));
}
remove_file(input_path)?;
Ok(())
}
}

Expand Down
14 changes: 9 additions & 5 deletions ndk-build2/src/cargo.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use crate::error::NdkError;
use crate::ndk::Ndk;
use crate::target::Target;
use std::path::Path;
use std::process::Command;
use crate::{
error::NdkError,
ndk::Ndk,
target::Target
};
use std::{
path::Path,
process::Command
};

pub fn cargo_ndk(
ndk: &Ndk,
Expand Down
Loading

0 comments on commit 84a7cc0

Please sign in to comment.