From 59f0f969aa8a12eb07fb4fd5a7c2093cbd124309 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 13 Dec 2016 18:57:21 +0200 Subject: [PATCH 1/2] Add support for clang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This does a little bit of refactoring in order to introduce abstraction-notion of different families of compilers that may be used, so the gcc-rs didn’t rely so much on matching pathes and targets. --- src/lib.rs | 271 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 174 insertions(+), 97 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 0290bcf..4740682 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -94,6 +94,43 @@ pub struct Tool { path: PathBuf, args: Vec, 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 } } From 7ef1bf4f6d9d075b3aeedb9574cc7c2c0377508c Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 20 Jan 2017 21:14:35 +0200 Subject: [PATCH 2/2] Fix nits --- src/lib.rs | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4740682..426a4e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -102,8 +102,8 @@ pub struct Tool { /// 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 { +#[derive(Copy, Clone, Debug)] +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 @@ -492,10 +492,8 @@ impl Config { let target = self.get_target(); let mut cmd = self.get_base_compiler(); - // 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")) + let nvcc = cmd.path.file_name() + .and_then(|p| p.to_str()).map(|p| p.contains("nvcc")) .unwrap_or(false); // Non-target flags @@ -512,13 +510,9 @@ impl Config { } 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. + // -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2. + "2" | "3" => cmd.args.push("/O2".into()), _ => {} } } @@ -640,7 +634,8 @@ impl Config { cmd.args.push(format!("-stdlib=lib{}", stdlib).into()); } _ => { - // FIXME: perhaps a warning? + println!("cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \ + does not support this option, ignored", cmd.family); } } }