@@ -456,10 +456,15 @@ mod c_vendor {
456456
457457#[ cfg( feature = "c-system" ) ]
458458mod c_system {
459+ extern crate ar;
460+
461+ use std:: collections:: HashMap ;
459462 use std:: env;
463+ use std:: fs:: File ;
460464 use std:: process:: { Command , Output } ;
461465 use std:: str;
462- use std:: path:: Path ;
466+ use std:: path:: { Path , PathBuf } ;
467+
463468 use sources;
464469
465470 fn success_output ( err : & str , cmd : & mut Command ) -> Output {
@@ -499,57 +504,111 @@ mod c_system {
499504 r. to_string ( )
500505 }
501506
507+ // TODO: this really should be part of ar
508+ fn clone_header ( h0 : & ar:: Header ) -> ar:: Header {
509+ let mut h = ar:: Header :: new ( h0. identifier ( ) . to_vec ( ) , h0. size ( ) ) ;
510+ h. set_mtime ( h0. mtime ( ) ) ;
511+ h. set_uid ( h0. uid ( ) ) ;
512+ h. set_gid ( h0. gid ( ) ) ;
513+ h. set_mode ( h0. mode ( ) ) ;
514+ h
515+ }
516+
517+ fn find_library < I > ( dirs : I , libname : & str ) -> Result < PathBuf , Vec < String > >
518+ where
519+ I : Iterator < Item = PathBuf >
520+ {
521+ let mut paths = Vec :: new ( ) ;
522+ for dir in dirs {
523+ let try_path = dir. join ( format ! ( "lib{}.a" , libname) ) ;
524+ if try_path. exists ( ) {
525+ return Ok ( try_path. to_path_buf ( ) ) ;
526+ } else {
527+ paths. push ( format ! ( "{:?}" , try_path) )
528+ }
529+ }
530+ Err ( paths)
531+ }
532+
502533 /// Link against system clang runtime libraries
503534 pub fn compile ( llvm_target : & [ & str ] ) {
504535 let target = env:: var ( "TARGET" ) . unwrap ( ) ;
505536 let target_os = env:: var ( "CARGO_CFG_TARGET_OS" ) . unwrap ( ) ;
506537 let compiler_rt_arch = get_arch_name_for_compiler_rtlib ( ) ;
538+ let out_dir = env:: var ( "OUT_DIR" ) . unwrap ( ) ;
539+
540+ println ! ( "cargo:rerun-if-env-changed=CLANG" ) ;
541+ println ! ( "cargo:rerun-if-env-changed=LLVM_CONFIG" ) ;
507542
508543 if ALL_SUPPORTED_ARCHES . split ( ";" ) . find ( |x| * x == compiler_rt_arch) == None {
509544 return ;
510545 }
511546
512- if let Ok ( clang) = env:: var ( "CLANG" ) {
547+ let fullpath = if let Ok ( clang) = env:: var ( "CLANG" ) {
513548 let output = success_output (
514549 "failed to find clang's compiler-rt" ,
515550 Command :: new ( clang)
516551 . arg ( format ! ( "--target={}" , target) )
517552 . arg ( "--rtlib=compiler-rt" )
518553 . arg ( "--print-libgcc-file-name" ) ,
519554 ) ;
520- let fullpath = Path :: new ( str:: from_utf8 ( & output. stdout ) . unwrap ( ) ) ;
521- let libpath = fullpath. parent ( ) . unwrap ( ) . display ( ) ;
522- let libname = fullpath
523- . file_stem ( )
524- . unwrap ( )
525- . to_str ( )
526- . unwrap ( )
527- . trim_start_matches ( "lib" ) ;
528- println ! ( "cargo:rustc-link-search=native={}" , libpath) ;
529- println ! ( "cargo:rustc-link-lib=static={}" , libname) ;
555+ let path = str:: from_utf8 ( & output. stdout ) . unwrap ( ) . trim_end ( ) ;
556+ Path :: new ( path) . to_path_buf ( )
530557 } else if let Ok ( llvm_config) = env:: var ( "LLVM_CONFIG" ) {
531558 // fallback if clang is not installed
532559 let ( subpath, libname) = match target_os. as_str ( ) {
533560 "linux" => ( "linux" , format ! ( "clang_rt.builtins-{}" , & compiler_rt_arch) ) ,
534561 "macos" => ( "darwin" , "clang_rt.builtins_osx_dynamic" . to_string ( ) ) ,
535562 _ => panic ! ( "unsupported target os: {}" , target_os) ,
536563 } ;
537- let cmd = format ! ( "ls -1d $({} --libdir)/clang/*/lib/{}" , llvm_config, subpath) ;
538564 let output = success_output (
539- "failed to find clang 's lib dir" ,
540- Command :: new ( "sh" ) . args ( & [ "-ec" , & cmd ] ) ,
565+ "failed to find llvm-config 's lib dir" ,
566+ Command :: new ( llvm_config ) . arg ( "--libdir" ) ,
541567 ) ;
542- for search_dir in str:: from_utf8 ( & output. stdout ) . unwrap ( ) . lines ( ) {
543- println ! ( "cargo:rustc-link-search=native={}" , search_dir) ;
568+ let libdir = str:: from_utf8 ( & output. stdout ) . unwrap ( ) . trim_end ( ) ;
569+ let paths = std:: fs:: read_dir ( Path :: new ( libdir) . join ( "clang" ) ) . unwrap ( ) . map ( |e| {
570+ e. unwrap ( ) . path ( ) . join ( "lib" ) . join ( subpath)
571+ } ) ;
572+ match find_library ( paths, & libname) {
573+ Ok ( p) => p,
574+ Err ( paths) => panic ! ( "failed to find llvm-config's compiler-rt: {}" , paths. join( ":" ) ) ,
544575 }
545- println ! ( "cargo:rustc-link-lib=static={}" , libname) ;
546576 } else {
547577 panic ! ( "neither CLANG nor LLVM_CONFIG could be read" ) ;
578+ } ;
579+
580+ let mut index = 0 ;
581+ let mut files = HashMap :: new ( ) ;
582+ let mut orig = ar:: Archive :: new ( File :: open ( & fullpath) . unwrap ( ) ) ;
583+ while let Some ( entry_result) = orig. next_entry ( ) {
584+ let entry = entry_result. unwrap ( ) ;
585+ let name = str:: from_utf8 ( entry. header ( ) . identifier ( ) ) . unwrap ( ) ;
586+ files. insert ( name. to_owned ( ) , index) ;
587+ index += 1 ;
548588 }
549589
550590 let sources = sources:: get_sources ( llvm_target) ;
591+ let mut new = ar:: Builder :: new ( File :: create ( Path :: new ( & out_dir) . join ( "libcompiler-rt.a" ) ) . unwrap ( ) ) ;
551592 for ( sym, _src) in sources. map . iter ( ) {
593+ let & i = {
594+ let sym_ = if sym. starts_with ( "__" ) { & sym[ 2 ..] } else { & sym } ;
595+ match files. get ( & format ! ( "{}.c.o" , sym_) ) {
596+ Some ( i) => i,
597+ None => match files. get ( & format ! ( "{}.S.o" , sym_) ) {
598+ Some ( i) => i,
599+ None => panic ! ( "could not find expected symbol {} in {:?}" , sym, & fullpath) ,
600+ } ,
601+ }
602+ } ;
603+ orig. jump_to_entry ( i) . unwrap ( ) ; // Work around a stupid rust-ar bug
604+ let mut entry = orig. jump_to_entry ( i) . unwrap ( ) ;
605+ let header = clone_header ( entry. header ( ) ) ;
606+ // TODO: ar really should have an append_entry to avoid the clone
607+ new. append ( & header, & mut entry) . unwrap ( ) ;
552608 println ! ( "cargo:rustc-cfg={}=\" optimized-c\" " , sym) ;
553609 }
610+
611+ println ! ( "cargo:rustc-link-search=native={}" , out_dir) ;
612+ println ! ( "cargo:rustc-link-lib=static={}" , "compiler-rt" ) ;
554613 }
555614}
0 commit comments