Skip to content

Commit 265a1de

Browse files
committed
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.
1 parent e8765fe commit 265a1de

File tree

4 files changed

+57
-1
lines changed

4 files changed

+57
-1
lines changed

cargo-apk/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Unreleased
22

33
- Fixed the library name in case of multiple build artifacts in the Android manifest.
4+
- Work around missing `libgcc` on NDK r23 beta 3 and above, by providing linker script that "redirects" to `libunwind`.
5+
See https://github.com/rust-windowing/android-ndk-rs/issues/149 and https://github.com/rust-lang/rust/pull/85806 for more details.
46

57
# 0.8.1 (2021-08-06)
68

cargo-apk/src/apk.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,11 +161,32 @@ impl<'a> ApkBuilder<'a> {
161161
let target_sdk_version = config.manifest.sdk.target_sdk_version.unwrap();
162162

163163
let mut cargo = cargo_ndk(&config.ndk, *target, target_sdk_version)?;
164-
cargo.arg("build");
164+
cargo.arg("rustc");
165165
if self.cmd.target().is_none() {
166166
cargo.arg("--target").arg(triple);
167167
}
168168
cargo.args(self.cmd.args());
169+
170+
// Workaround for https://github.com/rust-windowing/android-ndk-rs/issues/149:
171+
// Rust (1.56 as of writing) still requires libgcc during linking, but this does
172+
// not ship with the NDK anymore since NDK r23 beta 3.
173+
// See https://github.com/rust-lang/rust/pull/85806 for a discussion on why libgcc
174+
// is still required even after replacing it with libunwind in the source.
175+
// XXX: Add an upper-bound on the Rust version whenever this is not necessary anymore.
176+
if self.ndk.build_tag() > 7272597 {
177+
if !self.cmd.args().contains(&"--".to_owned()) {
178+
cargo.arg("--");
179+
}
180+
let cargo_apk_link_dir = self
181+
.cmd
182+
.target_dir()
183+
.join("cargo-apk-temp-extra-link-libraries");
184+
std::fs::create_dir_all(&cargo_apk_link_dir)?;
185+
std::fs::write(cargo_apk_link_dir.join("libgcc.a"), "INPUT(-lunwind)")
186+
.expect("Failed to write");
187+
cargo.arg("-L").arg(cargo_apk_link_dir);
188+
}
189+
169190
if !cargo.status()?.success() {
170191
return Err(NdkError::CmdFailed(cargo).into());
171192
}

ndk-build/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
# Unreleased
22

3+
- Provide NDK `build_tag` version from `source.properties` in the NDK root.
4+
35
# 0.4.2 (2021-08-06)
46

57
- 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.

ndk-build/src/ndk.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub struct Ndk {
99
sdk_path: PathBuf,
1010
ndk_path: PathBuf,
1111
build_tools_version: String,
12+
build_tag: u32,
1213
platforms: Vec<u32>,
1314
}
1415

@@ -54,6 +55,31 @@ impl Ndk {
5455
.max()
5556
.ok_or(NdkError::BuildToolsNotFound)?;
5657

58+
let build_tag = std::fs::read_to_string(ndk_path.join("source.properties"))
59+
.expect("Failed to read source.properties");
60+
61+
let build_tag = build_tag
62+
.split('\n')
63+
.find_map(|line| {
64+
let (key, value) = line
65+
.split_once('=')
66+
.expect("Failed to parse `key = value` from source.properties");
67+
if key.trim() == "Pkg.Revision" {
68+
// AOSP writes a constantly-incrementing build version to the patch field.
69+
// This number is incrementing across NDK releases.
70+
let mut parts = value.trim().split('.');
71+
let _major = parts.next().unwrap();
72+
let _minor = parts.next().unwrap();
73+
let patch = parts.next().unwrap();
74+
// Can have an optional `XXX-beta1`
75+
let patch = patch.split_once('-').unwrap().0;
76+
Some(patch.parse().unwrap())
77+
} else {
78+
None
79+
}
80+
})
81+
.expect("No `Pkg.Revision` in source.properties");
82+
5783
let ndk_platforms = std::fs::read_to_string(ndk_path.join("build/core/platforms.mk"))?;
5884
let ndk_platforms = ndk_platforms
5985
.split('\n')
@@ -88,6 +114,7 @@ impl Ndk {
88114
sdk_path,
89115
ndk_path,
90116
build_tools_version,
117+
build_tag,
91118
platforms,
92119
})
93120
}
@@ -104,6 +131,10 @@ impl Ndk {
104131
&self.build_tools_version
105132
}
106133

134+
pub fn build_tag(&self) -> u32 {
135+
self.build_tag
136+
}
137+
107138
pub fn platforms(&self) -> &[u32] {
108139
&self.platforms
109140
}

0 commit comments

Comments
 (0)