@@ -25,11 +25,28 @@ lazy_static::lazy_static! {
2525 static ref RUST_CLANG_TARGET_MAP : HashMap <String , String > = {
2626 let mut m = HashMap :: new( ) ;
2727 m. insert( "aarch64-linux-android" . to_string( ) , "armv8-linux-androideabi" . to_string( ) ) ;
28+ m. insert( "aarch64-apple-ios-sim" . to_string( ) , "arm64-apple-ios-sim" . to_string( ) ) ;
2829 m
2930 } ;
3031}
3132
32- fn hard_link ( src : PathBuf , dst : PathBuf ) {
33+ fn link_lib ( lib : & str , is_dynamic : bool ) {
34+ let lib_kind = if is_dynamic { "dylib" } else { "static" } ;
35+ debug_log ! ( "cargo:rustc-link-lib={}={}" , lib_kind, lib) ;
36+ println ! ( "cargo:rustc-link-lib={}={}" , lib_kind, lib) ;
37+ }
38+
39+ fn link_framework ( framework : & str ) {
40+ debug_log ! ( "cargo:rustc-link-lib=framework={}" , framework) ;
41+ println ! ( "cargo:rustc-link-lib=framework={}" , framework) ;
42+ }
43+
44+ fn add_search_path < P : AsRef < Path > > ( path : P ) {
45+ debug_log ! ( "cargo:rustc-link-search={}" , path. as_ref( ) . display( ) ) ;
46+ println ! ( "cargo:rustc-link-search={}" , path. as_ref( ) . display( ) ) ;
47+ }
48+
49+ fn copy_file ( src : PathBuf , dst : PathBuf ) {
3350 if let Err ( err) = std:: fs:: hard_link ( & src, & dst) {
3451 debug_log ! ( "Failed to hardlink {:?}. fallback to copy." , err) ;
3552 fs:: copy ( & src, & dst)
@@ -215,6 +232,7 @@ fn main() {
215232 ] ) ;
216233
217234 let target = env:: var ( "TARGET" ) . unwrap ( ) ;
235+ let is_mobile = target. contains ( "android" ) || target. contains ( "ios" ) ;
218236 debug_log ! ( "TARGET: {:?}" , target) ;
219237 let out_dir = PathBuf :: from ( env:: var ( "OUT_DIR" ) . unwrap ( ) ) ;
220238
@@ -281,12 +299,15 @@ fn main() {
281299 bindings_builder = bindings_builder. clang_arg ( format ! ( "--target={}" , clang_target) ) ;
282300 }
283301
302+ debug_log ! ( "Generating bindings..." ) ;
284303 let bindings_builder = bindings_builder
285304 . generate ( )
286305 . expect ( "Failed to generate bindings" ) ;
287306
288307 // Write the generated bindings to an output file
289308 let bindings_path = out_dir. join ( "bindings.rs" ) ;
309+
310+ debug_log ! ( "Writing bindings to {:?}" , bindings_path) ;
290311 bindings_builder
291312 . write_to_file ( bindings_path)
292313 . expect ( "Failed to write bindings" ) ;
@@ -339,7 +360,6 @@ fn main() {
339360 . always_configure ( false ) ;
340361
341362 let mut sherpa_libs: Vec < String > = Vec :: new ( ) ;
342- let sherpa_libs_kind = if is_dynamic { "dylib" } else { "static" } ;
343363
344364 // Download libraries, cache and set SHERPA_LIB_PATH
345365
@@ -352,7 +372,8 @@ fn main() {
352372 debug_log ! ( "Download binaries enabled" ) ;
353373 // debug_log!("Dist table: {:?}", DIST_TABLE.targets);
354374 // Try download sherpa libs and set SHERPA_LIB_PATH
355- if let Some ( dist) = DIST_TABLE . get ( & target, is_dynamic) {
375+ if let Some ( dist) = DIST_TABLE . get ( & target, & mut is_dynamic) {
376+ debug_log ! ( "is_dynamic after: {}" , is_dynamic) ;
356377 optional_dist = Some ( dist. clone ( ) ) ;
357378 let mut cache_dir = if let Some ( dir) = get_cache_dir ( ) {
358379 dir. join ( target. clone ( ) ) . join ( & dist. checksum )
@@ -365,8 +386,16 @@ fn main() {
365386 cache_dir = env:: var ( "OUT_DIR" ) . unwrap ( ) . into ( ) ;
366387 }
367388 debug_log ! ( "Cache dir: {}" , cache_dir. display( ) ) ;
389+
368390 let lib_dir = cache_dir. join ( & dist. name ) ;
369- if !lib_dir. exists ( ) {
391+ // if is mobile then check if cache dir not empty
392+ let cache_dir_empty = cache_dir
393+ . read_dir ( )
394+ . map ( |mut entries| entries. next ( ) . is_none ( ) )
395+ . unwrap_or ( true ) ;
396+ // Check if cache directory exists
397+ // Sherpa uses special directory structure for mobile
398+ if ( !lib_dir. exists ( ) && !is_mobile) || ( is_mobile && cache_dir_empty) {
370399 let downloaded_file = fetch_file ( & dist. url ) ;
371400 let hash = sha256 ( & downloaded_file) ;
372401 // verify checksum
@@ -382,15 +411,39 @@ fn main() {
382411 }
383412
384413 // In Android, we need to set SHERPA_LIB_PATH to the cache directory sincie it has jniLibs
385- if target . contains ( "android" ) || target . contains ( "ios" ) {
386- env:: set_var ( "SHERPA_LIB_PATH" , cache_dir) ;
414+ if is_mobile {
415+ env:: set_var ( "SHERPA_LIB_PATH" , & cache_dir) ;
387416 } else {
388417 env:: set_var ( "SHERPA_LIB_PATH" , cache_dir. join ( & dist. name ) ) ;
389418 }
390419
391420 debug_log ! ( "dist libs: {:?}" , dist. libs) ;
392421 if let Some ( libs) = dist. libs {
393- sherpa_libs = libs;
422+ for lib in libs. iter ( ) {
423+ let lib_path = cache_dir. join ( lib) ;
424+ let lib_parent = lib_path. parent ( ) . unwrap ( ) ;
425+ add_search_path ( lib_parent) ;
426+ }
427+
428+ sherpa_libs = libs
429+ . iter ( )
430+ . map ( |p| {
431+ Path :: new ( p)
432+ . file_name ( )
433+ . unwrap ( )
434+ . to_string_lossy ( )
435+ // Remove lib prefix
436+ . strip_prefix ( "lib" )
437+ . unwrap_or_else ( || p)
438+ // Remove .so
439+ . replace ( ".so" , "" )
440+ // Remove .dylib
441+ . replace ( ".dylib" , "" )
442+ // Remove .a
443+ . replace ( ".a" , "" )
444+ . to_string ( )
445+ } )
446+ . collect ( ) ;
394447 } else {
395448 sherpa_libs = extract_lib_names ( & lib_dir, is_dynamic, & target_os) ;
396449 }
@@ -403,70 +456,75 @@ fn main() {
403456 // Skip build if SHERPA_LIB_PATH specified
404457 debug_log ! ( "Skpping build with Cmake..." ) ;
405458 debug_log ! ( "SHERPA_LIB_PATH: {}" , sherpa_lib_path) ;
406- println ! (
407- "cargo:rustc-link-search={}" ,
408- Path :: new( & sherpa_lib_path) . join( "lib" ) . display( )
409- ) ;
410- sherpa_libs = extract_lib_names ( Path :: new ( & sherpa_lib_path) , is_dynamic, & target_os) ;
459+ add_search_path ( Path :: new ( & sherpa_lib_path) . join ( "lib" ) ) ;
460+ if sherpa_libs. is_empty ( ) {
461+ sherpa_libs = extract_lib_names ( Path :: new ( & sherpa_lib_path) , is_dynamic, & target_os) ;
462+ }
411463 } else {
412464 // Build with CMake
413465 let bindings_dir = config. build ( ) ;
414- println ! ( "cargo:rustc-link-search={}" , bindings_dir. display ( ) ) ;
466+ add_search_path ( & bindings_dir) ;
415467
416468 // Extract libs on desktop platforms
417- if !target . contains ( "android" ) && !target . contains ( "ios" ) {
469+ if !is_mobile {
418470 sherpa_libs = extract_lib_names ( & bindings_dir, is_dynamic, & target_os) ;
419471 }
420472 }
421473
422474 // Search paths
423- println ! ( "cargo:rustc-link-search={}" , out_dir. join( "lib" ) . display( ) ) ;
475+ debug_log ! ( "Sherpa libs: {:?}" , sherpa_libs) ;
476+ add_search_path ( out_dir. join ( "lib" ) ) ;
424477
425478 for lib in sherpa_libs {
426479 if lib. contains ( "cxx" ) {
427480 continue ;
428481 }
429- debug_log ! (
430- "LINK {}" ,
431- format!( "cargo:rustc-link-lib={}={}" , sherpa_libs_kind, lib)
432- ) ;
433- println ! ( "cargo:rustc-link-lib={}={}" , sherpa_libs_kind, lib) ;
482+ link_lib ( & lib, is_dynamic) ;
434483 }
435484
436485 // Windows debug
437486 if cfg ! ( all( debug_assertions, windows) ) {
438- println ! ( "cargo:rustc-link-lib=dylib= msvcrtd") ;
487+ link_lib ( " msvcrtd", true ) ;
439488 }
440489
441490 // macOS
442- if target_os == "macos" {
443- println ! ( "cargo:rustc-link-lib=framework=Foundation" ) ;
444- println ! ( "cargo:rustc-link-lib=c++" ) ;
491+ if target_os == "macos" || target_os == "ios" {
492+ link_framework ( "CoreML" ) ;
493+ link_framework ( "Foundation" ) ;
494+ link_lib ( "c++" , true ) ;
445495 }
446496
447497 // Linux
448498 if target_os == "linux" || target == "android" {
449- println ! ( "cargo:rustc-link-lib=dylib= stdc++") ;
499+ link_lib ( " stdc++", true ) ;
450500 }
451501
452- if target. contains ( "apple" ) {
502+ // macOS
503+ if target_os == "macos" {
453504 // On (older) OSX we need to link against the clang runtime,
454505 // which is hidden in some non-default path.
455506 //
456507 // More details at https://github.com/alexcrichton/curl-rust/issues/279.
457508 if let Some ( path) = macos_link_search_path ( ) {
458- println ! ( "cargo:rustc-link-lib=clang_rt.osx" ) ;
459- println ! ( "cargo:rustc-link-search={} ", path ) ;
509+ add_search_path ( path ) ;
510+ link_lib ( "clang_rt.osx ", is_dynamic ) ;
460511 }
461512 }
462513
514+ // TODO: add rpath for Android and iOS so it can find its dependencies in the same directory of executable
515+ // if is_mobile {
516+ // // Add rpath for Android and iOS so that the shared library can find its dependencies in the same directory as well
517+ // println!("cargo:rustc-link-arg=-Wl,-rpath,'$ORIGIN'");
518+ // }
519+
463520 // copy DLLs to target
464521 if is_dynamic {
465522 let mut libs_assets = extract_lib_assets ( & out_dir, & target_os) ;
466523 if let Ok ( sherpa_lib_path) = env:: var ( "SHERPA_LIB_PATH" ) {
467524 libs_assets. extend ( extract_lib_assets ( Path :: new ( & sherpa_lib_path) , & target_os) ) ;
468525 }
469526
527+ #[ cfg( feature = "download-binaries" ) ]
470528 if let Some ( dist) = optional_dist {
471529 if let Some ( assets) = dist. libs {
472530 if let Ok ( sherpa_lib_path) = env:: var ( "SHERPA_LIB_PATH" ) {
@@ -483,23 +541,23 @@ fn main() {
483541 let dst = target_dir. join ( filename) ;
484542 // debug_log!("HARD LINK {} TO {}", asset.display(), dst.display());
485543 if !dst. exists ( ) {
486- hard_link ( asset. clone ( ) , dst) ;
544+ copy_file ( asset. clone ( ) , dst) ;
487545 }
488546
489547 // Copy DLLs to examples as well
490548 if target_dir. join ( "examples" ) . exists ( ) {
491549 let dst = target_dir. join ( "examples" ) . join ( filename) ;
492550 // debug_log!("HARD LINK {} TO {}", asset.display(), dst.display());
493551 if !dst. exists ( ) {
494- hard_link ( asset. clone ( ) , dst) ;
552+ copy_file ( asset. clone ( ) , dst) ;
495553 }
496554 }
497555
498556 // Copy DLLs to target/profile/deps as well for tests
499557 let dst = target_dir. join ( "deps" ) . join ( filename) ;
500558 // debug_log!("HARD LINK {} TO {}", asset.display(), dst.display());
501559 if !dst. exists ( ) {
502- hard_link ( asset. clone ( ) , dst) ;
560+ copy_file ( asset. clone ( ) , dst) ;
503561 }
504562 }
505563 }
0 commit comments