@@ -4,6 +4,35 @@ use std::path::Path;
44use std:: path:: PathBuf ;
55use 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+
736fn main ( ) {
837 let bitcoin_dir = Path :: new ( "bitcoin" ) ;
938 let out_dir = env:: var ( "OUT_DIR" ) . unwrap ( ) ;
@@ -17,7 +46,12 @@ fn main() {
1746
1847 let build_config = "RelWithDebInfo" ;
1948
20- Command :: new ( "cmake" )
49+ let target = env:: var ( "TARGET" ) . unwrap ( ) ;
50+ let target_os = env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
51+ let is_android = target_os == "android" ;
52+
53+ let mut cmake_configure = Command :: new ( "cmake" ) ;
54+ cmake_configure
2155 . arg ( "-B" )
2256 . arg ( & build_dir)
2357 . arg ( "-S" )
@@ -40,9 +74,33 @@ fn main() {
4074 . arg ( "-DBUILD_SHARED_LIBS=OFF" )
4175 . arg ( "-DCMAKE_INSTALL_LIBDIR=lib" )
4276 . arg ( "-DENABLE_IPC=OFF" )
43- . arg ( format ! ( "-DCMAKE_INSTALL_PREFIX={}" , install_dir. display( ) ) )
44- . status ( )
45- . unwrap ( ) ;
77+ . arg ( format ! ( "-DCMAKE_INSTALL_PREFIX={}" , install_dir. display( ) ) ) ;
78+
79+ if is_android {
80+ let ndk =
81+ android_ndk_home ( ) . expect ( "Android target detected but ANDROID_NDK_HOME is not set" ) ;
82+ let toolchain_file = format ! ( "{ndk}/build/cmake/android.toolchain.cmake" ) ;
83+ let abi =
84+ android_abi ( & target) . unwrap_or_else ( || panic ! ( "unsupported Android target: {target}" ) ) ;
85+ // API level 24+ is required because Bitcoin Core uses getifaddrs
86+ // which was introduced in Android API 24 (Nougat).
87+ let api_level = env:: var ( "ANDROID_API_LEVEL" ) . unwrap_or_else ( |_| "24" . to_string ( ) ) ;
88+
89+ cmake_configure
90+ . arg ( format ! ( "-DCMAKE_TOOLCHAIN_FILE={toolchain_file}" ) )
91+ . arg ( format ! ( "-DANDROID_ABI={abi}" ) )
92+ . arg ( format ! ( "-DANDROID_PLATFORM=android-{api_level}" ) )
93+ . arg ( "-DCMAKE_SYSTEM_NAME=Android" )
94+ . arg ( format ! ( "-DCMAKE_ANDROID_ARCH_ABI={abi}" ) )
95+ . arg ( format ! ( "-DCMAKE_SYSTEM_VERSION={api_level}" ) )
96+ . arg ( format ! ( "-DCMAKE_ANDROID_NDK={ndk}" ) )
97+ // The Android NDK toolchain sets CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
98+ // to ONLY, which prevents cmake from finding host packages via
99+ // CMAKE_PREFIX_PATH. Override it so Boost headers can be located.
100+ . arg ( "-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH" ) ;
101+ }
102+
103+ cmake_configure. status ( ) . unwrap ( ) ;
46104
47105 let num_jobs = env:: var ( "NUM_JOBS" )
48106 . ok ( )
@@ -80,10 +138,58 @@ fn main() {
80138 let include_path = install_dir. join ( "include" ) ;
81139 let header = include_path. join ( "bitcoinkernel.h" ) ;
82140
83- #[ allow( deprecated) ]
84- let bindings = bindgen:: Builder :: default ( )
141+ let mut builder = bindgen:: Builder :: default ( )
85142 . header ( header. to_str ( ) . unwrap ( ) )
86- . clang_arg ( "-DBITCOINKERNEL_STATIC" )
143+ . clang_arg ( "-DBITCOINKERNEL_STATIC" ) ;
144+
145+ if is_android {
146+ builder = builder. clang_arg ( format ! ( "--target={target}" ) ) ;
147+ if let Some ( ndk) = android_ndk_home ( ) {
148+ let api_level = env:: var ( "ANDROID_API_LEVEL" ) . unwrap_or_else ( |_| "24" . to_string ( ) ) ;
149+ let sysroot = format ! ( "{ndk}/toolchains/llvm/prebuilt/linux-x86_64/sysroot" ) ;
150+ let ndk_triple = android_sysroot_triple ( & target) ;
151+
152+ // Suppress host system headers so only NDK headers are used.
153+ builder = builder. clang_arg ( "-nostdinc" ) ;
154+
155+ // Add clang builtin headers (stddef.h, stdarg.h, etc.) from the
156+ // resource directory. Must come before NDK sysroot includes.
157+ if let Ok ( libclang) = env:: var ( "LIBCLANG_PATH" ) {
158+ let libclang_path = Path :: new ( & libclang) ;
159+ let candidates = [
160+ libclang_path. to_path_buf ( ) ,
161+ libclang_path
162+ . parent ( )
163+ . map ( |p| p. to_path_buf ( ) )
164+ . unwrap_or_default ( ) ,
165+ ] ;
166+ for base in & candidates {
167+ let clang_dir = base. join ( "clang" ) ;
168+ if let Ok ( entries) = std:: fs:: read_dir ( & clang_dir) {
169+ for entry in entries. flatten ( ) {
170+ let resource_dir = entry. path ( ) ;
171+ if resource_dir. join ( "include" ) . join ( "stddef.h" ) . exists ( ) {
172+ builder = builder. clang_arg ( format ! (
173+ "-isystem{}" ,
174+ resource_dir. join( "include" ) . display( )
175+ ) ) ;
176+ break ;
177+ }
178+ }
179+ }
180+ }
181+ }
182+
183+ builder = builder
184+ . clang_arg ( format ! ( "--sysroot={sysroot}" ) )
185+ . clang_arg ( format ! ( "-isystem{sysroot}/usr/include" ) )
186+ . clang_arg ( format ! ( "-isystem{sysroot}/usr/include/{ndk_triple}" ) )
187+ . clang_arg ( format ! ( "-D__ANDROID_API__={api_level}" ) ) ;
188+ }
189+ }
190+
191+ #[ allow( deprecated) ]
192+ let bindings = builder
87193 . rust_target ( bindgen:: RustTarget :: Stable_1_71 )
88194 . rust_edition ( RustEdition :: Edition2021 )
89195 . generate ( )
@@ -97,14 +203,22 @@ fn main() {
97203 . expect ( "Couldn't write bindings!" ) ;
98204
99205 let compiler = cc:: Build :: new ( ) . get_compiler ( ) ;
100- let target_os = std:: env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
101206
102207 if target_os == "windows" {
103208 println ! ( "cargo:rustc-link-lib=bcrypt" ) ;
104209 println ! ( "cargo:rustc-link-lib=shell32" ) ;
105- }
106-
107- if compiler. is_like_clang ( ) {
210+ } else if is_android {
211+ // Android NDK ships libc++_static.a and libc++abi.a in the
212+ // per-architecture sysroot directory (not the API-level subdirectory).
213+ if let Some ( ndk) = android_ndk_home ( ) {
214+ let ndk_triple = android_sysroot_triple ( & target) ;
215+ let ndk_lib_dir =
216+ format ! ( "{ndk}/toolchains/llvm/prebuilt/linux-x86_64/sysroot/usr/lib/{ndk_triple}" ) ;
217+ println ! ( "cargo:rustc-link-search=native={ndk_lib_dir}" ) ;
218+ }
219+ println ! ( "cargo:rustc-link-lib=static=c++_static" ) ;
220+ println ! ( "cargo:rustc-link-lib=static=c++abi" ) ;
221+ } else if compiler. is_like_clang ( ) {
108222 if target_os == "macos" {
109223 println ! ( "cargo:rustc-link-lib=dylib=c++" ) ;
110224 } else {
0 commit comments