@@ -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
@@ -300,40 +470,92 @@ fn main() {
300
470
config. static_crt ( static_crt) ;
301
471
302
472
if matches ! ( target_os, TargetOs :: Android ) {
303
- // build flags for android taken from this doc
304
- // https://github.com/ggerganov/llama.cpp/blob/master/docs/android.md
473
+ // Android NDK Build Configuration
305
474
let android_ndk = env:: var ( "ANDROID_NDK" )
306
- . expect ( "Please install Android NDK and ensure that ANDROID_NDK env variable is set" ) ;
307
-
308
- println ! ( "cargo::rerun-if-env-changed=ANDROID_NDK" ) ;
475
+ . or_else ( |_| env:: var ( "NDK_ROOT" ) )
476
+ . or_else ( |_| env:: var ( "ANDROID_NDK_ROOT" ) )
477
+ . unwrap_or_else ( |_| {
478
+ panic ! (
479
+ "Android NDK not found. Please set one of: ANDROID_NDK, NDK_ROOT, ANDROID_NDK_ROOT\n \
480
+ Download from: https://developer.android.com/ndk/downloads"
481
+ ) ;
482
+ } ) ;
309
483
310
- config. define (
311
- "CMAKE_TOOLCHAIN_FILE" ,
312
- format ! ( "{android_ndk}/build/cmake/android.toolchain.cmake" ) ,
313
- ) ;
314
- if env:: var ( "ANDROID_PLATFORM" ) . is_ok ( ) {
315
- println ! ( "cargo::rerun-if-env-changed=ANDROID_PLATFORM" ) ;
316
- } else {
317
- config. define ( "ANDROID_PLATFORM" , "android-28" ) ;
484
+ // Validate NDK installation
485
+ if let Err ( error) = validate_android_ndk ( & android_ndk) {
486
+ panic ! ( "Android NDK validation failed: {}" , error) ;
318
487
}
319
- if target_triple. contains ( "aarch64" ) || target_triple. contains ( "armv7" ) {
320
- config. cflag ( "-march=armv8.7a" ) ;
321
- config. cxxflag ( "-march=armv8.7a" ) ;
488
+
489
+ // Rerun build script if NDK environment variables change
490
+ println ! ( "cargo:rerun-if-env-changed=ANDROID_NDK" ) ;
491
+ println ! ( "cargo:rerun-if-env-changed=NDK_ROOT" ) ;
492
+ println ! ( "cargo:rerun-if-env-changed=ANDROID_NDK_ROOT" ) ;
493
+
494
+ // Set CMake toolchain file for Android
495
+ let toolchain_file = format ! ( "{}/build/cmake/android.toolchain.cmake" , android_ndk) ;
496
+ config. define ( "CMAKE_TOOLCHAIN_FILE" , & toolchain_file) ;
497
+
498
+ // Configure Android platform (API level)
499
+ let android_platform = env:: var ( "ANDROID_PLATFORM" ) . unwrap_or_else ( |_| {
500
+ env:: var ( "ANDROID_API_LEVEL" )
501
+ . map ( |level| format ! ( "android-{}" , level) )
502
+ . unwrap_or_else ( |_| "android-28" . to_string ( ) )
503
+ } ) ;
504
+
505
+ println ! ( "cargo:rerun-if-env-changed=ANDROID_PLATFORM" ) ;
506
+ println ! ( "cargo:rerun-if-env-changed=ANDROID_API_LEVEL" ) ;
507
+ config. define ( "ANDROID_PLATFORM" , & android_platform) ;
508
+
509
+ // Map Rust target to Android ABI
510
+ let android_abi = if target_triple. contains ( "aarch64" ) {
511
+ "arm64-v8a"
512
+ } else if target_triple. contains ( "armv7" ) {
513
+ "armeabi-v7a"
322
514
} else if target_triple. contains ( "x86_64" ) {
323
- config. cflag ( "-march=x86-64" ) ;
324
- config. cxxflag ( "-march=x86-64" ) ;
515
+ "x86_64"
325
516
} else if target_triple. contains ( "i686" ) {
326
- config. cflag ( "-march=i686" ) ;
327
- config. cxxflag ( "-march=i686" ) ;
517
+ "x86"
328
518
} else {
329
- // Rather than guessing just fail.
330
- panic ! ( "Unsupported Android target {target_triple}" ) ;
519
+ panic ! (
520
+ "Unsupported Android target: {}\n \
521
+ Supported targets: aarch64-linux-android, armv7-linux-androideabi, i686-linux-android, x86_64-linux-android",
522
+ target_triple
523
+ ) ;
524
+ } ;
525
+
526
+ config. define ( "ANDROID_ABI" , android_abi) ;
527
+
528
+ // Configure architecture-specific compiler flags
529
+ match android_abi {
530
+ "arm64-v8a" => {
531
+ config. cflag ( "-march=armv8-a" ) ;
532
+ config. cxxflag ( "-march=armv8-a" ) ;
533
+ }
534
+ "armeabi-v7a" => {
535
+ config. cflag ( "-march=armv7-a" ) ;
536
+ config. cxxflag ( "-march=armv7-a" ) ;
537
+ config. cflag ( "-mfpu=neon" ) ;
538
+ config. cxxflag ( "-mfpu=neon" ) ;
539
+ config. cflag ( "-mthumb" ) ;
540
+ config. cxxflag ( "-mthumb" ) ;
541
+ }
542
+ "x86_64" => {
543
+ config. cflag ( "-march=x86-64" ) ;
544
+ config. cxxflag ( "-march=x86-64" ) ;
545
+ }
546
+ "x86" => {
547
+ config. cflag ( "-march=i686" ) ;
548
+ config. cxxflag ( "-march=i686" ) ;
549
+ }
550
+ _ => { }
331
551
}
552
+
553
+ // Android-specific CMake configurations
332
554
config. define ( "GGML_LLAMAFILE" , "OFF" ) ;
333
- if cfg ! ( feature = "shared-stdcxx" ) {
334
- println ! ( "cargo:rustc-link-lib=dylib=stdc++" ) ;
335
- println ! ( "cargo:rustc-link-lib=c++_shared " ) ;
336
- }
555
+
556
+ // Link Android system libraries
557
+ println ! ( "cargo:rustc-link-lib=log " ) ;
558
+ println ! ( "cargo:rustc-link-lib=android" ) ;
337
559
}
338
560
339
561
if matches ! ( target_os, TargetOs :: Linux )
0 commit comments