@@ -115,6 +115,7 @@ pub struct Build {
115
115
env : Vec < ( OsString , OsString ) > ,
116
116
compiler : Option < PathBuf > ,
117
117
archiver : Option < PathBuf > ,
118
+ ranlib : Option < PathBuf > ,
118
119
cargo_metadata : bool ,
119
120
link_lib_modifiers : Vec < String > ,
120
121
pic : Option < bool > ,
@@ -320,6 +321,7 @@ impl Build {
320
321
env : Vec :: new ( ) ,
321
322
compiler : None ,
322
323
archiver : None ,
324
+ ranlib : None ,
323
325
cargo_metadata : true ,
324
326
link_lib_modifiers : Vec :: new ( ) ,
325
327
pic : None ,
@@ -916,6 +918,17 @@ impl Build {
916
918
self . archiver = Some ( archiver. as_ref ( ) . to_owned ( ) ) ;
917
919
self
918
920
}
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
+
919
932
/// Define whether metadata should be emitted for cargo allowing it to
920
933
/// automatically link the binary. Defaults to `true`.
921
934
///
@@ -2094,7 +2107,11 @@ impl Build {
2094
2107
// Non-msvc targets (those using `ar`) need a separate step to add
2095
2108
// the symbol table to archives since our construction command of
2096
2109
// `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.
2098
2115
run ( ar. arg ( "s" ) . arg ( dst) , & cmd) ?;
2099
2116
}
2100
2117
@@ -2105,12 +2122,16 @@ impl Build {
2105
2122
let target = self . get_target ( ) ?;
2106
2123
2107
2124
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.
2109
2130
let mut out = OsString :: from ( "-out:" ) ;
2110
2131
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" ) ;
2114
2135
}
2115
2136
// If the library file already exists, add the library name
2116
2137
// as an argument to let lib.exe know we are appending the objs.
@@ -2120,7 +2141,7 @@ impl Build {
2120
2141
cmd. args ( objs) ;
2121
2142
run ( & mut cmd, & program) ?;
2122
2143
} else {
2123
- let ( mut ar, cmd) = self . get_ar ( ) ?;
2144
+ let ( mut ar, cmd, _any_flags ) = self . get_ar ( ) ?;
2124
2145
2125
2146
// Set an environment variable to tell the OSX archiver to ensure
2126
2147
// that all dates listed in the archive are zero, improving
@@ -2145,9 +2166,10 @@ impl Build {
2145
2166
// In any case if this doesn't end up getting read, it shouldn't
2146
2167
// cause that many issues!
2147
2168
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.
2151
2173
run ( ar. arg ( "cq" ) . arg ( dst) . args ( objs) , & cmd) ?;
2152
2174
}
2153
2175
@@ -2639,81 +2661,206 @@ impl Build {
2639
2661
}
2640
2662
}
2641
2663
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) ;
2646
2705
}
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 ( ) ) ) ;
2649
2712
}
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
- }
2661
2713
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) )
2677
2777
}
2778
+ } else {
2779
+ None
2678
2780
}
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)
2707
2854
}
2708
2855
}
2709
- ar
2856
+ } else {
2857
+ name = default;
2858
+ self . cmd ( & name)
2710
2859
}
2711
- None => default_ar,
2712
2860
}
2713
- } else {
2714
- default_ar
2715
2861
} ;
2716
- Ok ( ( self . cmd ( & program) , program) )
2862
+
2863
+ Ok ( ( tool, name) )
2717
2864
}
2718
2865
2719
2866
fn prefix_for_target ( & self , target : & str ) -> Option < String > {
0 commit comments