diff --git a/ndk-build/CHANGELOG.md b/ndk-build/CHANGELOG.md index 8c2dfdb..2f1d330 100644 --- a/ndk-build/CHANGELOG.md +++ b/ndk-build/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Link and `zipalign` all libraries with 16KiB alignment for Android 15+. ([#76](https://github.com/rust-mobile/cargo-apk/pull/76)) + # 0.10.0 (2023-11-30) - Add `android:extractNativeLibs`, `android:usesCleartextTraffic` attributes to the manifest's `Application` element, and `android:alwaysRetainTaskState` to the `Activity` element. ([#15](https://github.com/rust-mobile/cargo-apk/pull/15)) diff --git a/ndk-build/src/apk.rs b/ndk-build/src/apk.rs index 3df2b8f..b69f8a6 100644 --- a/ndk-build/src/apk.rs +++ b/ndk-build/src/apk.rs @@ -65,7 +65,7 @@ impl ApkConfig { self.build_dir.join(format!("{}.apk", self.apk_name)) } - pub fn create_apk(&self) -> Result { + pub fn create_apk(&self) -> Result, NdkError> { std::fs::create_dir_all(&self.build_dir)?; self.manifest.write_to(&self.build_dir)?; @@ -200,6 +200,8 @@ impl<'a> UnalignedApk<'a> { let mut aapt = self.config.build_tool(bin!("aapt"))?; aapt.arg("add"); + // TODO: We might want to disable library compression separately, which allows them to be + // mmap'ed without decompression step after installation. if self.config.disable_aapt_compression { aapt.arg("-0").arg(""); } @@ -218,6 +220,9 @@ impl<'a> UnalignedApk<'a> { zipalign .arg("-f") .arg("-v") + // Force all uncompressed libraries to be 16KiB-aligned for Android 15+ + // TODO: This requires build-tools 35.0.0 + .args(["-P", "16"]) .arg("4") .arg(self.config.unaligned_apk()) .arg(self.config.apk()); diff --git a/ndk-build/src/cargo.rs b/ndk-build/src/cargo.rs index b6459eb..5390a9a 100644 --- a/ndk-build/src/cargo.rs +++ b/ndk-build/src/cargo.rs @@ -69,6 +69,13 @@ pub fn cargo_ndk( rustflags.push_str("-Clink-arg="); rustflags.push_str(&clang_target); + if ndk.version().0 < 28 { + // Link with 16KiB alignment. This is the default since NDK r28: + // https://developer.android.com/guide/practices/page-sizes#compile-r28 + rustflags.push_str(SEP); + rustflags.push_str("-Clink-arg=-Wl,-z,max-page-size=16384"); + } + let ar = ndk.toolchain_bin("ar", target)?; cargo.env(format!("AR_{triple}"), &ar); cargo.env(cargo_env_target_cfg("AR", triple), &ar); @@ -79,7 +86,7 @@ pub fn cargo_ndk( // See https://github.com/rust-lang/rust/pull/85806 for a discussion on why libgcc // is still required even after replacing it with libunwind in the source. // XXX: Add an upper-bound on the Rust version whenever this is not necessary anymore. - if ndk.build_tag() > 7272597 { + if ndk.version().2 > 7272597 { let cargo_apk_link_dir = target_dir .as_ref() .join("cargo-apk-temp-extra-link-libraries"); diff --git a/ndk-build/src/ndk.rs b/ndk-build/src/ndk.rs index ca017c2..72a60e8 100644 --- a/ndk-build/src/ndk.rs +++ b/ndk-build/src/ndk.rs @@ -14,7 +14,7 @@ pub struct Ndk { user_home: PathBuf, ndk_path: PathBuf, build_tools_version: String, - build_tag: u32, + version: (u32, u32, u32), platforms: Vec, } @@ -88,22 +88,24 @@ impl Ndk { let build_tag = std::fs::read_to_string(ndk_path.join("source.properties")) .expect("Failed to read source.properties"); - let build_tag = build_tag + let version = build_tag .split('\n') .find_map(|line| { let (key, value) = line - .split_once('=') + .split_once(" = ") .expect("Failed to parse `key = value` from source.properties"); - if key.trim() == "Pkg.Revision" { - // AOSP writes a constantly-incrementing build version to the patch field. - // This number is incrementing across NDK releases. - let mut parts = value.trim().split('.'); - let _major = parts.next().unwrap(); - let _minor = parts.next().unwrap(); - let patch = parts.next().unwrap(); + if key == "Pkg.Revision" { + let mut p = value.split('.'); + let major = p.next().unwrap().parse().expect("Parse major field"); + let minor = p.next().unwrap().parse().expect("Parse minor field"); + let patch = p.next().unwrap(); // Can have an optional `XXX-beta1` let patch = patch.split_once('-').map_or(patch, |(patch, _beta)| patch); - Some(patch.parse().expect("Failed to parse patch field")) + Some(( + major, + minor, + patch.parse().expect("Failed to parse patch field"), + )) } else { None } @@ -145,7 +147,7 @@ impl Ndk { user_home, ndk_path, build_tools_version, - build_tag, + version, platforms, }) } @@ -162,8 +164,10 @@ impl Ndk { &self.build_tools_version } - pub fn build_tag(&self) -> u32 { - self.build_tag + // WARNING: NDK build tags don't need to be sequential. For example the build tag of NDK r27d + // `27.3.13750724` is ahead of the first r28 release at `28.0.1243356`. + pub fn version(&self) -> (u32, u32, u32) { + self.version } pub fn platforms(&self) -> &[u32] {