From 61795dbe7ccb874f52351200b35c81c8cf26a59b Mon Sep 17 00:00:00 2001 From: Marijn Suijten Date: Wed, 10 Nov 2021 23:04:05 +0100 Subject: [PATCH] cargo-apk: Work around missing libgcc on NDK r23 with linker script Rust still searches for libgcc even though [85806] replaces internal use with libunwind, especially now that the Android NDK (since r23-beta3) doesn't ship with any of gcc anymore. The apparent solution is to build your application with nightly and compile std locally (`-Zbuild-std`), but that is not desired for the majority of users. [7339] suggests to provide a local `libgcc.a` as linker script, which simply redirects linking to `libunwind` instead - and that has proven to work fine so far. Intead of shipping this file with the crate or writing it to an existing link-search directory on the system, we write it to a new directory that can be easily passed or removed to `rustc`, say in the event that a user switches to an older NDK and builds without cleaning. For this we need to switch from `cargo build` to `cargo rustc`, but the existing arguments and desired workflow remain identical. [85806]: https://github.com/rust-lang/rust/pull/85806 [7339]: https://github.com/termux/termux-packages/pull/7339#issuecomment-921581430 --- README.md | 11 +++++++---- cargo-apk/CHANGELOG.md | 2 ++ cargo-apk/src/apk.rs | 23 ++++++++++++++++++++++- ndk-build/CHANGELOG.md | 2 ++ ndk-build/src/ndk.rs | 31 +++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 59b97fa5..f7b52635 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,13 @@ Branch | Version | Status | Working r18 | 18.1.5063045 | _Deprecated_ | :x: r19 | 19.2.5345600 | _Deprecated_ | :heavy_check_mark: r20 | 20.1.5948944 | _Deprecated_ | :heavy_check_mark: -r21 | 21.4.7075529 | LTS | :heavy_check_mark: -r22 | 22.1.7171670 | Rolling Release | :heavy_check_mark: -r23 beta 1/2 | | Beta | :heavy_check_mark: -r23 beta 3 and beyond | | Beta | :x: Breaking on [#149](https://github.com/rust-windowing/android-ndk-rs/issues/149) :x: +r21 | 21.4.7075529 | _Deprecated_ | :heavy_check_mark: +r22 | 22.1.7171670 | _Deprecated_ | :heavy_check_mark: +r23 | beta 1/2 | _Deprecated_ | :heavy_check_mark: +r23 | 23.0.7272597-beta3 | _Deprecated_ | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189) +r23 | 23.1.7779620 | LTS | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189) +r24 | 24.0.7856742-beta1 | Rolling Release | :heavy_check_mark: Workaround in [#189](https://github.com/rust-windowing/android-ndk-rs/pull/189) + ## Hello world diff --git a/cargo-apk/CHANGELOG.md b/cargo-apk/CHANGELOG.md index 162b48ee..553c02e0 100644 --- a/cargo-apk/CHANGELOG.md +++ b/cargo-apk/CHANGELOG.md @@ -1,6 +1,8 @@ # Unreleased - Fixed the library name in case of multiple build artifacts in the Android manifest. +- Work around missing `libgcc` on NDK r23 beta 3 and above, by providing linker script that "redirects" to `libunwind`. + See https://github.com/rust-windowing/android-ndk-rs/issues/149 and https://github.com/rust-lang/rust/pull/85806 for more details. # 0.8.1 (2021-08-06) diff --git a/cargo-apk/src/apk.rs b/cargo-apk/src/apk.rs index c3028672..87af153b 100644 --- a/cargo-apk/src/apk.rs +++ b/cargo-apk/src/apk.rs @@ -161,11 +161,32 @@ impl<'a> ApkBuilder<'a> { let target_sdk_version = config.manifest.sdk.target_sdk_version.unwrap(); let mut cargo = cargo_ndk(&config.ndk, *target, target_sdk_version)?; - cargo.arg("build"); + cargo.arg("rustc"); if self.cmd.target().is_none() { cargo.arg("--target").arg(triple); } cargo.args(self.cmd.args()); + + // Workaround for https://github.com/rust-windowing/android-ndk-rs/issues/149: + // Rust (1.56 as of writing) still requires libgcc during linking, but this does + // not ship with the NDK anymore since NDK r23 beta 3. + // 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 self.ndk.build_tag() > 7272597 { + if !self.cmd.args().contains(&"--".to_owned()) { + cargo.arg("--"); + } + let cargo_apk_link_dir = self + .cmd + .target_dir() + .join("cargo-apk-temp-extra-link-libraries"); + std::fs::create_dir_all(&cargo_apk_link_dir)?; + std::fs::write(cargo_apk_link_dir.join("libgcc.a"), "INPUT(-lunwind)") + .expect("Failed to write"); + cargo.arg("-L").arg(cargo_apk_link_dir); + } + if !cargo.status()?.success() { return Err(NdkError::CmdFailed(cargo).into()); } diff --git a/ndk-build/CHANGELOG.md b/ndk-build/CHANGELOG.md index a15e0b77..be33fb99 100644 --- a/ndk-build/CHANGELOG.md +++ b/ndk-build/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- Provide NDK `build_tag` version from `source.properties` in the NDK root. + # 0.4.2 (2021-08-06) - Pass UNIX path separators to `aapt` on non-UNIX systems, ensuring the resulting separator is compatible with the target device instead of the host platform. diff --git a/ndk-build/src/ndk.rs b/ndk-build/src/ndk.rs index d8480d6b..a53c40e8 100644 --- a/ndk-build/src/ndk.rs +++ b/ndk-build/src/ndk.rs @@ -9,6 +9,7 @@ pub struct Ndk { sdk_path: PathBuf, ndk_path: PathBuf, build_tools_version: String, + build_tag: u32, platforms: Vec, } @@ -54,6 +55,31 @@ impl Ndk { .max() .ok_or(NdkError::BuildToolsNotFound)?; + let build_tag = std::fs::read_to_string(ndk_path.join("source.properties")) + .expect("Failed to read source.properties"); + + let build_tag = build_tag + .split('\n') + .find_map(|line| { + let (key, value) = line + .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(); + // 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")) + } else { + None + } + }) + .expect("No `Pkg.Revision` in source.properties"); + let ndk_platforms = std::fs::read_to_string(ndk_path.join("build/core/platforms.mk"))?; let ndk_platforms = ndk_platforms .split('\n') @@ -88,6 +114,7 @@ impl Ndk { sdk_path, ndk_path, build_tools_version, + build_tag, platforms, }) } @@ -104,6 +131,10 @@ impl Ndk { &self.build_tools_version } + pub fn build_tag(&self) -> u32 { + self.build_tag + } + pub fn platforms(&self) -> &[u32] { &self.platforms }