@@ -4,6 +4,35 @@ use std::path::Path;
44use std:: path:: PathBuf ;
55use std:: process:: Command ;
66
7+ /// Rust target triple -> NDK ABI name (`arm64-v8a`, `armeabi-v7a`, …).
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+ /// Rust target triple -> NDK sysroot lib directory triple.
19+ /// armv7 differs: Rust says `armv7-linux-androideabi`, NDK says `arm-linux-androideabi`.
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+ /// NDK root from `ANDROID_NDK_HOME`, `ANDROID_NDK_ROOT`, or `NDK_HOME`.
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+
736fn 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
@@ -81,13 +146,45 @@ fn main() {
81146 let header = include_path. join ( "bitcoinkernel.h" ) ;
82147
83148 #[ allow( deprecated) ]
84- let bindings = bindgen:: Builder :: default ( )
149+ let mut builder = bindgen:: Builder :: default ( )
85150 . header ( header. to_str ( ) . unwrap ( ) )
86151 . clang_arg ( "-DBITCOINKERNEL_STATIC" )
87152 . rust_target ( bindgen:: RustTarget :: Stable_1_71 )
88- . rust_edition ( RustEdition :: Edition2021 )
89- . generate ( )
90- . expect ( "Unable to generate bindings" ) ;
153+ . rust_edition ( RustEdition :: Edition2021 ) ;
154+
155+ // When cross-compiling for Android, bindgen's host libclang does not
156+ // know the NDK sysroot or target triple. Pass them explicitly so
157+ // system headers like stddef.h are found.
158+ if target_os == "android" {
159+ if let Some ( ndk) = android_ndk_home ( ) {
160+ let host_tag = if cfg ! ( target_os = "macos" ) {
161+ "darwin-x86_64"
162+ } else {
163+ "linux-x86_64"
164+ } ;
165+ let prebuilt = format ! ( "{ndk}/toolchains/llvm/prebuilt/{host_tag}" ) ;
166+ let sysroot = format ! ( "{prebuilt}/sysroot" ) ;
167+ builder = builder
168+ . clang_arg ( format ! ( "--target={target}" ) )
169+ . clang_arg ( format ! ( "--sysroot={sysroot}" ) ) ;
170+
171+ // The NDK's clang resource directory contains compiler builtins
172+ // (stddef.h, stdarg.h, etc.) that the host libclang may lack for
173+ // this target. Add it as a system include path.
174+ let clang_include = Path :: new ( & prebuilt) . join ( "lib" ) . join ( "clang" ) ;
175+ if let Ok ( entries) = std:: fs:: read_dir ( & clang_include) {
176+ for entry in entries. flatten ( ) {
177+ let include = entry. path ( ) . join ( "include" ) ;
178+ if include. is_dir ( ) {
179+ builder = builder. clang_arg ( format ! ( "-isystem{}" , include. display( ) ) ) ;
180+ break ;
181+ }
182+ }
183+ }
184+ }
185+ }
186+
187+ let bindings = builder. generate ( ) . expect ( "Unable to generate bindings" ) ;
91188
92189 let out_path = PathBuf :: from (
93190 env:: var ( "OUT_DIR" ) . expect ( "OUT_DIR was not defined by the cargo environment!" ) ,
@@ -97,14 +194,27 @@ fn main() {
97194 . expect ( "Couldn't write bindings!" ) ;
98195
99196 let compiler = cc:: Build :: new ( ) . get_compiler ( ) ;
100- let target_os = std:: env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
101197
102198 if target_os == "windows" {
103199 println ! ( "cargo:rustc-link-lib=bcrypt" ) ;
104200 println ! ( "cargo:rustc-link-lib=shell32" ) ;
105- }
106-
107- if compiler. is_like_clang ( ) {
201+ } else if target_os == "android" {
202+ // Android NDK ships libc++_static.a and libc++abi.a in the
203+ // per-architecture sysroot directory (not the API-level subdirectory).
204+ if let Some ( ndk) = android_ndk_home ( ) {
205+ let ndk_triple = android_sysroot_triple ( & target) ;
206+ let host_tag = if cfg ! ( target_os = "macos" ) {
207+ "darwin-x86_64"
208+ } else {
209+ "linux-x86_64"
210+ } ;
211+ let ndk_lib_dir =
212+ format ! ( "{ndk}/toolchains/llvm/prebuilt/{host_tag}/sysroot/usr/lib/{ndk_triple}" ) ;
213+ println ! ( "cargo:rustc-link-search=native={ndk_lib_dir}" ) ;
214+ }
215+ println ! ( "cargo:rustc-link-lib=static=c++_static" ) ;
216+ println ! ( "cargo:rustc-link-lib=static=c++abi" ) ;
217+ } else if compiler. is_like_clang ( ) {
108218 if target_os == "macos" {
109219 println ! ( "cargo:rustc-link-lib=dylib=c++" ) ;
110220 } else {
0 commit comments