Skip to content

Commit 0f3252f

Browse files
committed
feat: android support
Add Android cross-compilation support to the build system. Detects Android targets, configures cmake with the NDK toolchain, and links the NDK C++ static libraries.
1 parent 356c1bd commit 0f3252f

2 files changed

Lines changed: 85 additions & 6 deletions

File tree

libbitcoinkernel-sys/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Added
11+
- Added build support for android
1112
- New `btck_ConsensusParams` opaque type for holding consensus parameters
1213
- New `btck_chain_parameters_get_consensus_params` for extracting consensus params from `btck_ChainParameters` (lifetime-bound to the chain parameters object)
1314
- New `btck_block_check` for context-free block validation (size limits, coinbase structure, sigop limits, with optional POW and merkle-root checks via `btck_BlockCheckFlags`)

libbitcoinkernel-sys/build.rs

Lines changed: 84 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,35 @@ use std::path::Path;
44
use std::path::PathBuf;
55
use std::process::Command;
66

7+
/// Maps Rust target triples to Android ABI names used by the NDK.
8+
fn android_abi(target: &str) -> Option<&'static str> {
9+
match target {
10+
t if t.contains("aarch64") => Some("arm64-v8a"),
11+
t if t.contains("armv7") => Some("armeabi-v7a"),
12+
t if t.contains("x86_64") => Some("x86_64"),
13+
t if t.contains("i686") => Some("x86"),
14+
_ => None,
15+
}
16+
}
17+
18+
/// Maps Rust target triples to the NDK sysroot directory names.
19+
/// These differ from Rust triples for armv7 targets.
20+
fn android_sysroot_triple(target: &str) -> &str {
21+
if target.starts_with("armv7") {
22+
"arm-linux-androideabi"
23+
} else {
24+
target
25+
}
26+
}
27+
28+
/// Detect whether we are cross-compiling for Android and return the NDK home path if so.
29+
fn android_ndk_home() -> Option<String> {
30+
env::var("ANDROID_NDK_HOME")
31+
.or_else(|_| env::var("ANDROID_NDK_ROOT"))
32+
.or_else(|_| env::var("NDK_HOME"))
33+
.ok()
34+
}
35+
736
fn main() {
837
let bitcoin_dir = Path::new("bitcoin");
938
let out_dir = env::var("OUT_DIR").unwrap();
@@ -17,7 +46,8 @@ fn main() {
1746

1847
let build_config = "RelWithDebInfo";
1948

20-
Command::new("cmake")
49+
let mut cmake_configure = Command::new("cmake");
50+
cmake_configure
2151
.arg("-B")
2252
.arg(&build_dir)
2353
.arg("-S")
@@ -40,7 +70,42 @@ fn main() {
4070
.arg("-DBUILD_SHARED_LIBS=OFF")
4171
.arg("-DCMAKE_INSTALL_LIBDIR=lib")
4272
.arg("-DENABLE_IPC=OFF")
43-
.arg(format!("-DCMAKE_INSTALL_PREFIX={}", install_dir.display()))
73+
.arg(format!("-DCMAKE_INSTALL_PREFIX={}", install_dir.display()));
74+
75+
let target = env::var("TARGET").unwrap();
76+
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
77+
78+
if target_os == "android" {
79+
let ndk =
80+
android_ndk_home().expect("Android target detected but ANDROID_NDK_HOME is not set");
81+
let toolchain_file = format!("{ndk}/build/cmake/android.toolchain.cmake");
82+
assert!(
83+
Path::new(&toolchain_file).exists(),
84+
"Android NDK toolchain file not found at {toolchain_file}. \
85+
Check that ANDROID_NDK_HOME points to a valid NDK installation"
86+
);
87+
let abi =
88+
android_abi(&target).unwrap_or_else(|| panic!("unsupported Android target: {target}"));
89+
90+
// API level 24+ is required because Bitcoin Core uses getifaddrs
91+
// which was introduced in Android API 24 (Nougat).
92+
let api_level = env::var("ANDROID_API_LEVEL").unwrap_or_else(|_| "24".to_string());
93+
94+
cmake_configure
95+
.arg(format!("-DCMAKE_TOOLCHAIN_FILE={toolchain_file}"))
96+
.arg(format!("-DANDROID_ABI={abi}"))
97+
.arg(format!("-DANDROID_PLATFORM=android-{api_level}"))
98+
.arg("-DCMAKE_SYSTEM_NAME=Android")
99+
.arg(format!("-DCMAKE_ANDROID_ARCH_ABI={abi}"))
100+
.arg(format!("-DCMAKE_SYSTEM_VERSION={api_level}"))
101+
.arg(format!("-DCMAKE_ANDROID_NDK={ndk}"))
102+
// The Android NDK toolchain sets CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
103+
// to ONLY, which prevents cmake from finding host packages via
104+
// CMAKE_PREFIX_PATH. Override it so Boost headers can be located.
105+
.arg("-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH");
106+
}
107+
108+
cmake_configure
44109
.status()
45110
.expect("cmake should be installed and available in PATH");
46111

@@ -97,14 +162,27 @@ fn main() {
97162
.expect("Couldn't write bindings!");
98163

99164
let compiler = cc::Build::new().get_compiler();
100-
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap();
101165

102166
if target_os == "windows" {
103167
println!("cargo:rustc-link-lib=bcrypt");
104168
println!("cargo:rustc-link-lib=shell32");
105-
}
106-
107-
if compiler.is_like_clang() {
169+
} else if target_os == "android" {
170+
// Android NDK ships libc++_static.a and libc++abi.a in the
171+
// per-architecture sysroot directory (not the API-level subdirectory).
172+
if let Some(ndk) = android_ndk_home() {
173+
let ndk_triple = android_sysroot_triple(&target);
174+
let host_tag = if cfg!(target_os = "macos") {
175+
"darwin-x86_64"
176+
} else {
177+
"linux-x86_64"
178+
};
179+
let ndk_lib_dir =
180+
format!("{ndk}/toolchains/llvm/prebuilt/{host_tag}/sysroot/usr/lib/{ndk_triple}");
181+
println!("cargo:rustc-link-search=native={ndk_lib_dir}");
182+
}
183+
println!("cargo:rustc-link-lib=static=c++_static");
184+
println!("cargo:rustc-link-lib=static=c++abi");
185+
} else if compiler.is_like_clang() {
108186
if target_os == "macos" {
109187
println!("cargo:rustc-link-lib=dylib=c++");
110188
} else {

0 commit comments

Comments
 (0)