@@ -45,7 +45,13 @@ fn parse_target_os() -> Result<(TargetOs, String), String> {
45
45
} else {
46
46
Ok ( ( TargetOs :: Apple ( AppleVariant :: Other ) , target) )
47
47
}
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
49
55
Ok ( ( TargetOs :: Android , target) )
50
56
} else if target. contains ( "linux" ) {
51
57
Ok ( ( TargetOs :: Linux , target) )
@@ -162,6 +168,28 @@ fn macos_link_search_path() -> Option<String> {
162
168
None
163
169
}
164
170
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
+
165
193
fn is_hidden ( e : & DirEntry ) -> bool {
166
194
e. file_name ( )
167
195
. to_str ( )
@@ -233,7 +261,7 @@ fn main() {
233
261
) ;
234
262
235
263
// Bindings
236
- let bindings = bindgen:: Builder :: default ( )
264
+ let mut bindings_builder = bindgen:: Builder :: default ( )
237
265
. header ( "wrapper.h" )
238
266
. clang_arg ( format ! ( "-I{}" , llama_src. join( "include" ) . display( ) ) )
239
267
. clang_arg ( format ! ( "-I{}" , llama_src. join( "ggml/include" ) . display( ) ) )
@@ -244,7 +272,149 @@ fn main() {
244
272
. allowlist_type ( "ggml_.*" )
245
273
. allowlist_function ( "llama_.*" )
246
274
. 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
248
418
. generate ( )
249
419
. expect ( "Failed to generate bindings" ) ;
250
420
@@ -307,40 +477,92 @@ fn main() {
307
477
config. static_crt ( static_crt) ;
308
478
309
479
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
312
481
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
+ } ) ;
316
490
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) ;
325
494
}
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"
329
521
} else if target_triple. contains ( "x86_64" ) {
330
- config. cflag ( "-march=x86-64" ) ;
331
- config. cxxflag ( "-march=x86-64" ) ;
522
+ "x86_64"
332
523
} else if target_triple. contains ( "i686" ) {
333
- config. cflag ( "-march=i686" ) ;
334
- config. cxxflag ( "-march=i686" ) ;
524
+ "x86"
335
525
} 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
+ _ => { }
338
558
}
559
+
560
+ // Android-specific CMake configurations
339
561
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" ) ;
344
566
}
345
567
346
568
if matches ! ( target_os, TargetOs :: Linux )
0 commit comments