@ -94,6 +94,43 @@ pub struct Tool {
path : PathBuf ,
args : Vec < OsString > ,
env : Vec < ( OsString , OsString ) > ,
family : ToolFamily
}
/// Represents the family of tools this tool belongs to.
///
/// Each family of tools differs in how and what arguments they accept.
///
/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
#[ derive(Copy, Clone) ]
pub enum ToolFamily {
/// Tool is GNU Compiler Collection-like.
Gnu ,
/// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
/// and its cross-compilation approach is different.
Clang ,
/// Tool is the MSVC cl.exe.
Msvc ,
}
impl ToolFamily {
/// What the flag to request debug info for this family of tools look like
fn debug_flag ( & self ) -> & 'static str {
match * self {
ToolFamily ::Msvc = > "/Z7" ,
ToolFamily ::Gnu |
ToolFamily ::Clang = > "-g" ,
}
}
/// What the flag to include directories into header search path looks like
fn include_flag ( & self ) -> & 'static str {
match * self {
ToolFamily ::Msvc = > "/I" ,
ToolFamily ::Gnu |
ToolFamily ::Clang = > "-I" ,
}
}
}
/// Compile a library from the given set of input C files.
@ -452,137 +489,164 @@ impl Config {
/// falling back to the default configuration.
pub fn get_compiler ( & self ) -> Tool {
let opt_level = self . get_opt_level ( ) ;
let debug = self . get_debug ( ) ;
let target = self . get_target ( ) ;
let msvc = target . contains ( "msvc" ) ;
self . print ( & format ! ( "debug={} opt-level={}" , debug , opt_level ) ) ;
let mut cmd = self . get_base_compiler ( ) ;
let nvcc = cmd . path
. to_str ( )
// FIXME: this may detect nvcc if compiler happens to be in a directory containing nvcc but
// otherwise unrelated to nvcc, whatever it is.
let nvcc = cmd . path . to_str ( )
. map ( | path | path . contains ( "nvcc" ) )
. unwrap_or ( false ) ;
if msvc {
cmd . args . push ( "/nologo" . into ( ) ) ;
let features = env ::var ( "CARGO_CFG_TARGET_FEATURE" ) . unwrap_or ( String ::new ( ) ) ;
if features . contains ( "crt-static" ) {
cmd . args . push ( "/MT" . into ( ) ) ;
} else {
cmd . args . push ( "/MD" . into ( ) ) ;
}
match & opt_level [ . . ] {
"z" | "s" = > cmd . args . push ( "/Os" . into ( ) ) ,
"2" = > cmd . args . push ( "/O2" . into ( ) ) ,
"1" = > cmd . args . push ( "/O1" . into ( ) ) ,
_ = > { }
// Non-target flags
// If the flag is not conditioned on target variable, it belongs here :)
match cmd . family {
ToolFamily ::Msvc = > {
cmd . args . push ( "/nologo" . into ( ) ) ;
let features = env ::var ( "CARGO_CFG_TARGET_FEATURE" )
. unwrap_or ( String ::new ( ) ) ;
if features . contains ( "crt-static" ) {
cmd . args . push ( "/MT" . into ( ) ) ;
} else {
cmd . args . push ( "/MD" . into ( ) ) ;
}
match & opt_level [ . . ] {
"z" | "s" = > cmd . args . push ( "/Os" . into ( ) ) ,
"2" = > cmd . args . push ( "/O2" . into ( ) ) ,
// FIXME: -O1 does not exactly match the meaning of /O1 in MSVC. Rather than
// being a flag for “generate sufficiently fast code but also compile fast”,
// /O1 is instead “generate the smallest code in majority of cases”.
"1" = > cmd . args . push ( "/O1" . into ( ) ) ,
// FIXME: this behaves badly if opt_level is set to 3 (which is an option on
// Gnu and Clang-family tools) or more. Consider using /Ox or /O2 instead.
_ = > { }
}
}
if target . contains ( "i586" ) {
cmd . args . push ( "/ARCH:IA32" . into ( ) ) ;
ToolFamily ::Gnu |
ToolFamily ::Clang = > {
cmd . args . push ( format ! ( "-O{}" , opt_level ) . into ( ) ) ;
if ! nvcc {
cmd . args . push ( "-ffunction-sections" . into ( ) ) ;
cmd . args . push ( "-fdata-sections" . into ( ) ) ;
if self . pic . unwrap_or ( ! target . contains ( "i686" ) & &
! target . contains ( "windows-gnu" ) ) {
cmd . args . push ( "-fPIC" . into ( ) ) ;
}
} else if self . pic . unwrap_or ( false ) {
cmd . args . push ( "-Xcompiler" . into ( ) ) ;
cmd . args . push ( "\'-fPIC\'" . into ( ) ) ;
}
}
} else if nvcc {
cmd . args . push ( format ! ( "-O{}" , opt_level ) . into ( ) ) ;
} else {
cmd . args . push ( format ! ( "-O{}" , opt_level ) . into ( ) ) ;
cmd . args . push ( "-ffunction-sections" . into ( ) ) ;
cmd . args . push ( "-fdata-sections" . into ( ) ) ;
}
for arg in self . envflags ( if self . cpp { "CXXFLAGS" } else { "CFLAGS" } ) {
for arg in self . envflags ( if self . cpp { "CXXFLAGS" } else { "CFLAGS" } ) {
cmd . args . push ( arg . into ( ) ) ;
}
if debug {
cmd . args . push ( if msvc { "/Z7" } else { "-g" } . into ( ) ) ;
if self . get_debug ( ) {
cmd . args . push ( cmd . family . debug_flag ( ) . into ( ) ) ;
}
if target . contains ( "-ios" ) {
self . ios_flags ( & mut cmd ) ;
} else if ! msvc {
if target . contains ( "i686" ) | | target . contains ( "i586" ) {
cmd . args . push ( "-m32" . into ( ) ) ;
} else if target . contains ( "x86_64" ) | | target . contains ( "powerpc64" ) {
cmd . args . push ( "-m64" . into ( ) ) ;
// Target flags
match cmd . family {
ToolFamily ::Clang = > {
cmd . args . push ( format ! ( "--target={}" , target ) . into ( ) ) ;
}
if ! nvcc & &
self . pic . unwrap_or ( ! target . contains ( "i686" ) & & ! target . contains ( "windows-gnu" ) ) {
cmd . args . push ( "-fPIC" . into ( ) ) ;
} else if nvcc & & self . pic . unwrap_or ( false ) {
cmd . args . push ( "-Xcompiler" . into ( ) ) ;
cmd . args . push ( "\'-fPIC\'" . into ( ) ) ;
ToolFamily ::Msvc = > {
if target . contains ( "i586" ) {
cmd . args . push ( "/ARCH:IA32" . into ( ) ) ;
}
}
ToolFamily ::Gnu = > {
if target . contains ( "i686" ) | | target . contains ( "i586" ) {
cmd . args . push ( "-m32" . into ( ) ) ;
} else if target . contains ( "x86_64" ) | | target . contains ( "powerpc64" ) {
cmd . args . push ( "-m64" . into ( ) ) ;
}
if target . contains ( "musl" ) {
cmd . args . push ( "-static" . into ( ) ) ;
}
if target . contains ( "musl" ) {
cmd . args . push ( "-static" . into ( ) ) ;
}
// armv7 targets get to use armv7 instructions
if target . starts_with ( "armv7-unknown-linux-" ) {
cmd . args . push ( "-march=armv7-a" . into ( ) ) ;
}
// armv7 targets get to use armv7 instructions
if target . starts_with ( "armv7-unknown-linux-" ) {
cmd . args . push ( "-march=armv7-a" . into ( ) ) ;
}
// On android we can guarantee some extra float instructions
// (specified in the android spec online)
if target . starts_with ( "armv7-linux-androideabi" ) {
cmd . args . push ( "-march=armv7-a" . into ( ) ) ;
cmd . args . push ( "-mfpu=vfpv3-d16" . into ( ) ) ;
}
// On android we can guarantee some extra float instructions
// (specified in the android spec online)
if target . starts_with ( "armv7-linux-androideabi" ) {
cmd . args . push ( "-march=armv7-a" . into ( ) ) ;
cmd . args . push ( "-mfpu=vfpv3-d16" . into ( ) ) ;
}
// For us arm == armv6 by default
if target . starts_with ( "arm-unknown-linux-" ) {
cmd . args . push ( "-march=armv6" . into ( ) ) ;
cmd . args . push ( "-marm" . into ( ) ) ;
}
// For us arm == armv6 by default
if target . starts_with ( "arm-unknown-linux-" ) {
cmd . args . push ( "-march=armv6" . into ( ) ) ;
cmd . args . push ( "-marm" . into ( ) ) ;
}
// Turn codegen down on i586 to avoid some instructions.
if target . starts_with ( "i586-unknown-linux-" ) {
cmd . args . push ( "-march=pentium" . into ( ) ) ;
}
// Turn codegen down on i586 to avoid some instructions.
if target . starts_with ( "i586-unknown-linux-" ) {
cmd . args . push ( "-march=pentium" . into ( ) ) ;
}
// Set codegen level for i686 correctly
if target . starts_with ( "i686-unknown-linux-" ) {
cmd . args . push ( "-march=i686" . into ( ) ) ;
}
// Set codegen level for i686 correctly
if target . starts_with ( "i686-unknown-linux-" ) {
cmd . args . push ( "-march=i686" . into ( ) ) ;
}
// Looks like `musl-gcc` makes is hard for `-m32` to make its way
// all the way to the linker, so we need to actually instruct the
// linker that we're generating 32-bit executables as well. This'll
// typically only be used for build scripts which transitively use
// these flags that try to compile executables.
if target = = "i686-unknown-linux-musl" {
cmd . args . push ( "-Wl,-melf_i386" . into ( ) ) ;
}
// Looks like `musl-gcc` makes is hard for `-m32` to make its way
// all the way to the linker, so we need to actually instruct the
// linker that we're generating 32-bit executables as well. This'll
// typically only be used for build scripts which transitively use
// these flags that try to compile executables.
if target = = "i686-unknown-linux-musl" {
cmd . args . push ( "-Wl,-melf_i386" . into ( ) ) ;
}
if target . starts_with ( "thumb" ) {
cmd . args . push ( "-mthumb" . into ( ) ) ;
if target . starts_with ( "thumb" ) {
cmd . args . push ( "-mthumb" . into ( ) ) ;
if target . ends_with ( "eabihf" ) {
cmd . args . push ( "-mfloat-abi=hard" . into ( ) )
if target . ends_with ( "eabihf" ) {
cmd . args . push ( "-mfloat-abi=hard" . into ( ) )
}
}
}
if target . starts_with ( "thumbv6m" ) {
cmd . args . push ( "-march=armv6s-m" . into ( ) ) ;
}
if target . starts_with ( "thumbv7em" ) {
cmd . args . push ( "-march=armv7e-m" . into ( ) ) ;
if target . starts_with ( "thumbv6m" ) {
cmd . args . push ( "-march=armv6s-m" . into ( ) ) ;
}
if target . starts_with ( "thumbv7em" ) {
cmd . args . push ( "-march=armv7e-m" . into ( ) ) ;
if target . ends_with ( "eabihf" ) {
cmd . args . push ( "-mfpu=fpv4-sp-d16" . into ( ) )
if target . ends_with ( "eabihf" ) {
cmd . args . push ( "-mfpu=fpv4-sp-d16" . into ( ) )
}
}
if target . starts_with ( "thumbv7m" ) {
cmd . args . push ( "-march=armv7-m" . into ( ) ) ;
}
}
if target . starts_with ( "thumbv7m" ) {
cmd . args . push ( "-march=armv7-m" . into ( ) ) ;
}
}
if self . cpp & & ! msvc {
if let Some ( ref stdlib ) = self . cpp_set_stdlib {
cmd . args . push ( format ! ( "-stdlib=lib{}" , stdlib ) . into ( ) ) ;
if target . contains ( "-ios" ) {
// FIXME: potential bug. iOS is always compiled with Clang, but Gcc compiler may be
// detected instead.
self . ios_flags ( & mut cmd ) ;
}
if self . cpp {
match ( self . cpp_set_stdlib . as_ref ( ) , cmd . family ) {
( Some ( stdlib ) , ToolFamily ::Gnu ) |
( Some ( stdlib ) , ToolFamily ::Clang ) = > {
cmd . args . push ( format ! ( "-stdlib=lib{}" , stdlib ) . into ( ) ) ;
}
_ = > {
// FIXME: perhaps a warning?
}
}
}
for directory in self . include_directories . iter ( ) {
cmd . args . push ( if msvc { "/I" } else { "-I" } . into ( ) ) ;
cmd . args . push ( cmd . family . include_flag ( ) . into ( ) ) ;
cmd . args . push ( directory . into ( ) ) ;
}
@ -591,7 +655,7 @@ impl Config {
}
for & ( ref key , ref value ) in self . definitions . iter ( ) {
let lead = if msvc { "/" } else { "-" } ;
let lead = if let ToolFamily ::Msvc = cmd . family { "/" } else { "-" } ;
if let & Some ( ref value ) = value {
cmd . args . push ( format ! ( "{}D{}={}" , lead , key , value ) . into ( ) ) ;
} else {
@ -928,10 +992,23 @@ impl Config {
impl Tool {
fn new ( path : PathBuf ) -> Tool {
// Try to detect family of the tool from its name, falling back to Gnu.
let family = if let Some ( fname ) = path . file_name ( ) . and_then ( | p | p . to_str ( ) ) {
if fname . contains ( "clang" ) {
ToolFamily ::Clang
} else if fname . contains ( "cl" ) {
ToolFamily ::Msvc
} else {
ToolFamily ::Gnu
}
} else {
ToolFamily ::Gnu
} ;
Tool {
path : path ,
args : Vec ::new ( ) ,
env : Vec ::new ( ) ,
family : family
}
}