@@ -115,6 +115,7 @@ pub struct Build {
115115 env : Vec < ( OsString , OsString ) > ,
116116 compiler : Option < PathBuf > ,
117117 archiver : Option < PathBuf > ,
118+ ranlib : Option < PathBuf > ,
118119 cargo_metadata : bool ,
119120 link_lib_modifiers : Vec < String > ,
120121 pic : Option < bool > ,
@@ -320,6 +321,7 @@ impl Build {
320321 env : Vec :: new ( ) ,
321322 compiler : None ,
322323 archiver : None ,
324+ ranlib : None ,
323325 cargo_metadata : true ,
324326 link_lib_modifiers : Vec :: new ( ) ,
325327 pic : None ,
@@ -916,6 +918,17 @@ impl Build {
916918 self . archiver = Some ( archiver. as_ref ( ) . to_owned ( ) ) ;
917919 self
918920 }
921+
922+ /// Configures the tool used to index archives.
923+ ///
924+ /// This option is automatically determined from the target platform or a
925+ /// number of environment variables, so it's not required to call this
926+ /// function.
927+ pub fn ranlib < P : AsRef < Path > > ( & mut self , ranlib : P ) -> & mut Build {
928+ self . ranlib = Some ( ranlib. as_ref ( ) . to_owned ( ) ) ;
929+ self
930+ }
931+
919932 /// Define whether metadata should be emitted for cargo allowing it to
920933 /// automatically link the binary. Defaults to `true`.
921934 ///
@@ -2094,7 +2107,11 @@ impl Build {
20942107 // Non-msvc targets (those using `ar`) need a separate step to add
20952108 // the symbol table to archives since our construction command of
20962109 // `cq` doesn't add it for us.
2097- let ( mut ar, cmd) = self . get_ar ( ) ?;
2110+ let ( mut ar, cmd, _any_flags) = self . get_ar ( ) ?;
2111+
2112+ // NOTE: We add `s` even if flags were passed using $ARFLAGS/ar_flag, because `s`
2113+ // here represents a _mode_, not an arbitrary flag. Further discussion of this choice
2114+ // can be seen in https://github.com/rust-lang/cc-rs/pull/763.
20982115 run ( ar. arg ( "s" ) . arg ( dst) , & cmd) ?;
20992116 }
21002117
@@ -2105,12 +2122,16 @@ impl Build {
21052122 let target = self . get_target ( ) ?;
21062123
21072124 if target. contains ( "msvc" ) {
2108- let ( mut cmd, program) = self . get_ar ( ) ?;
2125+ let ( mut cmd, program, any_flags) = self . get_ar ( ) ?;
2126+ // NOTE: -out: here is an I/O flag, and so must be included even if $ARFLAGS/ar_flag is
2127+ // in use. -nologo on the other hand is just a regular flag, and one that we'll skip if
2128+ // the caller has explicitly dictated the flags they want. See
2129+ // https://github.com/rust-lang/cc-rs/pull/763 for further discussion.
21092130 let mut out = OsString :: from ( "-out:" ) ;
21102131 out. push ( dst) ;
2111- cmd. arg ( out) . arg ( "-nologo" ) ;
2112- for flag in self . ar_flags . iter ( ) {
2113- cmd. arg ( flag ) ;
2132+ cmd. arg ( out) ;
2133+ if !any_flags {
2134+ cmd. arg ( "-nologo" ) ;
21142135 }
21152136 // If the library file already exists, add the library name
21162137 // as an argument to let lib.exe know we are appending the objs.
@@ -2120,7 +2141,7 @@ impl Build {
21202141 cmd. args ( objs) ;
21212142 run ( & mut cmd, & program) ?;
21222143 } else {
2123- let ( mut ar, cmd) = self . get_ar ( ) ?;
2144+ let ( mut ar, cmd, _any_flags ) = self . get_ar ( ) ?;
21242145
21252146 // Set an environment variable to tell the OSX archiver to ensure
21262147 // that all dates listed in the archive are zero, improving
@@ -2145,9 +2166,10 @@ impl Build {
21452166 // In any case if this doesn't end up getting read, it shouldn't
21462167 // cause that many issues!
21472168 ar. env ( "ZERO_AR_DATE" , "1" ) ;
2148- for flag in self . ar_flags . iter ( ) {
2149- ar. arg ( flag) ;
2150- }
2169+
2170+ // NOTE: We add cq here regardless of whether $ARFLAGS/ar_flag have been used because
2171+ // it dictates the _mode_ ar runs in, which the setter of $ARFLAGS/ar_flag can't
2172+ // dictate. See https://github.com/rust-lang/cc-rs/pull/763 for further discussion.
21512173 run ( ar. arg ( "cq" ) . arg ( dst) . args ( objs) , & cmd) ?;
21522174 }
21532175
@@ -2639,81 +2661,206 @@ impl Build {
26392661 }
26402662 }
26412663
2642- fn get_ar ( & self ) -> Result < ( Command , String ) , Error > {
2643- if let Some ( ref p) = self . archiver {
2644- let name = p. file_name ( ) . and_then ( |s| s. to_str ( ) ) . unwrap_or ( "ar" ) ;
2645- return Ok ( ( self . cmd ( p) , name. to_string ( ) ) ) ;
2664+ fn get_ar ( & self ) -> Result < ( Command , String , bool ) , Error > {
2665+ self . try_get_archiver_and_flags ( )
2666+ }
2667+
2668+ /// Get the archiver (ar) that's in use for this configuration.
2669+ ///
2670+ /// You can use [`Command::get_program`] to get just the path to the command.
2671+ ///
2672+ /// This method will take into account all configuration such as debug
2673+ /// information, optimization level, include directories, defines, etc.
2674+ /// Additionally, the compiler binary in use follows the standard
2675+ /// conventions for this path, e.g. looking at the explicitly set compiler,
2676+ /// environment variables (a number of which are inspected here), and then
2677+ /// falling back to the default configuration.
2678+ ///
2679+ /// # Panics
2680+ ///
2681+ /// Panics if an error occurred while determining the architecture.
2682+ pub fn get_archiver ( & self ) -> Command {
2683+ match self . try_get_archiver ( ) {
2684+ Ok ( tool) => tool,
2685+ Err ( e) => fail ( & e. message ) ,
2686+ }
2687+ }
2688+
2689+ /// Get the archiver that's in use for this configuration.
2690+ ///
2691+ /// This will return a result instead of panicing;
2692+ /// see [`get_archiver()`] for the complete description.
2693+ pub fn try_get_archiver ( & self ) -> Result < Command , Error > {
2694+ Ok ( self . try_get_archiver_and_flags ( ) ?. 0 )
2695+ }
2696+
2697+ fn try_get_archiver_and_flags ( & self ) -> Result < ( Command , String , bool ) , Error > {
2698+ let ( mut cmd, name) = self . get_base_archiver ( ) ?;
2699+ let flags = self . envflags ( "ARFLAGS" ) ;
2700+ let mut any_flags = !flags. is_empty ( ) ;
2701+ cmd. args ( flags) ;
2702+ for flag in & self . ar_flags {
2703+ any_flags = true ;
2704+ cmd. arg ( flag) ;
26462705 }
2647- if let Ok ( p) = self . get_var ( "AR" ) {
2648- return Ok ( ( self . cmd ( & p) , p) ) ;
2706+ Ok ( ( cmd, name, any_flags) )
2707+ }
2708+
2709+ fn get_base_archiver ( & self ) -> Result < ( Command , String ) , Error > {
2710+ if let Some ( ref a) = self . archiver {
2711+ return Ok ( ( self . cmd ( a) , a. to_string_lossy ( ) . into_owned ( ) ) ) ;
26492712 }
2650- let target = self . get_target ( ) ?;
2651- let default_ar = "ar" . to_string ( ) ;
2652- let program = if target. contains ( "android" ) {
2653- format ! ( "{}-ar" , target. replace( "armv7" , "arm" ) )
2654- } else if target. contains ( "emscripten" ) {
2655- // Windows use bat files so we have to be a bit more specific
2656- if cfg ! ( windows) {
2657- let mut cmd = self . cmd ( "cmd" ) ;
2658- cmd. arg ( "/c" ) . arg ( "emar.bat" ) ;
2659- return Ok ( ( cmd, "emar.bat" . to_string ( ) ) ) ;
2660- }
26612713
2662- "emar" . to_string ( )
2663- } else if target. contains ( "msvc" ) {
2664- let compiler = self . get_base_compiler ( ) ?;
2665- let mut lib = String :: new ( ) ;
2666- if compiler. family == ( ToolFamily :: Msvc { clang_cl : true } ) {
2667- // See if there is 'llvm-lib' next to 'clang-cl'
2668- // Another possibility could be to see if there is 'clang'
2669- // next to 'clang-cl' and use 'search_programs()' to locate
2670- // 'llvm-lib'. This is because 'clang-cl' doesn't support
2671- // the -print-search-dirs option.
2672- if let Some ( mut cmd) = which ( & compiler. path ) {
2673- cmd. pop ( ) ;
2674- cmd. push ( "llvm-lib.exe" ) ;
2675- if let Some ( llvm_lib) = which ( & cmd) {
2676- lib = llvm_lib. to_str ( ) . unwrap ( ) . to_owned ( ) ;
2714+ self . get_base_archiver_variant ( "AR" , "ar" )
2715+ }
2716+
2717+ /// Get the ranlib that's in use for this configuration.
2718+ ///
2719+ /// You can use [`Command::get_program`] to get just the path to the command.
2720+ ///
2721+ /// This method will take into account all configuration such as debug
2722+ /// information, optimization level, include directories, defines, etc.
2723+ /// Additionally, the compiler binary in use follows the standard
2724+ /// conventions for this path, e.g. looking at the explicitly set compiler,
2725+ /// environment variables (a number of which are inspected here), and then
2726+ /// falling back to the default configuration.
2727+ ///
2728+ /// # Panics
2729+ ///
2730+ /// Panics if an error occurred while determining the architecture.
2731+ pub fn get_ranlib ( & self ) -> Command {
2732+ match self . try_get_ranlib ( ) {
2733+ Ok ( tool) => tool,
2734+ Err ( e) => fail ( & e. message ) ,
2735+ }
2736+ }
2737+
2738+ /// Get the ranlib that's in use for this configuration.
2739+ ///
2740+ /// This will return a result instead of panicing;
2741+ /// see [`get_ranlib()`] for the complete description.
2742+ pub fn try_get_ranlib ( & self ) -> Result < Command , Error > {
2743+ let mut cmd = self . get_base_ranlib ( ) ?;
2744+ cmd. args ( self . envflags ( "RANLIBFLAGS" ) ) ;
2745+ Ok ( cmd)
2746+ }
2747+
2748+ fn get_base_ranlib ( & self ) -> Result < Command , Error > {
2749+ if let Some ( ref r) = self . ranlib {
2750+ return Ok ( self . cmd ( r) ) ;
2751+ }
2752+
2753+ Ok ( self . get_base_archiver_variant ( "RANLIB" , "ranlib" ) ?. 0 )
2754+ }
2755+
2756+ fn get_base_archiver_variant ( & self , env : & str , tool : & str ) -> Result < ( Command , String ) , Error > {
2757+ let target = self . get_target ( ) ?;
2758+ let mut name = String :: new ( ) ;
2759+ let tool_opt: Option < Command > = self
2760+ . env_tool ( env)
2761+ . map ( |( tool, _wrapper, args) | {
2762+ let mut cmd = self . cmd ( tool) ;
2763+ cmd. args ( args) ;
2764+ cmd
2765+ } )
2766+ . or_else ( || {
2767+ if target. contains ( "emscripten" ) {
2768+ // Windows use bat files so we have to be a bit more specific
2769+ if cfg ! ( windows) {
2770+ let mut cmd = self . cmd ( "cmd" ) ;
2771+ name = format ! ( "em{}.bat" , tool) ;
2772+ cmd. arg ( "/c" ) . arg ( & name) ;
2773+ Some ( cmd)
2774+ } else {
2775+ name = format ! ( "em{}" , tool) ;
2776+ Some ( self . cmd ( & name) )
26772777 }
2778+ } else {
2779+ None
26782780 }
2679- }
2680- if lib. is_empty ( ) {
2681- lib = match windows_registry:: find ( & target, "lib.exe" ) {
2682- Some ( t) => return Ok ( ( t, "lib.exe" . to_string ( ) ) ) ,
2683- None => "lib.exe" . to_string ( ) ,
2684- }
2685- }
2686- lib
2687- } else if target. contains ( "illumos" ) {
2688- // The default 'ar' on illumos uses a non-standard flags,
2689- // but the OS comes bundled with a GNU-compatible variant.
2690- //
2691- // Use the GNU-variant to match other Unix systems.
2692- "gar" . to_string ( )
2693- } else if self . get_host ( ) ? != target {
2694- match self . prefix_for_target ( & target) {
2695- Some ( p) => {
2696- // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both.
2697- // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be
2698- // outright broken (such as when targetting freebsd with `--disable-lto`
2699- // toolchain where the archiver attempts to load the LTO plugin anyway but
2700- // fails to find one).
2701- let mut ar = default_ar;
2702- for & infix in & [ "" , "-gcc" ] {
2703- let target_ar = format ! ( "{}{}-ar" , p, infix) ;
2704- if Command :: new ( & target_ar) . output ( ) . is_ok ( ) {
2705- ar = target_ar;
2706- break ;
2781+ } ) ;
2782+
2783+ let default = tool. to_string ( ) ;
2784+ let tool = match tool_opt {
2785+ Some ( t) => t,
2786+ None => {
2787+ if target. contains ( "android" ) {
2788+ name = format ! ( "{}-{}" , target. replace( "armv7" , "arm" ) , tool) ;
2789+ self . cmd ( & name)
2790+ } else if target. contains ( "msvc" ) {
2791+ // NOTE: There isn't really a ranlib on msvc, so arguably we should return
2792+ // `None` somehow here. But in general, callers will already have to be aware
2793+ // of not running ranlib on Windows anyway, so it feels okay to return lib.exe
2794+ // here.
2795+
2796+ let compiler = self . get_base_compiler ( ) ?;
2797+ let mut lib = String :: new ( ) ;
2798+ if compiler. family == ( ToolFamily :: Msvc { clang_cl : true } ) {
2799+ // See if there is 'llvm-lib' next to 'clang-cl'
2800+ // Another possibility could be to see if there is 'clang'
2801+ // next to 'clang-cl' and use 'search_programs()' to locate
2802+ // 'llvm-lib'. This is because 'clang-cl' doesn't support
2803+ // the -print-search-dirs option.
2804+ if let Some ( mut cmd) = which ( & compiler. path ) {
2805+ cmd. pop ( ) ;
2806+ cmd. push ( "llvm-lib.exe" ) ;
2807+ if let Some ( llvm_lib) = which ( & cmd) {
2808+ lib = llvm_lib. to_str ( ) . unwrap ( ) . to_owned ( ) ;
2809+ }
2810+ }
2811+ }
2812+
2813+ if lib. is_empty ( ) {
2814+ name = String :: from ( "lib.exe" ) ;
2815+ match windows_registry:: find ( & target, "lib.exe" ) {
2816+ Some ( t) => t,
2817+ None => self . cmd ( "lib.exe" ) ,
2818+ }
2819+ } else {
2820+ name = lib;
2821+ self . cmd ( & name)
2822+ }
2823+ } else if target. contains ( "illumos" ) {
2824+ // The default 'ar' on illumos uses a non-standard flags,
2825+ // but the OS comes bundled with a GNU-compatible variant.
2826+ //
2827+ // Use the GNU-variant to match other Unix systems.
2828+ name = format ! ( "g{}" , tool) ;
2829+ self . cmd ( & name)
2830+ } else if self . get_host ( ) ? != target {
2831+ match self . prefix_for_target ( & target) {
2832+ Some ( p) => {
2833+ // GCC uses $target-gcc-ar, whereas binutils uses $target-ar -- try both.
2834+ // Prefer -ar if it exists, as builds of `-gcc-ar` have been observed to be
2835+ // outright broken (such as when targetting freebsd with `--disable-lto`
2836+ // toolchain where the archiver attempts to load the LTO plugin anyway but
2837+ // fails to find one).
2838+ //
2839+ // The same applies to ranlib.
2840+ let mut chosen = default;
2841+ for & infix in & [ "" , "-gcc" ] {
2842+ let target_p = format ! ( "{}{}-{}" , p, infix, tool) ;
2843+ if Command :: new ( & target_p) . output ( ) . is_ok ( ) {
2844+ chosen = target_p;
2845+ break ;
2846+ }
2847+ }
2848+ name = chosen;
2849+ self . cmd ( & name)
2850+ }
2851+ None => {
2852+ name = default;
2853+ self . cmd ( & name)
27072854 }
27082855 }
2709- ar
2856+ } else {
2857+ name = default;
2858+ self . cmd ( & name)
27102859 }
2711- None => default_ar,
27122860 }
2713- } else {
2714- default_ar
27152861 } ;
2716- Ok ( ( self . cmd ( & program) , program) )
2862+
2863+ Ok ( ( tool, name) )
27172864 }
27182865
27192866 fn prefix_for_target ( & self , target : & str ) -> Option < String > {
0 commit comments