@@ -45,7 +45,13 @@ fn parse_target_os() -> Result<(TargetOs, String), String> {
4545 } else {
4646 Ok ( ( TargetOs :: Apple ( AppleVariant :: Other ) , target) )
4747 }
48- } else if target. contains ( "android" ) {
48+ } else if target. contains ( "android" )
49+ || target == "aarch64-linux-android"
50+ || target == "armv7-linux-androideabi"
51+ || target == "i686-linux-android"
52+ || target == "x86_64-linux-android"
53+ {
54+ // Handle both full android targets and short names like arm64-v8a that cargo ndk might use
4955 Ok ( ( TargetOs :: Android , target) )
5056 } else if target. contains ( "linux" ) {
5157 Ok ( ( TargetOs :: Linux , target) )
@@ -162,6 +168,28 @@ fn macos_link_search_path() -> Option<String> {
162168 None
163169}
164170
171+ fn validate_android_ndk ( ndk_path : & str ) -> Result < ( ) , String > {
172+ let ndk_path = Path :: new ( ndk_path) ;
173+
174+ if !ndk_path. exists ( ) {
175+ return Err ( format ! (
176+ "Android NDK path does not exist: {}" ,
177+ ndk_path. display( )
178+ ) ) ;
179+ }
180+
181+ let toolchain_file = ndk_path. join ( "build/cmake/android.toolchain.cmake" ) ;
182+ if !toolchain_file. exists ( ) {
183+ return Err ( format ! (
184+ "Android NDK toolchain file not found: {}\n \
185+ This indicates an incomplete NDK installation.",
186+ toolchain_file. display( )
187+ ) ) ;
188+ }
189+
190+ Ok ( ( ) )
191+ }
192+
165193fn is_hidden ( e : & DirEntry ) -> bool {
166194 e. file_name ( )
167195 . to_str ( )
@@ -233,7 +261,7 @@ fn main() {
233261 ) ;
234262
235263 // Bindings
236- let bindings = bindgen:: Builder :: default ( )
264+ let mut bindings_builder = bindgen:: Builder :: default ( )
237265 . header ( "wrapper.h" )
238266 . clang_arg ( format ! ( "-I{}" , llama_src. join( "include" ) . display( ) ) )
239267 . clang_arg ( format ! ( "-I{}" , llama_src. join( "ggml/include" ) . display( ) ) )
@@ -244,7 +272,149 @@ fn main() {
244272 . allowlist_type ( "ggml_.*" )
245273 . allowlist_function ( "llama_.*" )
246274 . allowlist_type ( "llama_.*" )
247- . prepend_enum_name ( false )
275+ . prepend_enum_name ( false ) ;
276+
277+ // Configure Android-specific bindgen settings
278+ if matches ! ( target_os, TargetOs :: Android ) {
279+ // Detect Android NDK from environment variables
280+ let android_ndk = env:: var ( "ANDROID_NDK" )
281+ . or_else ( |_| env:: var ( "ANDROID_NDK_ROOT" ) )
282+ . or_else ( |_| env:: var ( "NDK_ROOT" ) )
283+ . or_else ( |_| env:: var ( "CARGO_NDK_ANDROID_NDK" ) )
284+ . or_else ( |_| {
285+ // Try to auto-detect NDK from Android SDK
286+ if let Some ( home) = env:: home_dir ( ) {
287+ let android_home = env:: var ( "ANDROID_HOME" )
288+ . or_else ( |_| env:: var ( "ANDROID_SDK_ROOT" ) )
289+ . unwrap_or_else ( |_| format ! ( "{}/Android/Sdk" , home. display( ) ) ) ;
290+
291+ let ndk_dir = format ! ( "{}/ndk" , android_home) ;
292+ if let Ok ( entries) = std:: fs:: read_dir ( & ndk_dir) {
293+ let mut versions: Vec < _ > = entries
294+ . filter_map ( |e| e. ok ( ) )
295+ . filter ( |e| e. file_type ( ) . map ( |t| t. is_dir ( ) ) . unwrap_or ( false ) )
296+ . filter_map ( |e| e. file_name ( ) . to_str ( ) . map ( |s| s. to_string ( ) ) )
297+ . collect ( ) ;
298+ versions. sort ( ) ;
299+ if let Some ( latest) = versions. last ( ) {
300+ return Ok ( format ! ( "{}/{}" , ndk_dir, latest) ) ;
301+ }
302+ }
303+ }
304+ Err ( env:: VarError :: NotPresent )
305+ } )
306+ . unwrap_or_else ( |_| {
307+ panic ! (
308+ "Android NDK not found. Please set one of: ANDROID_NDK, NDK_ROOT, ANDROID_NDK_ROOT\n \
309+ Current target: {}\n \
310+ Download from: https://developer.android.com/ndk/downloads",
311+ target_triple
312+ ) ;
313+ } ) ;
314+
315+ // Get Android API level
316+ let android_api = env:: var ( "ANDROID_API_LEVEL" )
317+ . or_else ( |_| env:: var ( "ANDROID_PLATFORM" ) . map ( |p| p. replace ( "android-" , "" ) ) )
318+ . or_else ( |_| env:: var ( "CARGO_NDK_ANDROID_PLATFORM" ) . map ( |p| p. replace ( "android-" , "" ) ) )
319+ . unwrap_or_else ( |_| "28" . to_string ( ) ) ;
320+
321+ // Determine host platform
322+ let host_tag = if cfg ! ( target_os = "macos" ) {
323+ "darwin-x86_64"
324+ } else if cfg ! ( target_os = "linux" ) {
325+ "linux-x86_64"
326+ } else if cfg ! ( target_os = "windows" ) {
327+ "windows-x86_64"
328+ } else {
329+ panic ! ( "Unsupported host platform for Android NDK" ) ;
330+ } ;
331+
332+ // Map Rust target to Android architecture
333+ let android_target_prefix = if target_triple. contains ( "aarch64" ) {
334+ "aarch64-linux-android"
335+ } else if target_triple. contains ( "armv7" ) {
336+ "arm-linux-androideabi"
337+ } else if target_triple. contains ( "x86_64" ) {
338+ "x86_64-linux-android"
339+ } else if target_triple. contains ( "i686" ) {
340+ "i686-linux-android"
341+ } else {
342+ panic ! ( "Unsupported Android target: {}" , target_triple) ;
343+ } ;
344+
345+ // Setup Android toolchain paths
346+ let toolchain_path = format ! ( "{}/toolchains/llvm/prebuilt/{}" , android_ndk, host_tag) ;
347+ let sysroot = format ! ( "{}/sysroot" , toolchain_path) ;
348+
349+ // Validate toolchain existence
350+ if !std:: path:: Path :: new ( & toolchain_path) . exists ( ) {
351+ panic ! (
352+ "Android NDK toolchain not found at: {}\n \
353+ Please ensure you have the correct Android NDK for your platform.",
354+ toolchain_path
355+ ) ;
356+ }
357+
358+ // Find clang builtin includes
359+ let clang_builtin_includes = {
360+ let clang_lib_path = format ! ( "{}/lib/clang" , toolchain_path) ;
361+ std:: fs:: read_dir ( & clang_lib_path) . ok ( ) . and_then ( |entries| {
362+ entries
363+ . filter_map ( |e| e. ok ( ) )
364+ . find ( |entry| {
365+ entry. file_type ( ) . map ( |t| t. is_dir ( ) ) . unwrap_or ( false )
366+ && entry
367+ . file_name ( )
368+ . to_str ( )
369+ . map ( |name| name. chars ( ) . next ( ) . unwrap_or ( '0' ) . is_ascii_digit ( ) )
370+ . unwrap_or ( false )
371+ } )
372+ . and_then ( |entry| {
373+ let include_path =
374+ format ! ( "{}/{}/include" , clang_lib_path, entry. file_name( ) . to_str( ) ?) ;
375+ if std:: path:: Path :: new ( & include_path) . exists ( ) {
376+ Some ( include_path)
377+ } else {
378+ None
379+ }
380+ } )
381+ } )
382+ } ;
383+
384+ // Configure bindgen for Android
385+ bindings_builder = bindings_builder
386+ . clang_arg ( format ! ( "--target={}" , target_triple) )
387+ . clang_arg ( format ! ( "--sysroot={}" , sysroot) )
388+ . clang_arg ( format ! ( "-D__ANDROID_API__={}" , android_api) )
389+ . clang_arg ( "-D__ANDROID__" ) ;
390+
391+ // Add include paths in correct order
392+ if let Some ( ref builtin_includes) = clang_builtin_includes {
393+ bindings_builder = bindings_builder
394+ . clang_arg ( "-isystem" )
395+ . clang_arg ( builtin_includes) ;
396+ }
397+
398+ bindings_builder = bindings_builder
399+ . clang_arg ( "-isystem" )
400+ . clang_arg ( format ! ( "{}/usr/include/{}" , sysroot, android_target_prefix) )
401+ . clang_arg ( "-isystem" )
402+ . clang_arg ( format ! ( "{}/usr/include" , sysroot) )
403+ . clang_arg ( "-include" )
404+ . clang_arg ( "stdbool.h" )
405+ . clang_arg ( "-include" )
406+ . clang_arg ( "stdint.h" ) ;
407+
408+ // Set additional clang args for cargo ndk compatibility
409+ if env:: var ( "CARGO_SUBCOMMAND" ) . as_deref ( ) == Ok ( "ndk" ) {
410+ std:: env:: set_var (
411+ "BINDGEN_EXTRA_CLANG_ARGS" ,
412+ format ! ( "--target={}" , target_triple) ,
413+ ) ;
414+ }
415+ }
416+
417+ let bindings = bindings_builder
248418 . generate ( )
249419 . expect ( "Failed to generate bindings" ) ;
250420
@@ -307,40 +477,92 @@ fn main() {
307477 config. static_crt ( static_crt) ;
308478
309479 if matches ! ( target_os, TargetOs :: Android ) {
310- // build flags for android taken from this doc
311- // https://github.com/ggerganov/llama.cpp/blob/master/docs/android.md
480+ // Android NDK Build Configuration
312481 let android_ndk = env:: var ( "ANDROID_NDK" )
313- . expect ( "Please install Android NDK and ensure that ANDROID_NDK env variable is set" ) ;
314-
315- println ! ( "cargo::rerun-if-env-changed=ANDROID_NDK" ) ;
482+ . or_else ( |_| env:: var ( "NDK_ROOT" ) )
483+ . or_else ( |_| env:: var ( "ANDROID_NDK_ROOT" ) )
484+ . unwrap_or_else ( |_| {
485+ panic ! (
486+ "Android NDK not found. Please set one of: ANDROID_NDK, NDK_ROOT, ANDROID_NDK_ROOT\n \
487+ Download from: https://developer.android.com/ndk/downloads"
488+ ) ;
489+ } ) ;
316490
317- config. define (
318- "CMAKE_TOOLCHAIN_FILE" ,
319- format ! ( "{android_ndk}/build/cmake/android.toolchain.cmake" ) ,
320- ) ;
321- if env:: var ( "ANDROID_PLATFORM" ) . is_ok ( ) {
322- println ! ( "cargo::rerun-if-env-changed=ANDROID_PLATFORM" ) ;
323- } else {
324- config. define ( "ANDROID_PLATFORM" , "android-28" ) ;
491+ // Validate NDK installation
492+ if let Err ( error) = validate_android_ndk ( & android_ndk) {
493+ panic ! ( "Android NDK validation failed: {}" , error) ;
325494 }
326- if target_triple. contains ( "aarch64" ) || target_triple. contains ( "armv7" ) {
327- config. cflag ( "-march=armv8.7a" ) ;
328- config. cxxflag ( "-march=armv8.7a" ) ;
495+
496+ // Rerun build script if NDK environment variables change
497+ println ! ( "cargo:rerun-if-env-changed=ANDROID_NDK" ) ;
498+ println ! ( "cargo:rerun-if-env-changed=NDK_ROOT" ) ;
499+ println ! ( "cargo:rerun-if-env-changed=ANDROID_NDK_ROOT" ) ;
500+
501+ // Set CMake toolchain file for Android
502+ let toolchain_file = format ! ( "{}/build/cmake/android.toolchain.cmake" , android_ndk) ;
503+ config. define ( "CMAKE_TOOLCHAIN_FILE" , & toolchain_file) ;
504+
505+ // Configure Android platform (API level)
506+ let android_platform = env:: var ( "ANDROID_PLATFORM" ) . unwrap_or_else ( |_| {
507+ env:: var ( "ANDROID_API_LEVEL" )
508+ . map ( |level| format ! ( "android-{}" , level) )
509+ . unwrap_or_else ( |_| "android-28" . to_string ( ) )
510+ } ) ;
511+
512+ println ! ( "cargo:rerun-if-env-changed=ANDROID_PLATFORM" ) ;
513+ println ! ( "cargo:rerun-if-env-changed=ANDROID_API_LEVEL" ) ;
514+ config. define ( "ANDROID_PLATFORM" , & android_platform) ;
515+
516+ // Map Rust target to Android ABI
517+ let android_abi = if target_triple. contains ( "aarch64" ) {
518+ "arm64-v8a"
519+ } else if target_triple. contains ( "armv7" ) {
520+ "armeabi-v7a"
329521 } else if target_triple. contains ( "x86_64" ) {
330- config. cflag ( "-march=x86-64" ) ;
331- config. cxxflag ( "-march=x86-64" ) ;
522+ "x86_64"
332523 } else if target_triple. contains ( "i686" ) {
333- config. cflag ( "-march=i686" ) ;
334- config. cxxflag ( "-march=i686" ) ;
524+ "x86"
335525 } else {
336- // Rather than guessing just fail.
337- panic ! ( "Unsupported Android target {target_triple}" ) ;
526+ panic ! (
527+ "Unsupported Android target: {}\n \
528+ Supported targets: aarch64-linux-android, armv7-linux-androideabi, i686-linux-android, x86_64-linux-android",
529+ target_triple
530+ ) ;
531+ } ;
532+
533+ config. define ( "ANDROID_ABI" , android_abi) ;
534+
535+ // Configure architecture-specific compiler flags
536+ match android_abi {
537+ "arm64-v8a" => {
538+ config. cflag ( "-march=armv8-a" ) ;
539+ config. cxxflag ( "-march=armv8-a" ) ;
540+ }
541+ "armeabi-v7a" => {
542+ config. cflag ( "-march=armv7-a" ) ;
543+ config. cxxflag ( "-march=armv7-a" ) ;
544+ config. cflag ( "-mfpu=neon" ) ;
545+ config. cxxflag ( "-mfpu=neon" ) ;
546+ config. cflag ( "-mthumb" ) ;
547+ config. cxxflag ( "-mthumb" ) ;
548+ }
549+ "x86_64" => {
550+ config. cflag ( "-march=x86-64" ) ;
551+ config. cxxflag ( "-march=x86-64" ) ;
552+ }
553+ "x86" => {
554+ config. cflag ( "-march=i686" ) ;
555+ config. cxxflag ( "-march=i686" ) ;
556+ }
557+ _ => { }
338558 }
559+
560+ // Android-specific CMake configurations
339561 config. define ( "GGML_LLAMAFILE" , "OFF" ) ;
340- if cfg ! ( feature = "shared-stdcxx" ) {
341- println ! ( "cargo:rustc-link-lib=dylib=stdc++" ) ;
342- println ! ( "cargo:rustc-link-lib=c++_shared " ) ;
343- }
562+
563+ // Link Android system libraries
564+ println ! ( "cargo:rustc-link-lib=log " ) ;
565+ println ! ( "cargo:rustc-link-lib=android" ) ;
344566 }
345567
346568 if matches ! ( target_os, TargetOs :: Linux )
0 commit comments