|
|
@ -21,6 +21,22 @@ |
|
|
|
//!
|
|
|
|
//! [`Build`]: struct.Build.html
|
|
|
|
//!
|
|
|
|
//! # Parallelism
|
|
|
|
//!
|
|
|
|
//! To parallelize computation, enable the `parallel` feature for the crate.
|
|
|
|
//!
|
|
|
|
//! ```toml
|
|
|
|
//! [build-dependencies]
|
|
|
|
//! cc = { version = "1.0", features = ["parallel"] }
|
|
|
|
//! ```
|
|
|
|
//! To specify the max number of concurrent compilation jobs, set the `NUM_JOBS`
|
|
|
|
//! environment variable to the desired amount.
|
|
|
|
//!
|
|
|
|
//! Cargo will also set this environment variable when executed with the `-jN` flag.
|
|
|
|
//!
|
|
|
|
//! If `NUM_JOBS` is not set, the `RAYON_NUM_THREADS` environment variable can
|
|
|
|
//! also specify the build paralellism.
|
|
|
|
//!
|
|
|
|
//! # Examples
|
|
|
|
//!
|
|
|
|
//! Use the `Build` struct to compile `src/foo.c`:
|
|
|
@ -119,18 +135,21 @@ enum ErrorKind { |
|
|
|
ToolNotFound, |
|
|
|
} |
|
|
|
|
|
|
|
/// Represents an internal error that occurred, with an explaination.
|
|
|
|
/// Represents an internal error that occurred, with an explanation.
|
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub struct Error { |
|
|
|
/// Describes the kind of error that occurred.
|
|
|
|
kind: ErrorKind, |
|
|
|
/// More explaination of error that occurred.
|
|
|
|
/// More explanation of error that occurred.
|
|
|
|
message: String, |
|
|
|
} |
|
|
|
|
|
|
|
impl Error { |
|
|
|
fn new(kind: ErrorKind, message: &str) -> Error { |
|
|
|
Error { kind: kind, message: message.to_owned() } |
|
|
|
Error { |
|
|
|
kind: kind, |
|
|
|
message: message.to_owned(), |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -150,9 +169,11 @@ impl From<io::Error> for Error { |
|
|
|
#[derive(Clone, Debug)] |
|
|
|
pub struct Tool { |
|
|
|
path: PathBuf, |
|
|
|
cc_wrapper_path: Option<PathBuf>, |
|
|
|
cc_wrapper_args: Vec<OsString>, |
|
|
|
args: Vec<OsString>, |
|
|
|
env: Vec<(OsString, OsString)>, |
|
|
|
family: ToolFamily |
|
|
|
family: ToolFamily, |
|
|
|
} |
|
|
|
|
|
|
|
/// Represents the family of tools this tool belongs to.
|
|
|
@ -176,8 +197,7 @@ impl ToolFamily { |
|
|
|
fn debug_flag(&self) -> &'static str { |
|
|
|
match *self { |
|
|
|
ToolFamily::Msvc => "/Z7", |
|
|
|
ToolFamily::Gnu | |
|
|
|
ToolFamily::Clang => "-g", |
|
|
|
ToolFamily::Gnu | ToolFamily::Clang => "-g", |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -185,8 +205,7 @@ impl ToolFamily { |
|
|
|
fn include_flag(&self) -> &'static str { |
|
|
|
match *self { |
|
|
|
ToolFamily::Msvc => "/I", |
|
|
|
ToolFamily::Gnu | |
|
|
|
ToolFamily::Clang => "-I", |
|
|
|
ToolFamily::Gnu | ToolFamily::Clang => "-I", |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -194,8 +213,7 @@ impl ToolFamily { |
|
|
|
fn expand_flag(&self) -> &'static str { |
|
|
|
match *self { |
|
|
|
ToolFamily::Msvc => "/E", |
|
|
|
ToolFamily::Gnu | |
|
|
|
ToolFamily::Clang => "-E", |
|
|
|
ToolFamily::Gnu | ToolFamily::Clang => "-E", |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -206,8 +224,7 @@ impl ToolFamily { |
|
|
|
|
|
|
|
match *self { |
|
|
|
ToolFamily::Msvc => &MSVC_FLAGS, |
|
|
|
ToolFamily::Gnu | |
|
|
|
ToolFamily::Clang => &GNU_CLANG_FLAGS, |
|
|
|
ToolFamily::Gnu | ToolFamily::Clang => &GNU_CLANG_FLAGS, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -215,8 +232,7 @@ impl ToolFamily { |
|
|
|
fn warnings_to_errors_flag(&self) -> &'static str { |
|
|
|
match *self { |
|
|
|
ToolFamily::Msvc => "/WX", |
|
|
|
ToolFamily::Gnu | |
|
|
|
ToolFamily::Clang => "-Werror" |
|
|
|
ToolFamily::Gnu | ToolFamily::Clang => "-Werror", |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -362,7 +378,10 @@ 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 |
|
|
|
} |
|
|
|
|
|
|
@ -417,11 +436,18 @@ impl Build { |
|
|
|
.opt_level(0) |
|
|
|
.host(&target) |
|
|
|
.debug(false) |
|
|
|
.cpp(self.cpp) |
|
|
|
.cpp(self.cpp); |
|
|
|
.cuda(self.cuda); |
|
|
|
let compiler = cfg.try_get_compiler()?; |
|
|
|
let mut cmd = compiler.to_command(); |
|
|
|
command_add_output_file(&mut cmd, &obj, target.contains("msvc"), false); |
|
|
|
|
|
|
|
// We need to explicitly tell msvc not to link and create an exe
|
|
|
|
// in the root directory of the crate
|
|
|
|
if target.contains("msvc") { |
|
|
|
cmd.arg("/c"); |
|
|
|
} |
|
|
|
|
|
|
|
cmd.arg(&src); |
|
|
|
|
|
|
|
let output = cmd.output()?; |
|
|
@ -497,8 +523,10 @@ impl Build { |
|
|
|
|
|
|
|
/// Add files which will be compiled
|
|
|
|
pub fn files<P>(&mut self, p: P) -> &mut Build |
|
|
|
where P: IntoIterator, |
|
|
|
P::Item: AsRef<Path> { |
|
|
|
where |
|
|
|
P: IntoIterator, |
|
|
|
P::Item: AsRef<Path>, |
|
|
|
{ |
|
|
|
for file in p.into_iter() { |
|
|
|
self.file(file); |
|
|
|
} |
|
|
@ -604,7 +632,10 @@ impl Build { |
|
|
|
/// .cpp_link_stdlib("stdc++")
|
|
|
|
/// .compile("libfoo.so");
|
|
|
|
/// ```
|
|
|
|
pub fn cpp_link_stdlib<'a, V: Into<Option<&'a str>>>(&mut self, cpp_link_stdlib: V) -> &mut Build { |
|
|
|
pub fn cpp_link_stdlib<'a, V: Into<Option<&'a str>>>( |
|
|
|
&mut self, |
|
|
|
cpp_link_stdlib: V, |
|
|
|
) -> &mut Build { |
|
|
|
self.cpp_link_stdlib = Some(cpp_link_stdlib.into().map(|s| s.into())); |
|
|
|
self |
|
|
|
} |
|
|
@ -642,7 +673,10 @@ impl Build { |
|
|
|
/// .cpp_set_stdlib("c++")
|
|
|
|
/// .compile("libfoo.a");
|
|
|
|
/// ```
|
|
|
|
pub fn cpp_set_stdlib<'a, V: Into<Option<&'a str>>>(&mut self, cpp_set_stdlib: V) -> &mut Build { |
|
|
|
pub fn cpp_set_stdlib<'a, V: Into<Option<&'a str>>>( |
|
|
|
&mut self, |
|
|
|
cpp_set_stdlib: V, |
|
|
|
) -> &mut Build { |
|
|
|
let cpp_set_stdlib = cpp_set_stdlib.into(); |
|
|
|
self.cpp_set_stdlib = cpp_set_stdlib.map(|s| s.into()); |
|
|
|
self.cpp_link_stdlib(cpp_set_stdlib); |
|
|
@ -777,10 +811,13 @@ impl Build { |
|
|
|
|
|
|
|
#[doc(hidden)] |
|
|
|
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build |
|
|
|
where A: AsRef<OsStr>, |
|
|
|
B: AsRef<OsStr> |
|
|
|
where |
|
|
|
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 |
|
|
|
} |
|
|
|
|
|
|
@ -803,14 +840,21 @@ impl Build { |
|
|
|
for file in self.files.iter() { |
|
|
|
let obj = dst.join(file).with_extension("o"); |
|
|
|
let obj = if !obj.starts_with(&dst) { |
|
|
|
dst.join(obj.file_name().ok_or_else(|| Error::new(ErrorKind::IOError, "Getting object file details failed."))?) |
|
|
|
dst.join(obj.file_name().ok_or_else(|| { |
|
|
|
Error::new(ErrorKind::IOError, "Getting object file details failed.") |
|
|
|
})?) |
|
|
|
} else { |
|
|
|
obj |
|
|
|
}; |
|
|
|
|
|
|
|
match obj.parent() { |
|
|
|
Some(s) => fs::create_dir_all(s)?, |
|
|
|
None => return Err(Error::new(ErrorKind::IOError, "Getting object file details failed.")), |
|
|
|
None => { |
|
|
|
return Err(Error::new( |
|
|
|
ErrorKind::IOError, |
|
|
|
"Getting object file details failed.", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
objects.push(Object::new(file.to_path_buf(), obj)); |
|
|
@ -820,7 +864,8 @@ impl Build { |
|
|
|
|
|
|
|
if self.get_target()?.contains("msvc") { |
|
|
|
let compiler = self.get_base_compiler()?; |
|
|
|
let atlmfc_lib = compiler.env() |
|
|
|
let atlmfc_lib = compiler |
|
|
|
.env() |
|
|
|
.iter() |
|
|
|
.find(|&&(ref var, _)| var.as_os_str() == OsStr::new("LIB")) |
|
|
|
.and_then(|&(_, ref lib_paths)| { |
|
|
@ -831,7 +876,10 @@ impl Build { |
|
|
|
}); |
|
|
|
|
|
|
|
if let Some(atlmfc_lib) = atlmfc_lib { |
|
|
|
self.print(&format!("cargo:rustc-link-search=native={}", atlmfc_lib.display())); |
|
|
|
self.print(&format!( |
|
|
|
"cargo:rustc-link-search=native={}", |
|
|
|
atlmfc_lib.display() |
|
|
|
)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -880,8 +928,12 @@ impl Build { |
|
|
|
|
|
|
|
let results: Mutex<Vec<Result<(), Error>>> = Mutex::new(Vec::new()); |
|
|
|
|
|
|
|
objs.par_iter().with_max_len(1) |
|
|
|
.for_each(|obj| results.lock().unwrap().push(self.compile_object(obj))); |
|
|
|
objs.par_iter().with_max_len(1).for_each( |
|
|
|
|&(ref src, ref dst)| { |
|
|
|
let res = self.compile_object(src, dst); |
|
|
|
results.lock().unwrap().push(res) |
|
|
|
}, |
|
|
|
); |
|
|
|
|
|
|
|
// Check for any errors and return the first one found.
|
|
|
|
for result in results.into_inner().unwrap().iter() { |
|
|
@ -912,12 +964,17 @@ impl Build { |
|
|
|
for &(ref a, ref b) in self.env.iter() { |
|
|
|
cmd.env(a, b); |
|
|
|
} |
|
|
|
(cmd, |
|
|
|
compiler.path |
|
|
|
( |
|
|
|
cmd, |
|
|
|
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()) |
|
|
|
.into_owned(), |
|
|
|
) |
|
|
|
}; |
|
|
|
command_add_output_file(&mut cmd, &obj.dst, msvc, is_asm); |
|
|
|
cmd.arg(if msvc { "/c" } else { "-c" }); |
|
|
@ -936,16 +993,21 @@ impl Build { |
|
|
|
} |
|
|
|
cmd.arg(compiler.family.expand_flag()); |
|
|
|
|
|
|
|
assert!(self.files.len() <= 1, |
|
|
|
"Expand may only be called for a single file"); |
|
|
|
assert!( |
|
|
|
self.files.len() <= 1, |
|
|
|
"Expand may only be called for a single file" |
|
|
|
); |
|
|
|
|
|
|
|
for file in self.files.iter() { |
|
|
|
cmd.arg(file); |
|
|
|
} |
|
|
|
|
|
|
|
let name = compiler.path |
|
|
|
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(); |
|
|
|
|
|
|
@ -1004,6 +1066,11 @@ impl Build { |
|
|
|
let target = self.get_target()?; |
|
|
|
|
|
|
|
let mut cmd = self.get_base_compiler()?; |
|
|
|
let nvcc = cmd.path |
|
|
|
.file_name() |
|
|
|
.and_then(|p| p.to_str()) |
|
|
|
.map(|p| p.contains("nvcc")) |
|
|
|
.unwrap_or(false); |
|
|
|
|
|
|
|
// Non-target flags
|
|
|
|
// If the flag is not conditioned on target variable, it belongs here :)
|
|
|
@ -1018,14 +1085,14 @@ impl Build { |
|
|
|
Some(true) => "/MT", |
|
|
|
Some(false) => "/MD", |
|
|
|
None => { |
|
|
|
let features = env::var("CARGO_CFG_TARGET_FEATURE") |
|
|
|
.unwrap_or(String::new()); |
|
|
|
let features = |
|
|
|
env::var("CARGO_CFG_TARGET_FEATURE").unwrap_or(String::new()); |
|
|
|
if features.contains("crt-static") { |
|
|
|
"/MT" |
|
|
|
} else { |
|
|
|
"/MD" |
|
|
|
} |
|
|
|
}, |
|
|
|
} |
|
|
|
}; |
|
|
|
cmd.args.push(crt_flag.into()); |
|
|
|
|
|
|
@ -1037,8 +1104,7 @@ impl Build { |
|
|
|
_ => {} |
|
|
|
} |
|
|
|
} |
|
|
|
ToolFamily::Gnu | |
|
|
|
ToolFamily::Clang => { |
|
|
|
ToolFamily::Gnu | ToolFamily::Clang => { |
|
|
|
// arm-linux-androideabi-gcc 4.8 shipped with Android NDK does
|
|
|
|
// not support '-Oz'
|
|
|
|
if opt_level == "z" && cmd.family != ToolFamily::Clang { |
|
|
@ -1083,6 +1149,8 @@ impl Build { |
|
|
|
ToolFamily::Gnu => { |
|
|
|
if target.contains("i686") || target.contains("i586") { |
|
|
|
cmd.args.push("-m32".into()); |
|
|
|
} else if target == "x86_64-unknown-linux-gnux32" { |
|
|
|
cmd.args.push("-mx32".into()); |
|
|
|
} else if target.contains("x86_64") || target.contains("powerpc64") { |
|
|
|
cmd.args.push("-m64".into()); |
|
|
|
} |
|
|
@ -1184,8 +1252,11 @@ impl Build { |
|
|
|
cmd.args.push(format!("-stdlib=lib{}", stdlib).into()); |
|
|
|
} |
|
|
|
_ => { |
|
|
|
println!("cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \ |
|
|
|
does not support this option, ignored", cmd.family); |
|
|
|
println!( |
|
|
|
"cargo:warning=cpp_set_stdlib is specified, but the {:?} compiler \ |
|
|
|
does not support this option, ignored", |
|
|
|
cmd.family |
|
|
|
); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -1195,6 +1266,12 @@ impl Build { |
|
|
|
cmd.args.push(directory.into()); |
|
|
|
} |
|
|
|
|
|
|
|
if self.warnings { |
|
|
|
for flag in cmd.family.warnings_flags().iter() { |
|
|
|
cmd.args.push(flag.into()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
for flag in self.flags.iter() { |
|
|
|
cmd.args.push(flag.into()); |
|
|
|
} |
|
|
@ -1207,7 +1284,11 @@ impl Build { |
|
|
|
} |
|
|
|
|
|
|
|
for &(ref key, ref value) in self.definitions.iter() { |
|
|
|
let lead = if let ToolFamily::Msvc = cmd.family {"/"} 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 { |
|
|
@ -1269,37 +1350,46 @@ 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")?; |
|
|
|
run( |
|
|
|
cmd.arg(out).arg("/nologo").args(objects).args( |
|
|
|
&self.objects, |
|
|
|
), |
|
|
|
"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
|
|
|
|
// exist for now.
|
|
|
|
let lib_dst = dst.with_file_name(format!("{}.lib", lib_name)); |
|
|
|
let _ = fs::remove_file(&lib_dst); |
|
|
|
match fs::hard_link(&dst, &lib_dst) |
|
|
|
.or_else(|_| { |
|
|
|
match fs::hard_link(&dst, &lib_dst).or_else(|_| { |
|
|
|
// if hard-link fails, just copy (ignoring the number of bytes written)
|
|
|
|
fs::copy(&dst, &lib_dst).map(|_| ()) |
|
|
|
}) { |
|
|
|
Ok(_) => (), |
|
|
|
Err(_) => return Err(Error::new(ErrorKind::IOError, "Could not copy or create a hard-link to the generated lib file.")), |
|
|
|
Err(_) => { |
|
|
|
return Err(Error::new( |
|
|
|
ErrorKind::IOError, |
|
|
|
"Could not copy or create a hard-link to the generated lib file.", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
} else { |
|
|
|
let (mut ar, cmd) = self.get_ar()?; |
|
|
|
run(ar |
|
|
|
.arg("crs") |
|
|
|
.arg(dst) |
|
|
|
.args(&objects) |
|
|
|
.args(&self.objects), |
|
|
|
&cmd)?; |
|
|
|
run( |
|
|
|
ar.arg("crs").arg(dst).args(objects).args(&self.objects), |
|
|
|
&cmd, |
|
|
|
)?; |
|
|
|
} |
|
|
|
|
|
|
|
Ok(()) |
|
|
@ -1312,14 +1402,24 @@ impl Build { |
|
|
|
} |
|
|
|
|
|
|
|
let target = self.get_target()?; |
|
|
|
let arch = target.split('-').nth(0).ok_or_else(|| Error::new(ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target."))?; |
|
|
|
let arch = target.split('-').nth(0).ok_or_else(|| { |
|
|
|
Error::new( |
|
|
|
ErrorKind::ArchitectureInvalid, |
|
|
|
"Unknown architecture for iOS target.", |
|
|
|
) |
|
|
|
})?; |
|
|
|
let arch = match arch { |
|
|
|
"arm" | "armv7" | "thumbv7" => ArchSpec::Device("armv7"), |
|
|
|
"armv7s" | "thumbv7s" => ArchSpec::Device("armv7s"), |
|
|
|
"arm64" | "aarch64" => ArchSpec::Device("arm64"), |
|
|
|
"i386" | "i686" => ArchSpec::Simulator("-m32"), |
|
|
|
"x86_64" => ArchSpec::Simulator("-m64"), |
|
|
|
_ => return Err(Error::new(ErrorKind::ArchitectureInvalid, "Unknown architecture for iOS target.")), |
|
|
|
_ => { |
|
|
|
return Err(Error::new( |
|
|
|
ErrorKind::ArchitectureInvalid, |
|
|
|
"Unknown architecture for iOS target.", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
let sdk = match arch { |
|
|
@ -1347,7 +1447,12 @@ impl Build { |
|
|
|
|
|
|
|
let sdk_path = match String::from_utf8(sdk_path) { |
|
|
|
Ok(p) => p, |
|
|
|
Err(_) => return Err(Error::new(ErrorKind::IOError, "Unable to determine iOS SDK path.")), |
|
|
|
Err(_) => { |
|
|
|
return Err(Error::new( |
|
|
|
ErrorKind::IOError, |
|
|
|
"Unable to determine iOS SDK path.", |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
cmd.args.push("-isysroot".into()); |
|
|
@ -1385,21 +1490,21 @@ impl Build { |
|
|
|
"cc" |
|
|
|
}; |
|
|
|
|
|
|
|
let tool_opt: Option<Tool> = self.env_tool(env) |
|
|
|
.map(|(tool, args)| { |
|
|
|
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.args.push(arg.into()); |
|
|
|
t.cc_wrapper_args.push(arg.into()); |
|
|
|
} |
|
|
|
t |
|
|
|
}) |
|
|
|
.or_else(|| { |
|
|
|
if target.contains("emscripten") { |
|
|
|
let tool = if self.cpp { |
|
|
|
"em++" |
|
|
|
} else { |
|
|
|
"emcc" |
|
|
|
}; |
|
|
|
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")); |
|
|
@ -1454,6 +1559,7 @@ impl Build { |
|
|
|
"powerpc64-unknown-linux-gnu" => Some("powerpc-linux-gnu"), |
|
|
|
"powerpc64le-unknown-linux-gnu" => Some("powerpc64le-linux-gnu"), |
|
|
|
"s390x-unknown-linux-gnu" => Some("s390x-linux-gnu"), |
|
|
|
"sparc64-unknown-linux-gnu" => Some("sparc64-linux-gnu"), |
|
|
|
"sparc64-unknown-netbsd" => Some("sparc64--netbsd"), |
|
|
|
"sparcv9-sun-solaris" => Some("sparcv9-sun-solaris"), |
|
|
|
"thumbv6m-none-eabi" => Some("arm-none-eabi"), |
|
|
@ -1506,7 +1612,13 @@ impl Build { |
|
|
|
|
|
|
|
match res { |
|
|
|
Some(res) => Ok(res), |
|
|
|
None => Err(Error::new(ErrorKind::EnvVarNotFound, &format!("Could not find environment variable {}.", var_base))), |
|
|
|
None => Err(Error::new( |
|
|
|
ErrorKind::EnvVarNotFound, |
|
|
|
&format!( |
|
|
|
"Could not find environment variable {}.", |
|
|
|
var_base |
|
|
|
), |
|
|
|
)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1519,15 +1631,20 @@ impl Build { |
|
|
|
.collect() |
|
|
|
} |
|
|
|
|
|
|
|
fn env_tool(&self, name: &str) -> Option<(String, Vec<String>)> { |
|
|
|
|
|
|
|
/// 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"]; |
|
|
|
|
|
|
|
for t in whitelist.iter() { |
|
|
|
if tool.starts_with(t) && tool[t.len()..].starts_with(' ') { |
|
|
|
return (t.to_string(), vec![tool[t.len()..].trim_left().to_string()]); |
|
|
|
let args = tool.split_whitespace().collect::<Vec<_>>(); |
|
|
|
|
|
|
|
return (args[1].to_string(), Some(t.to_string()), args[2..].iter().map(|s| s.to_string()).collect()); |
|
|
|
} |
|
|
|
} |
|
|
|
(tool, Vec::new()) |
|
|
|
(tool, None, Vec::new()) |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
@ -1547,17 +1664,17 @@ impl Build { |
|
|
|
} else { |
|
|
|
Ok(Some("stdc++".to_string())) |
|
|
|
} |
|
|
|
}, |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn get_ar(&self) -> Result<(Command, String), Error> { |
|
|
|
if let Some(ref p) = self.archiver { |
|
|
|
let name = p.file_name().and_then(|s| s.to_str()).unwrap_or("ar"); |
|
|
|
return Ok((self.cmd(p), name.to_string())) |
|
|
|
return Ok((self.cmd(p), name.to_string())); |
|
|
|
} |
|
|
|
if let Ok(p) = self.get_var("AR") { |
|
|
|
return Ok((self.cmd(&p), p)) |
|
|
|
return Ok((self.cmd(&p), p)); |
|
|
|
} |
|
|
|
let program = if self.get_target()?.contains("android") { |
|
|
|
format!("{}-ar", self.get_target()?.replace("armv7", "arm")) |
|
|
@ -1566,7 +1683,7 @@ impl Build { |
|
|
|
if cfg!(windows) { |
|
|
|
let mut cmd = self.cmd("cmd"); |
|
|
|
cmd.arg("/c").arg("emar.bat"); |
|
|
|
return Ok((cmd, "emar.bat".to_string())) |
|
|
|
return Ok((cmd, "emar.bat".to_string())); |
|
|
|
} |
|
|
|
|
|
|
|
"emar".to_string() |
|
|
@ -1598,20 +1715,21 @@ impl Build { |
|
|
|
} |
|
|
|
|
|
|
|
fn get_debug(&self) -> bool { |
|
|
|
self.debug.unwrap_or_else(|| { |
|
|
|
match self.getenv("DEBUG") { |
|
|
|
self.debug.unwrap_or_else(|| match self.getenv("DEBUG") { |
|
|
|
Some(s) => s != "false", |
|
|
|
None => false, |
|
|
|
} |
|
|
|
}) |
|
|
|
} |
|
|
|
|
|
|
|
fn get_out_dir(&self) -> Result<PathBuf, Error> { |
|
|
|
match self.out_dir.clone() { |
|
|
|
Some(p) => Ok(p), |
|
|
|
None => Ok(env::var_os("OUT_DIR") |
|
|
|
.map(PathBuf::from) |
|
|
|
.ok_or_else(|| Error::new(ErrorKind::EnvVarNotFound, "Environment variable OUT_DIR not defined."))?), |
|
|
|
None => Ok(env::var_os("OUT_DIR").map(PathBuf::from).ok_or_else(|| { |
|
|
|
Error::new( |
|
|
|
ErrorKind::EnvVarNotFound, |
|
|
|
"Environment variable OUT_DIR not defined.", |
|
|
|
) |
|
|
|
})?), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1624,7 +1742,13 @@ impl Build { |
|
|
|
fn getenv_unwrap(&self, v: &str) -> Result<String, Error> { |
|
|
|
match self.getenv(v) { |
|
|
|
Some(s) => Ok(s), |
|
|
|
None => Err(Error::new(ErrorKind::EnvVarNotFound, &format!("Environment variable {} not defined.", v.to_string()))), |
|
|
|
None => Err(Error::new( |
|
|
|
ErrorKind::EnvVarNotFound, |
|
|
|
&format!( |
|
|
|
"Environment variable {} not defined.", |
|
|
|
v.to_string() |
|
|
|
), |
|
|
|
)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1657,9 +1781,11 @@ impl Tool { |
|
|
|
}; |
|
|
|
Tool { |
|
|
|
path: path, |
|
|
|
cc_wrapper_path: None, |
|
|
|
cc_wrapper_args: Vec::new(), |
|
|
|
args: Vec::new(), |
|
|
|
env: Vec::new(), |
|
|
|
family: family |
|
|
|
family: family, |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1680,7 +1806,15 @@ impl Tool { |
|
|
|
/// command returned will already have the initial arguments and environment
|
|
|
|
/// variables configured.
|
|
|
|
pub fn to_command(&self) -> Command { |
|
|
|
let mut cmd = Command::new(&self.path); |
|
|
|
let mut cmd = match self.cc_wrapper_path { |
|
|
|
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) |
|
|
|
}; |
|
|
|
cmd.args(&self.args); |
|
|
|
for &(ref k, ref v) in self.env.iter() { |
|
|
|
cmd.env(k, v); |
|
|
@ -1709,13 +1843,73 @@ impl Tool { |
|
|
|
pub fn env(&self) -> &[(OsString, OsString)] { |
|
|
|
&self.env |
|
|
|
} |
|
|
|
|
|
|
|
/// Returns the compiler command in format of CC environment variable.
|
|
|
|
/// Or empty string if CC env was not present
|
|
|
|
///
|
|
|
|
/// This is typically used by configure script
|
|
|
|
pub fn cc_env(&self) -> OsString { |
|
|
|
match self.cc_wrapper_path { |
|
|
|
Some(ref cc_wrapper_path) => { |
|
|
|
let mut cc_env = cc_wrapper_path.as_os_str().to_owned(); |
|
|
|
cc_env.push(" "); |
|
|
|
cc_env.push(self.path.to_path_buf().into_os_string()); |
|
|
|
for arg in self.cc_wrapper_args.iter() { |
|
|
|
cc_env.push(" "); |
|
|
|
cc_env.push(arg); |
|
|
|
} |
|
|
|
cc_env |
|
|
|
}, |
|
|
|
None => { |
|
|
|
OsString::from("") |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/// Returns the compiler flags in format of CFLAGS environment variable.
|
|
|
|
/// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
|
|
|
|
/// This is typically used by configure script
|
|
|
|
pub fn cflags_env(&self) -> OsString { |
|
|
|
let mut flags = OsString::new(); |
|
|
|
for (i, arg) in self.args.iter().enumerate() { |
|
|
|
if i > 0 { |
|
|
|
flags.push(" "); |
|
|
|
} |
|
|
|
flags.push(arg); |
|
|
|
} |
|
|
|
flags |
|
|
|
} |
|
|
|
|
|
|
|
/// Whether the tool is GNU Compiler Collection-like.
|
|
|
|
pub fn is_like_gnu(&self) -> bool { |
|
|
|
self.family == ToolFamily::Gnu |
|
|
|
} |
|
|
|
|
|
|
|
/// Whether the tool is Clang-like.
|
|
|
|
pub fn is_like_clang(&self) -> bool { |
|
|
|
self.family == ToolFamily::Clang |
|
|
|
} |
|
|
|
|
|
|
|
/// Whether the tool is MSVC-like.
|
|
|
|
pub fn is_like_msvc(&self) -> bool { |
|
|
|
self.family == ToolFamily::Msvc |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
fn run(cmd: &mut Command, program: &str) -> Result<(), Error> { |
|
|
|
let (mut child, print) = spawn(cmd, program)?; |
|
|
|
let status = match child.wait() { |
|
|
|
Ok(s) => s, |
|
|
|
Err(_) => return Err(Error::new(ErrorKind::ToolExecError, &format!("Failed to wait on spawned child process, command {:?} with args {:?}.", cmd, program))), |
|
|
|
Err(_) => { |
|
|
|
return Err(Error::new( |
|
|
|
ErrorKind::ToolExecError, |
|
|
|
&format!( |
|
|
|
"Failed to wait on spawned child process, command {:?} with args {:?}.", |
|
|
|
cmd, |
|
|
|
program |
|
|
|
), |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
print.join().unwrap(); |
|
|
|
println!("{}", status); |
|
|
@ -1723,7 +1917,15 @@ fn run(cmd: &mut Command, program: &str) -> Result<(), Error> { |
|
|
|
if status.success() { |
|
|
|
Ok(()) |
|
|
|
} else { |
|
|
|
Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} did not execute successfully (status code {}).", cmd, program, status))) |
|
|
|
Err(Error::new( |
|
|
|
ErrorKind::ToolExecError, |
|
|
|
&format!( |
|
|
|
"Command {:?} with args {:?} did not execute successfully (status code {}).", |
|
|
|
cmd, |
|
|
|
program, |
|
|
|
status |
|
|
|
), |
|
|
|
)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1731,10 +1933,24 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> { |
|
|
|
cmd.stdout(Stdio::piped()); |
|
|
|
let (mut child, print) = spawn(cmd, program)?; |
|
|
|
let mut stdout = vec![]; |
|
|
|
child.stdout.take().unwrap().read_to_end(&mut stdout).unwrap(); |
|
|
|
child |
|
|
|
.stdout |
|
|
|
.take() |
|
|
|
.unwrap() |
|
|
|
.read_to_end(&mut stdout) |
|
|
|
.unwrap(); |
|
|
|
let status = match child.wait() { |
|
|
|
Ok(s) => s, |
|
|
|
Err(_) => return Err(Error::new(ErrorKind::ToolExecError, &format!("Failed to wait on spawned child process, command {:?} with args {:?}.", cmd, program))), |
|
|
|
Err(_) => { |
|
|
|
return Err(Error::new( |
|
|
|
ErrorKind::ToolExecError, |
|
|
|
&format!( |
|
|
|
"Failed to wait on spawned child process, command {:?} with args {:?}.", |
|
|
|
cmd, |
|
|
|
program |
|
|
|
), |
|
|
|
)) |
|
|
|
} |
|
|
|
}; |
|
|
|
print.join().unwrap(); |
|
|
|
println!("{}", status); |
|
|
@ -1742,7 +1958,15 @@ fn run_output(cmd: &mut Command, program: &str) -> Result<Vec<u8>, Error> { |
|
|
|
if status.success() { |
|
|
|
Ok(stdout) |
|
|
|
} else { |
|
|
|
Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} did not execute successfully (status code {}).", cmd, program, status))) |
|
|
|
Err(Error::new( |
|
|
|
ErrorKind::ToolExecError, |
|
|
|
&format!( |
|
|
|
"Command {:?} with args {:?} did not execute successfully (status code {}).", |
|
|
|
cmd, |
|
|
|
program, |
|
|
|
status |
|
|
|
), |
|
|
|
)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1756,12 +1980,13 @@ 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()) { |
|
|
|
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)) |
|
|
|
} |
|
|
@ -1772,9 +1997,23 @@ fn spawn(cmd: &mut Command, program: &str) -> Result<(Child, JoinHandle<()>), Er |
|
|
|
} else { |
|
|
|
"" |
|
|
|
}; |
|
|
|
Err(Error::new(ErrorKind::ToolNotFound, &format!("Failed to find tool. Is `{}` installed?{}", program, extra))) |
|
|
|
} |
|
|
|
Err(_) => Err(Error::new(ErrorKind::ToolExecError, &format!("Command {:?} with args {:?} failed to start.", cmd, program))), |
|
|
|
Err(Error::new( |
|
|
|
ErrorKind::ToolNotFound, |
|
|
|
&format!( |
|
|
|
"Failed to find tool. Is `{}` installed?{}", |
|
|
|
program, |
|
|
|
extra |
|
|
|
), |
|
|
|
)) |
|
|
|
} |
|
|
|
Err(_) => Err(Error::new( |
|
|
|
ErrorKind::ToolExecError, |
|
|
|
&format!( |
|
|
|
"Command {:?} with args {:?} failed to start.", |
|
|
|
cmd, |
|
|
|
program |
|
|
|
), |
|
|
|
)), |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|