aboutsummaryrefslogtreecommitdiff
path: root/cc/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cc/src/lib.rs')
-rw-r--r--cc/src/lib.rs709
1 files changed, 493 insertions, 216 deletions
diff --git a/cc/src/lib.rs b/cc/src/lib.rs
index 67d8f6f..7672cf4 100644
--- a/cc/src/lib.rs
+++ b/cc/src/lib.rs
@@ -61,15 +61,14 @@
extern crate rayon;
use std::env;
-use std::ffi::{OsString, OsStr};
+use std::ffi::{OsStr, OsString};
use std::fs;
-use std::path::{PathBuf, Path};
-use std::process::{Command, Stdio, Child};
-use std::io::{self, BufReader, BufRead, Read, Write};
+use std::path::{Path, PathBuf};
+use std::process::{Child, Command, Stdio};
+use std::io::{self, BufRead, BufReader, Read, Write};
use std::thread::{self, JoinHandle};
-
-#[cfg(feature = "parallel")]
-use std::sync::Mutex;
+use std::collections::HashMap;
+use std::sync::{Arc, Mutex};
// These modules are all glue to support reading the MSVC version from
// the registry and from COM interfaces
@@ -97,6 +96,7 @@ pub struct Build {
objects: Vec<PathBuf>,
flags: Vec<String>,
flags_supported: Vec<String>,
+ known_flag_support_status: Arc<Mutex<HashMap<String, bool>>>,
files: Vec<PathBuf>,
cpp: bool,
cpp_link_stdlib: Option<Option<String>>,
@@ -116,7 +116,9 @@ pub struct Build {
shared_flag: Option<bool>,
static_flag: Option<bool>,
warnings_into_errors: bool,
- warnings: bool,
+ warnings: Option<bool>,
+ extra_warnings: Option<bool>,
+ env_cache: Arc<Mutex<HashMap<String, Option<String>>>>,
}
/// Represents the types of errors that may occur while using cc-rs.
@@ -174,6 +176,7 @@ pub struct Tool {
env: Vec<(OsString, OsString)>,
family: ToolFamily,
cuda: bool,
+ removed_args: Vec<OsString>,
}
/// Represents the family of tools this tool belongs to.
@@ -189,22 +192,27 @@ enum ToolFamily {
/// and its cross-compilation approach is different.
Clang,
/// Tool is the MSVC cl.exe.
- Msvc,
+ Msvc { clang_cl: bool },
}
impl ToolFamily {
/// What the flag to request debug info for this family of tools look like
- fn debug_flag(&self) -> &'static str {
+ fn add_debug_flags(&self, cmd: &mut Tool) {
match *self {
- ToolFamily::Msvc => "/Z7",
- ToolFamily::Gnu | ToolFamily::Clang => "-g",
+ ToolFamily::Msvc { .. } => {
+ cmd.push_cc_arg("/Z7".into());
+ }
+ ToolFamily::Gnu | ToolFamily::Clang => {
+ cmd.push_cc_arg("-g".into());
+ cmd.push_cc_arg("-fno-omit-frame-pointer".into());
+ }
}
}
/// What the flag to include directories into header search path looks like
fn include_flag(&self) -> &'static str {
match *self {
- ToolFamily::Msvc => "/I",
+ ToolFamily::Msvc { .. } => "/I",
ToolFamily::Gnu | ToolFamily::Clang => "-I",
}
}
@@ -212,26 +220,31 @@ impl ToolFamily {
/// What the flag to request macro-expanded source output looks like
fn expand_flag(&self) -> &'static str {
match *self {
- ToolFamily::Msvc => "/E",
+ ToolFamily::Msvc { .. } => "/E",
ToolFamily::Gnu | ToolFamily::Clang => "-E",
}
}
/// What the flags to enable all warnings
- fn warnings_flags(&self) -> &'static [&'static str] {
- static MSVC_FLAGS: &'static [&'static str] = &["/W4"];
- static GNU_CLANG_FLAGS: &'static [&'static str] = &["-Wall", "-Wextra"];
+ fn warnings_flags(&self) -> &'static str {
+ match *self {
+ ToolFamily::Msvc { .. } => "/W4",
+ ToolFamily::Gnu | ToolFamily::Clang => "-Wall",
+ }
+ }
+ /// What the flags to enable extra warnings
+ fn extra_warnings_flags(&self) -> Option<&'static str> {
match *self {
- ToolFamily::Msvc => &MSVC_FLAGS,
- ToolFamily::Gnu | ToolFamily::Clang => &GNU_CLANG_FLAGS,
+ ToolFamily::Msvc { .. } => None,
+ ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"),
}
}
/// What the flag to turn warning into errors
fn warnings_to_errors_flag(&self) -> &'static str {
match *self {
- ToolFamily::Msvc => "/WX",
+ ToolFamily::Msvc { .. } => "/WX",
ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
}
}
@@ -240,9 +253,8 @@ impl ToolFamily {
/// debug info flag passed to the C++ compiler.
fn nvcc_debug_flag(&self) -> &'static str {
match *self {
- ToolFamily::Msvc => unimplemented!(),
- ToolFamily::Gnu |
- ToolFamily::Clang => "-G",
+ ToolFamily::Msvc { .. } => unimplemented!(),
+ ToolFamily::Gnu | ToolFamily::Clang => "-G",
}
}
@@ -250,11 +262,14 @@ impl ToolFamily {
/// compiler.
fn nvcc_redirect_flag(&self) -> &'static str {
match *self {
- ToolFamily::Msvc => unimplemented!(),
- ToolFamily::Gnu |
- ToolFamily::Clang => "-Xcompiler",
+ ToolFamily::Msvc { .. } => unimplemented!(),
+ ToolFamily::Gnu | ToolFamily::Clang => "-Xcompiler",
}
}
+
+ fn verbose_stderr(&self) -> bool {
+ *self == ToolFamily::Clang
+ }
}
/// Represents an object.
@@ -269,10 +284,7 @@ struct Object {
impl Object {
/// Create a new source file -> object file pair.
fn new(src: PathBuf, dst: PathBuf) -> Object {
- Object {
- src: src,
- dst: dst,
- }
+ Object { src: src, dst: dst }
}
}
@@ -289,6 +301,7 @@ impl Build {
objects: Vec::new(),
flags: Vec::new(),
flags_supported: Vec::new(),
+ known_flag_support_status: Arc::new(Mutex::new(HashMap::new())),
files: Vec::new(),
shared_flag: None,
static_flag: None,
@@ -307,8 +320,10 @@ impl Build {
cargo_metadata: true,
pic: None,
static_crt: None,
- warnings: true,
+ warnings: None,
+ extra_warnings: None,
warnings_into_errors: false,
+ env_cache: Arc::new(Mutex::new(HashMap::new())),
}
}
@@ -344,10 +359,8 @@ impl Build {
/// .compile("foo");
/// ```
pub fn define<'a, V: Into<Option<&'a str>>>(&mut self, var: &str, val: V) -> &mut Build {
- self.definitions.push((
- var.to_string(),
- val.into().map(|s| s.to_string()),
- ));
+ self.definitions
+ .push((var.to_string(), val.into().map(|s| s.to_string())));
self
}
@@ -398,22 +411,40 @@ impl Build {
///
/// It may return error if it's unable to run the compilier with a test file
/// (e.g. the compiler is missing or a write to the `out_dir` failed).
+ ///
+ /// Note: Once computed, the result of this call is stored in the
+ /// `known_flag_support` field. If `is_flag_supported(flag)`
+ /// is called again, the result will be read from the hash table.
pub fn is_flag_supported(&self, flag: &str) -> Result<bool, Error> {
+ let mut known_status = self.known_flag_support_status.lock().unwrap();
+ if let Some(is_supported) = known_status.get(flag).cloned() {
+ return Ok(is_supported);
+ }
+
let out_dir = self.get_out_dir()?;
let src = self.ensure_check_file()?;
let obj = out_dir.join("flag_check");
let target = self.get_target()?;
+ let host = self.get_host()?;
let mut cfg = Build::new();
cfg.flag(flag)
.target(&target)
.opt_level(0)
- .host(&target)
+ .host(&host)
.debug(false)
.cpp(self.cpp)
.cuda(self.cuda);
- let compiler = cfg.try_get_compiler()?;
+ let mut compiler = cfg.try_get_compiler()?;
+
+ // Clang uses stderr for verbose output, which yields a false positive
+ // result if the CFLAGS/CXXFLAGS include -v to aid in debugging.
+ if compiler.family.verbose_stderr() {
+ compiler.remove_arg("-v".into());
+ }
+
let mut cmd = compiler.to_command();
- command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false);
+ let is_arm = target.contains("aarch64") || target.contains("arm");
+ command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false, is_arm);
// We need to explicitly tell msvc not to link and create an exe
// in the root directory of the crate
@@ -424,7 +455,10 @@ impl Build {
cmd.arg(&src);
let output = cmd.output()?;
- Ok(output.stderr.is_empty())
+ let is_supported = output.stderr.is_empty();
+
+ known_status.insert(flag.to_owned(), is_supported);
+ Ok(is_supported)
}
/// Add an arbitrary flag to the invocation of the compiler if it supports it
@@ -565,7 +599,30 @@ impl Build {
/// .compile("libfoo.a");
/// ```
pub fn warnings(&mut self, warnings: bool) -> &mut Build {
- self.warnings = warnings;
+ self.warnings = Some(warnings);
+ self.extra_warnings = Some(warnings);
+ self
+ }
+
+ /// Set extra warnings flags.
+ ///
+ /// Adds some flags:
+ /// - nothing for MSVC.
+ /// - "-Wextra" for GNU and Clang.
+ ///
+ /// Enabled by default.
+ ///
+ /// # Example
+ ///
+ /// ```no_run
+ /// // Disables -Wextra, -Wall remains enabled:
+ /// cc::Build::new()
+ /// .file("src/foo.c")
+ /// .extra_warnings(false)
+ /// .compile("libfoo.a");
+ /// ```
+ pub fn extra_warnings(&mut self, warnings: bool) -> &mut Build {
+ self.extra_warnings = Some(warnings);
self
}
@@ -575,6 +632,8 @@ impl Build {
/// The default value of this property depends on the current target: On
/// OS X `Some("c++")` is used, when compiling for a Visual Studio based
/// target `None` is used and for other targets `Some("stdc++")` is used.
+ /// If the `CXXSTDLIB` environment variable is set, its value will
+ /// override the default value.
///
/// A value of `None` indicates that no automatic linking should happen,
/// otherwise cargo will link against the specified library.
@@ -777,9 +836,8 @@ impl Build {
A: AsRef<OsStr>,
B: AsRef<OsStr>,
{
- self.env.push(
- (a.as_ref().to_owned(), b.as_ref().to_owned()),
- );
+ self.env
+ .push((a.as_ref().to_owned(), b.as_ref().to_owned()));
self
}
@@ -880,31 +938,19 @@ impl Build {
fn compile_objects(&self, objs: &[Object]) -> Result<(), Error> {
use self::rayon::prelude::*;
- let mut cfg = rayon::Configuration::new();
if let Ok(amt) = env::var("NUM_JOBS") {
if let Ok(amt) = amt.parse() {
- cfg = cfg.num_threads(amt);
+ let _ = rayon::ThreadPoolBuilder::new()
+ .num_threads(amt)
+ .build_global();
}
}
- drop(rayon::initialize(cfg));
-
- let results: Mutex<Vec<Result<(), Error>>> = Mutex::new(Vec::new());
-
- objs.par_iter().with_max_len(1).for_each(
- |obj| {
- let res = self.compile_object(obj);
- results.lock().unwrap().push(res)
- },
- );
// Check for any errors and return the first one found.
- for result in results.into_inner().unwrap().iter() {
- if result.is_err() {
- return result.clone();
- }
- }
-
- Ok(())
+ objs.par_iter()
+ .with_max_len(1)
+ .map(|obj| self.compile_object(obj))
+ .collect()
}
#[cfg(not(feature = "parallel"))]
@@ -917,7 +963,8 @@ impl Build {
fn compile_object(&self, obj: &Object) -> Result<(), Error> {
let is_asm = obj.src.extension().and_then(|s| s.to_str()) == Some("asm");
- let msvc = self.get_target()?.contains("msvc");
+ let target = self.get_target()?;
+ let msvc = target.contains("msvc");
let (mut cmd, name) = if msvc && is_asm {
self.msvc_macro_assembler()?
} else {
@@ -931,15 +978,17 @@ impl Build {
compiler
.path
.file_name()
- .ok_or_else(|| {
- Error::new(ErrorKind::IOError, "Failed to get compiler path.")
- })?
+ .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
.to_string_lossy()
.into_owned(),
)
};
- command_add_output_file(&mut cmd, &obj.dst, msvc, is_asm);
- cmd.arg(if msvc { "/c" } else { "-c" });
+ let is_arm = target.contains("aarch64") || target.contains("arm");
+ command_add_output_file(&mut cmd, &obj.dst, msvc, is_asm, is_arm);
+ // armasm and armasm64 don't requrie -c option
+ if !msvc || !is_asm || !is_arm {
+ cmd.arg(if msvc { "/c" } else { "-c" });
+ }
cmd.arg(&obj.src);
run(&mut cmd, &name)?;
@@ -967,9 +1016,7 @@ impl Build {
let name = compiler
.path
.file_name()
- .ok_or_else(|| {
- Error::new(ErrorKind::IOError, "Failed to get compiler path.")
- })?
+ .ok_or_else(|| Error::new(ErrorKind::IOError, "Failed to get compiler path."))?
.to_string_lossy()
.into_owned();
@@ -1032,7 +1079,7 @@ impl Build {
// Non-target flags
// If the flag is not conditioned on target variable, it belongs here :)
match cmd.family {
- ToolFamily::Msvc => {
+ ToolFamily::Msvc { .. } => {
assert!(!self.cuda,
"CUDA C++ compilation not supported for MSVC, yet... but you are welcome to implement it :)");
@@ -1054,8 +1101,8 @@ impl Build {
cmd.args.push(crt_flag.into());
match &opt_level[..] {
- "z" | "s" => cmd.args.push("/Os".into()),
- "1" => cmd.args.push("/O1".into()),
+ // Msvc uses /O1 to enable all optimizations that minimize code size.
+ "z" | "s" | "1" => cmd.args.push("/O1".into()),
// -O3 is a valid value for gcc and clang compilers, but not msvc. Cap to /O2.
"2" | "3" => cmd.args.push("/O2".into()),
_ => {}
@@ -1070,8 +1117,10 @@ impl Build {
cmd.args.push(format!("-O{}", opt_level).into());
}
- cmd.push_cc_arg("-ffunction-sections".into());
- cmd.push_cc_arg("-fdata-sections".into());
+ if !target.contains("-ios") {
+ cmd.push_cc_arg("-ffunction-sections".into());
+ cmd.push_cc_arg("-fdata-sections".into());
+ }
if self.pic.unwrap_or(!target.contains("windows-gnu")) {
cmd.push_cc_arg("-fPIC".into());
}
@@ -1086,8 +1135,8 @@ impl Build {
let nvcc_debug_flag = cmd.family.nvcc_debug_flag().into();
cmd.args.push(nvcc_debug_flag);
}
- let debug_flag = cmd.family.debug_flag().into();
- cmd.push_cc_arg(debug_flag);
+ let family = cmd.family;
+ family.add_debug_flags(&mut cmd);
}
// Target flags
@@ -1095,9 +1144,20 @@ impl Build {
ToolFamily::Clang => {
cmd.args.push(format!("--target={}", target).into());
}
- ToolFamily::Msvc => {
- if target.contains("i586") {
- cmd.args.push("/ARCH:IA32".into());
+ ToolFamily::Msvc { clang_cl } => {
+ if clang_cl {
+ if target.contains("x86_64") {
+ cmd.args.push("-m64".into());
+ } else if target.contains("i586") {
+ cmd.args.push("-m32".into());
+ cmd.args.push("/arch:IA32".into());
+ } else {
+ cmd.args.push("-m32".into());
+ }
+ } else {
+ if target.contains("i586") {
+ cmd.args.push("/ARCH:IA32".into());
+ }
}
}
ToolFamily::Gnu => {
@@ -1109,24 +1169,35 @@ impl Build {
cmd.args.push("-m64".into());
}
- if self.static_flag.is_none() && target.contains("musl") {
- cmd.args.push("-static".into());
+ if self.static_flag.is_none() {
+ let features = env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new());
+ if features.contains("crt-static") {
+ cmd.args.push("-static".into());
+ }
}
// armv7 targets get to use armv7 instructions
- if target.starts_with("armv7-") && target.contains("-linux-") {
+ if (target.starts_with("armv7") || target.starts_with("thumbv7")) && target.contains("-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());
+ // (x86 Android doesn't say "eabi")
+ if target.contains("-androideabi") && target.contains("v7") {
+ // -march=armv7-a handled above
cmd.args.push("-mthumb".into());
- cmd.args.push("-mfpu=vfpv3-d16".into());
+ if !target.contains("neon") {
+ // On android we can guarantee some extra float instructions
+ // (specified in the android spec online)
+ // NEON guarantees even more; see below.
+ cmd.args.push("-mfpu=vfpv3-d16".into());
+ }
cmd.args.push("-mfloat-abi=softfp".into());
}
+ if target.contains("neon") {
+ cmd.args.push("-mfpu=neon-vfpv4".into());
+ }
+
if target.starts_with("armv4t-unknown-linux-") {
cmd.args.push("-march=armv4t".into());
cmd.args.push("-marm".into());
@@ -1169,7 +1240,7 @@ impl Build {
// 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" {
+ if target == "i686-unknown-linux-musl" || target == "i586-unknown-linux-musl" {
cmd.args.push("-Wl,-melf_i386".into());
}
@@ -1193,6 +1264,31 @@ impl Build {
if target.starts_with("thumbv7m") {
cmd.args.push("-march=armv7-m".into());
}
+ if target.starts_with("armebv7r") | target.starts_with("armv7r") {
+ if target.starts_with("armeb") {
+ cmd.args.push("-mbig-endian".into());
+ } else {
+ cmd.args.push("-mlittle-endian".into());
+ }
+
+ // ARM mode
+ cmd.args.push("-marm".into());
+
+ // R Profile
+ cmd.args.push("-march=armv7-r".into());
+
+ if target.ends_with("eabihf") {
+ // Calling convention
+ cmd.args.push("-mfloat-abi=hard".into());
+
+ // lowest common denominator FPU
+ // (see Cortex-R4 technical reference manual)
+ cmd.args.push("-mfpu=vfpv3-d16".into())
+ } else {
+ // Calling convention
+ cmd.args.push("-mfloat-abi=soft".into());
+ }
+ }
}
}
@@ -1212,14 +1308,13 @@ impl Build {
if self.cpp {
match (self.cpp_set_stdlib.as_ref(), cmd.family) {
(None, _) => {}
- (Some(stdlib), ToolFamily::Gnu) |
- (Some(stdlib), ToolFamily::Clang) => {
+ (Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
}
_ => {
println!(
"cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \
- does not support this option, ignored",
+ does not support this option, ignored",
cmd.family
);
}
@@ -1231,9 +1326,19 @@ impl Build {
cmd.args.push(directory.into());
}
- if self.warnings {
- for flag in cmd.family.warnings_flags().iter() {
- cmd.push_cc_arg(flag.into());
+ // If warnings and/or extra_warnings haven't been explicitly set,
+ // then we set them only if the environment doesn't already have
+ // CFLAGS/CXXFLAGS, since those variables presumably already contain
+ // the desired set of warnings flags.
+
+ if self.warnings.unwrap_or(if self.has_flags() { false } else { true }) {
+ let wflags = cmd.family.warnings_flags().into();
+ cmd.push_cc_arg(wflags);
+ }
+
+ if self.extra_warnings.unwrap_or(if self.has_flags() { false } else { true }) {
+ if let Some(wflags) = cmd.family.extra_warnings_flags() {
+ cmd.push_cc_arg(wflags.into());
}
}
@@ -1248,7 +1353,7 @@ impl Build {
}
for &(ref key, ref value) in self.definitions.iter() {
- let lead = if let ToolFamily::Msvc = cmd.family {
+ let lead = if let ToolFamily::Msvc { .. } = cmd.family {
"/"
} else {
"-"
@@ -1268,10 +1373,20 @@ impl Build {
Ok(cmd)
}
+ fn has_flags(&self) -> bool {
+ let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
+ let flags_env_var_value = self.get_var(flags_env_var_name);
+ if let Ok(_) = flags_env_var_value { true } else { false }
+ }
+
fn msvc_macro_assembler(&self) -> Result<(Command, String), Error> {
let target = self.get_target()?;
let tool = if target.contains("x86_64") {
"ml64.exe"
+ } else if target.contains("arm") {
+ "armasm.exe"
+ } else if target.contains("aarch64") {
+ "armasm64.exe"
} else {
"ml.exe"
};
@@ -1307,20 +1422,55 @@ impl Build {
if target.contains("msvc") {
let mut cmd = match self.archiver {
Some(ref s) => self.cmd(s),
- None => {
- windows_registry::find(&target, "lib.exe").unwrap_or_else(
- || {
- self.cmd("lib.exe")
- },
- )
- }
+ None => windows_registry::find(&target, "lib.exe")
+ .unwrap_or_else(|| self.cmd("lib.exe")),
};
+
let mut out = OsString::from("/OUT:");
out.push(dst);
- run(
- cmd.arg(out).arg("/nologo").args(&objects).args(&self.objects),
- "lib.exe",
- )?;
+ cmd.arg(out).arg("/nologo");
+
+ // Similar to https://github.com/rust-lang/rust/pull/47507
+ // and https://github.com/rust-lang/rust/pull/48548
+ let estimated_command_line_len = objects
+ .iter()
+ .chain(&self.objects)
+ .map(|a| a.as_os_str().len())
+ .sum::<usize>();
+ if estimated_command_line_len > 1024 * 6 {
+ let mut args = String::from("\u{FEFF}"); // BOM
+ for arg in objects.iter().chain(&self.objects) {
+ args.push('"');
+ for c in arg.to_str().unwrap().chars() {
+ if c == '"' {
+ args.push('\\')
+ }
+ args.push(c)
+ }
+ args.push('"');
+ args.push('\n');
+ }
+
+ let mut utf16le = Vec::new();
+ for code_unit in args.encode_utf16() {
+ utf16le.push(code_unit as u8);
+ utf16le.push((code_unit >> 8) as u8);
+ }
+
+ let mut args_file = OsString::from(dst);
+ args_file.push(".args");
+ fs::File::create(&args_file)
+ .unwrap()
+ .write_all(&utf16le)
+ .unwrap();
+
+ let mut args_file_arg = OsString::from("@");
+ args_file_arg.push(args_file);
+ cmd.arg(args_file_arg);
+ } else {
+ cmd.args(&objects).args(&self.objects);
+ }
+ run(&mut cmd, "lib.exe")?;
// The Rust compiler will look for libfoo.a and foo.lib, but the
// MSVC linker will also be passed foo.lib, so be sure that both
@@ -1412,6 +1562,18 @@ impl Build {
cmd.args.push("-isysroot".into());
cmd.args.push(sdk_path.trim().into());
+ cmd.args.push("-fembed-bitcode".into());
+ /*
+ * TODO we probably ultimatedly want the -fembed-bitcode-marker flag
+ * but can't have it now because of an issue in LLVM:
+ * https://github.com/alexcrichton/cc-rs/issues/301
+ * https://github.com/rust-lang/rust/pull/48896#comment-372192660
+ */
+ /*
+ if self.get_opt_level()? == "0" {
+ cmd.args.push("-fembed-bitcode-marker".into());
+ }
+ */
Ok(())
}
@@ -1430,44 +1592,53 @@ impl Build {
}
let host = self.get_host()?;
let target = self.get_target()?;
- let (env, msvc, gnu, traditional) = if self.cpp {
- ("CXX", "cl.exe", "g++", "c++")
+ let (env, msvc, gnu, traditional, clang) = if self.cpp {
+ ("CXX", "cl.exe", "g++", "c++", "clang++")
} else {
- ("CC", "cl.exe", "gcc", "cc")
+ ("CC", "cl.exe", "gcc", "cc", "clang")
};
// On Solaris, c++/cc unlikely to exist or be correct.
- let default = if host.contains("solaris") { gnu } else { traditional };
-
- let tool_opt: Option<Tool> =
- self.env_tool(env)
- .map(|(tool, cc, args)| {
- let mut t = Tool::new(PathBuf::from(tool));
- if let Some(cc) = cc {
- t.cc_wrapper_path = Some(PathBuf::from(cc));
- }
- for arg in args {
- t.cc_wrapper_args.push(arg.into());
- }
- t
- })
- .or_else(|| {
- if target.contains("emscripten") {
- let tool = if self.cpp { "em++" } else { "emcc" };
- // Windows uses bat file so we have to be a bit more specific
- if cfg!(windows) {
- let mut t = Tool::new(PathBuf::from("cmd"));
- t.args.push("/c".into());
- t.args.push(format!("{}.bat", tool).into());
- Some(t)
- } else {
- Some(Tool::new(PathBuf::from(tool)))
- }
+ let default = if host.contains("solaris") {
+ gnu
+ } else {
+ traditional
+ };
+
+ let cl_exe = windows_registry::find_tool(&target, "cl.exe");
+
+ let tool_opt: Option<Tool> = self.env_tool(env)
+ .map(|(tool, cc, args)| {
+ // chop off leading/trailing whitespace to work around
+ // semi-buggy build scripts which are shared in
+ // makefiles/configure scripts (where spaces are far more
+ // lenient)
+ let mut t = Tool::new(PathBuf::from(tool.trim()));
+ if let Some(cc) = cc {
+ t.cc_wrapper_path = Some(PathBuf::from(cc));
+ }
+ for arg in args {
+ t.cc_wrapper_args.push(arg.into());
+ }
+ t
+ })
+ .or_else(|| {
+ if target.contains("emscripten") {
+ let tool = if self.cpp { "em++" } else { "emcc" };
+ // Windows uses bat file so we have to be a bit more specific
+ if cfg!(windows) {
+ let mut t = Tool::new(PathBuf::from("cmd"));
+ t.args.push("/c".into());
+ t.args.push(format!("{}.bat", tool).into());
+ Some(t)
} else {
- None
+ Some(Tool::new(PathBuf::from(tool)))
}
- })
- .or_else(|| windows_registry::find_tool(&target, "cl.exe"));
+ } else {
+ None
+ }
+ })
+ .or_else(|| cl_exe.clone());
let tool = match tool_opt {
Some(t) => t,
@@ -1479,7 +1650,20 @@ impl Build {
format!("{}.exe", gnu)
}
} else if target.contains("android") {
- format!("{}-{}", target.replace("armv7", "arm"), gnu)
+ let target = target
+ .replace("armv7neon", "arm")
+ .replace("armv7", "arm")
+ .replace("thumbv7neon", "arm")
+ .replace("thumbv7", "arm");
+ let gnu_compiler = format!("{}-{}", target, gnu);
+ let clang_compiler = format!("{}-{}", target, clang);
+ // Check if gnu compiler is present
+ // if not, use clang
+ if Command::new(&gnu_compiler).spawn().is_ok() {
+ gnu_compiler
+ } else {
+ clang_compiler
+ }
} else if target.contains("cloudabi") {
format!("{}-{}", target, traditional)
} else if self.get_host()? != target {
@@ -1489,6 +1673,7 @@ impl Build {
let prefix = cross_compile.or(match &target[..] {
"aarch64-unknown-linux-gnu" => Some("aarch64-linux-gnu"),
"aarch64-unknown-linux-musl" => Some("aarch64-linux-musl"),
+ "aarch64-unknown-netbsd" => Some("aarch64--netbsd"),
"arm-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
"armv4t-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
"armv5te-unknown-linux-gnueabi" => Some("arm-linux-gnueabi"),
@@ -1500,7 +1685,14 @@ impl Build {
"armv6-unknown-netbsd-eabihf" => Some("armv6--netbsdelf-eabihf"),
"armv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
"armv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
+ "armv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
+ "armv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
+ "thumbv7-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
+ "thumbv7-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
+ "thumbv7neon-unknown-linux-gnueabihf" => Some("arm-linux-gnueabihf"),
+ "thumbv7neon-unknown-linux-musleabihf" => Some("arm-linux-musleabihf"),
"armv7-unknown-netbsd-eabihf" => Some("armv7--netbsdelf-eabihf"),
+ "i586-unknown-linux-musl" => Some("musl"),
"i686-pc-windows-gnu" => Some("i686-w64-mingw32"),
"i686-unknown-linux-musl" => Some("musl"),
"i686-unknown-netbsd" => Some("i486--netbsdelf"),
@@ -1509,13 +1701,19 @@ impl Build {
"mips64-unknown-linux-gnuabi64" => Some("mips64-linux-gnuabi64"),
"mips64el-unknown-linux-gnuabi64" => Some("mips64el-linux-gnuabi64"),
"powerpc-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
+ "powerpc-unknown-linux-gnuspe" => Some("powerpc-linux-gnuspe"),
"powerpc-unknown-netbsd" => Some("powerpc--netbsd"),
"powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"),
"powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"),
"s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"),
+ "sparc-unknown-linux-gnu" => Some("sparc-linux-gnu"),
"sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"),
"sparc64-unknown-netbsd" => Some("sparc64--netbsd"),
"sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"),
+ "armebv7r-none-eabi" => Some("arm-none-eabi"),
+ "armebv7r-none-eabihf" => Some("arm-none-eabi"),
+ "armv7r-none-eabi" => Some("arm-none-eabi"),
+ "armv7r-none-eabihf" => Some("arm-none-eabi"),
"thumbv6m-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabi" => Some("arm-none-eabi"),
"thumbv7em-none-eabihf" => Some("arm-none-eabi"),
@@ -1537,20 +1735,45 @@ impl Build {
}
};
- let tool = if self.cuda {
- assert!(tool.args.is_empty(),
- "CUDA compilation currently assumes empty pre-existing args");
+ let mut tool = if self.cuda {
+ assert!(
+ tool.args.is_empty(),
+ "CUDA compilation currently assumes empty pre-existing args"
+ );
let nvcc = match self.get_var("NVCC") {
Err(_) => "nvcc".into(),
Ok(nvcc) => nvcc,
};
let mut nvcc_tool = Tool::with_features(PathBuf::from(nvcc), self.cuda);
- nvcc_tool.args.push(format!("-ccbin={}", tool.path.display()).into());
+ nvcc_tool
+ .args
+ .push(format!("-ccbin={}", tool.path.display()).into());
nvcc_tool
} else {
tool
};
+ // If we found `cl.exe` in our environment, the tool we're returning is
+ // an MSVC-like tool, *and* no env vars were set then set env vars for
+ // the tool that we're returning.
+ //
+ // Env vars are needed for things like `link.exe` being put into PATH as
+ // well as header include paths sometimes. These paths are automatically
+ // included by default but if the `CC` or `CXX` env vars are set these
+ // won't be used. This'll ensure that when the env vars are used to
+ // configure for invocations like `clang-cl` we still get a "works out
+ // of the box" experience.
+ if let Some(cl_exe) = cl_exe {
+ if tool.family == (ToolFamily::Msvc { clang_cl: true }) &&
+ tool.env.len() == 0 &&
+ target.contains("msvc")
+ {
+ for &(ref k, ref v) in cl_exe.env.iter() {
+ tool.env.push((k.to_owned(), v.to_owned()));
+ }
+ }
+ }
+
Ok(tool)
}
@@ -1568,10 +1791,7 @@ impl Build {
Some(res) => Ok(res),
None => Err(Error::new(
ErrorKind::EnvVarNotFound,
- &format!(
- "Could not find environment variable {}.",
- var_base
- ),
+ &format!("Could not find environment variable {}.", var_base),
)),
}
}
@@ -1585,21 +1805,68 @@ impl Build {
.collect()
}
-
/// Returns compiler path, optional modifier name from whitelist, and arguments vec
fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> {
- self.get_var(name).ok().map(|tool| {
- let whitelist = ["ccache", "distcc", "sccache"];
+ let tool = match self.get_var(name) {
+ Ok(tool) => tool,
+ Err(_) => return None,
+ };
- for t in whitelist.iter() {
- if tool.starts_with(t) && tool[t.len()..].starts_with(' ') {
- let args = tool.split_whitespace().collect::<Vec<_>>();
+ // If this is an exact path on the filesystem we don't want to do any
+ // interpretation at all, just pass it on through. This'll hopefully get
+ // us to support spaces-in-paths.
+ if Path::new(&tool).exists() {
+ return Some((tool, None, Vec::new()));
+ }
+
+ // Ok now we want to handle a couple of scenarios. We'll assume from
+ // here on out that spaces are splitting separate arguments. Two major
+ // features we want to support are:
+ //
+ // CC='sccache cc'
+ //
+ // aka using `sccache` or any other wrapper/caching-like-thing for
+ // compilations. We want to know what the actual compiler is still,
+ // though, because our `Tool` API support introspection of it to see
+ // what compiler is in use.
+ //
+ // additionally we want to support
+ //
+ // CC='cc -flag'
+ //
+ // where the CC env var is used to also pass default flags to the C
+ // compiler.
+ //
+ // It's true that everything here is a bit of a pain, but apparently if
+ // you're not literally make or bash then you get a lot of bug reports.
+ let known_wrappers = ["ccache", "distcc", "sccache", "icecc"];
+
+ let mut parts = tool.split_whitespace();
+ let maybe_wrapper = match parts.next() {
+ Some(s) => s,
+ None => return None,
+ };
- return (args[1].to_string(), Some(t.to_string()), args[2..].iter().map(|s| s.to_string()).collect());
- }
+ let file_stem = Path::new(maybe_wrapper)
+ .file_stem()
+ .unwrap()
+ .to_str()
+ .unwrap();
+ if known_wrappers.contains(&file_stem) {
+ if let Some(compiler) = parts.next() {
+ return Some((
+ compiler.to_string(),
+ Some(maybe_wrapper.to_string()),
+ parts.map(|s| s.to_string()).collect(),
+ ));
}
- (tool, None, Vec::new())
- })
+ }
+
+ Some((
+ maybe_wrapper.to_string(),
+ None,
+ parts.map(|s| s.to_string()).collect(),
+ ))
}
/// Returns the default C++ standard library for the current target: `libc++`
@@ -1608,17 +1875,25 @@ impl Build {
match self.cpp_link_stdlib.clone() {
Some(s) => Ok(s),
None => {
- let target = self.get_target()?;
- if target.contains("msvc") {
- Ok(None)
- } else if target.contains("darwin") {
- Ok(Some("c++".to_string()))
- } else if target.contains("freebsd") {
- Ok(Some("c++".to_string()))
- } else if target.contains("openbsd") {
- Ok(Some("c++".to_string()))
+ if let Ok(stdlib) = self.get_var("CXXSTDLIB") {
+ if stdlib.is_empty() {
+ Ok(None)
+ } else {
+ Ok(Some(stdlib))
+ }
} else {
- Ok(Some("stdc++".to_string()))
+ let target = self.get_target()?;
+ if target.contains("msvc") {
+ Ok(None)
+ } else if target.contains("apple") {
+ Ok(Some("c++".to_string()))
+ } else if target.contains("freebsd") {
+ Ok(Some("c++".to_string()))
+ } else if target.contains("openbsd") {
+ Ok(Some("c++".to_string()))
+ } else {
+ Ok(Some("stdc++".to_string()))
+ }
}
}
}
@@ -1690,8 +1965,13 @@ impl Build {
}
fn getenv(&self, v: &str) -> Option<String> {
+ let mut cache = self.env_cache.lock().unwrap();
+ if let Some(val) = cache.get(v) {
+ return val.clone()
+ }
let r = env::var(v).ok();
self.print(&format!("{} = {:?}", v, r));
+ cache.insert(v.to_string(), r.clone());
r
}
@@ -1700,10 +1980,7 @@ impl Build {
Some(s) => Ok(s),
None => Err(Error::new(
ErrorKind::EnvVarNotFound,
- &format!(
- "Environment variable {} not defined.",
- v.to_string()
- ),
+ &format!("Environment variable {} not defined.", v.to_string()),
)),
}
}
@@ -1729,11 +2006,15 @@ impl Tool {
fn with_features(path: PathBuf, cuda: bool) -> 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") {
+ if fname.contains("clang-cl") {
+ ToolFamily::Msvc { clang_cl: true }
+ } else if fname.contains("cl") &&
+ !fname.contains("cloudabi") &&
+ !fname.contains("uclibc") &&
+ !fname.contains("clang") {
+ ToolFamily::Msvc { clang_cl: false }
+ } else if fname.contains("clang") {
ToolFamily::Clang
- } else if fname.contains("cl") && !fname.contains("cloudabi") &&
- !fname.contains("uclibc") {
- ToolFamily::Msvc
} else {
ToolFamily::Gnu
}
@@ -1748,9 +2029,15 @@ impl Tool {
env: Vec::new(),
family: family,
cuda: cuda,
+ removed_args: Vec::new(),
}
}
+ /// Add an argument to be stripped from the final command arguments.
+ fn remove_arg(&mut self, flag: OsString) {
+ self.removed_args.push(flag);
+ }
+
/// Add a flag, and optionally prepend the NVCC wrapper flag "-Xcompiler".
///
/// Currently this is only used for compiling CUDA sources, since NVCC only
@@ -1773,12 +2060,15 @@ impl Tool {
Some(ref cc_wrapper_path) => {
let mut cmd = Command::new(&cc_wrapper_path);
cmd.arg(&self.path);
- cmd.args(&self.cc_wrapper_args);
cmd
- },
- None => Command::new(&self.path)
+ }
+ None => Command::new(&self.path),
};
- cmd.args(&self.args);
+ cmd.args(&self.cc_wrapper_args);
+
+ let value = self.args.iter().filter(|a| !self.removed_args.contains(a)).collect::<Vec<_>>();
+ cmd.args(&value);
+
for &(ref k, ref v) in self.env.iter() {
cmd.env(k, v);
}
@@ -1822,10 +2112,8 @@ impl Tool {
cc_env.push(arg);
}
cc_env
- },
- None => {
- OsString::from("")
}
+ None => OsString::from(""),
}
}
@@ -1855,7 +2143,10 @@ impl Tool {
/// Whether the tool is MSVC-like.
pub fn is_like_msvc(&self) -> bool {
- self.family == ToolFamily::Msvc
+ match self.family {
+ ToolFamily::Msvc { .. } => true,
+ _ => false,
+ }
}
}
@@ -1868,8 +2159,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
ErrorKind::ToolExecError,
&format!(
"Failed to wait on spawned child process, command {:?} with args {:?}.",
- cmd,
- program
+ cmd, program
),
))
}
@@ -1884,9 +2174,7 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> {
ErrorKind::ToolExecError,
&format!(
"Command {:?} with args {:?} did not execute successfully (status code {}).",
- cmd,
- program,
- status
+ cmd, program, status
),
))
}
@@ -1909,8 +2197,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
ErrorKind::ToolExecError,
&format!(
"Failed to wait on spawned child process, command {:?} with args {:?}.",
- cmd,
- program
+ cmd, program
),
))
}
@@ -1925,9 +2212,7 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> {
ErrorKind::ToolExecError,
&format!(
"Command {:?} with args {:?} did not execute successfully (status code {}).",
- cmd,
- program,
- status
+ cmd, program, status
),
))
}
@@ -1943,39 +2228,30 @@ fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Er
match cmd.stderr(Stdio::piped()).spawn() {
Ok(mut child) => {
let stderr = BufReader::new(child.stderr.take().unwrap());
- let print = thread::spawn(move || for line in stderr.split(b'\n').filter_map(
- |l| l.ok(),
- )
- {
- print!("cargo:warning=");
- std::io::stdout().write_all(&line).unwrap();
- println!("");
+ let print = thread::spawn(move || {
+ for line in stderr.split(b'\n').filter_map(|l| l.ok()) {
+ print!("cargo:warning=");
+ std::io::stdout().write_all(&line).unwrap();
+ println!("");
+ }
});
Ok((child, print))
}
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
let extra = if cfg!(windows) {
" (see https://github.com/alexcrichton/cc-rs#compile-time-requirements \
- for help)"
+ for help)"
} else {
""
};
Err(Error::new(
ErrorKind::ToolNotFound,
- &format!(
- "Failed to find tool. Is `{}` installed?{}",
- program,
- extra
- ),
+ &format!("Failed to find tool. Is `{}` installed?{}", program, extra),
))
}
Err(_) => Err(Error::new(
ErrorKind::ToolExecError,
- &format!(
- "Command {:?} with args {:?} failed to start.",
- cmd,
- program
- ),
+ &format!("Command {:?} with args {:?} failed to start.", cmd, program),
)),
}
}
@@ -1984,9 +2260,10 @@ fn fail(s: &str) -> ! {
panic!("\n\nInternal error occurred: {}\n\n", s)
}
-
-fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool) {
- if msvc && is_asm {
+fn command_add_output_file(cmd: &mut Command, dst: &Path, msvc: bool, is_asm: bool, is_arm: bool) {
+ if msvc && is_asm && is_arm {
+ cmd.arg("-o").arg(&dst);
+ } else if msvc && is_asm {
cmd.arg("/Fo").arg(dst);
} else if msvc {
let mut s = OsString::from("/Fo");