use argparse::is_rustc_compiling_local_crate;
use error::{Result, ResultExt};
use utils::{CommandExt, join_2, parent_3};
use fs2::FileExt;
use rand::{Rng, thread_rng};
use walkdir::WalkDir;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs::{File, rename};
use std::path::Path;
use std::process::Command;
pub fn rustc<'a, I: Iterator<Item = &'a OsStr> + Clone>(args: I) -> Result<()> {
let rustc_path = env::var_os("COV_RUSTC").expect("COV_RUSTC");
let cov_build_path_os = env::var_os("COV_BUILD_PATH").expect("COV_BUILD_PATH");
let cov_build_path = Path::new(&cov_build_path_os);
let workspace_path = parent_3(cov_build_path);
let is_local = is_rustc_compiling_local_crate(args.clone(), workspace_path);
let mut cmd = Command::new(rustc_path);
cmd.args(args);
if is_local {
let profiler_lib_path = env::var_os("COV_PROFILER_LIB_PATH").expect("COV_PROFILER_LIB_PATH");
let profiler_lib_name = env::var_os("COV_PROFILER_LIB_NAME").expect("COV_PROFILER_LIB_NAME");
debug!("Profiler: -L {:?} -l {:?}", profiler_lib_path, profiler_lib_name);
if profiler_lib_path == OsStr::new("@native") && profiler_lib_name == OsStr::new("@native") {
cmd.arg("-Zprofile");
} else {
cmd.arg("-Cpasses=insert-gcov-profiling").arg("-L").arg(profiler_lib_path).arg("-l").arg(profiler_lib_name);
}
cmd.args(&[
"-Clink-dead-code",
"-Coverflow-checks=off",
"-Cinline-threshold=0",
"-Ccodegen-units=1",
]);
}
debug!("Executing {:?}", cmd);
cmd.ensure_success("rustc")?;
if is_local {
move_gcov_files(cov_build_path, OsStr::new("gcno"))?;
}
Ok(())
}
pub fn rustdoc<'a, I: Iterator<Item = &'a OsStr>>(args: I) -> Result<()> {
let rustdoc_path = env::var_os("COV_RUSTDOC").expect("COV_RUSTDOC");
let cov_build_path_os = env::var_os("COV_BUILD_PATH").expect("COV_BUILD_PATH");
let cov_build_path = Path::new(&cov_build_path_os);
let mut cmd = Command::new(rustdoc_path);
let link_dir = env::var_os("COV_PROFILER_LIB_PATH").expect("COV_PROFILER_LIB_PATH");
if link_dir != OsStr::new("@native") {
cmd.arg("-L").arg(link_dir);
}
cmd.args(args);
debug!("Executing {:?}", cmd);
cmd.ensure_success("rustdoc")?;
move_gcov_files(cov_build_path, OsStr::new("gcda"))?;
Ok(())
}
pub fn run<'a, I: Iterator<Item = &'a OsStr>>(mut args: I) -> Result<()> {
let cov_build_path_os = env::var_os("COV_BUILD_PATH").expect("COV_BUILD_PATH");
let cov_build_path = Path::new(&cov_build_path_os);
let mut cmd = Command::new(args.next().expect("launcher"));
cmd.args(args);
debug!("Executing {:?}", cmd);
cmd.ensure_success("test")?;
move_gcov_files(cov_build_path, OsStr::new("gcda"))?;
Ok(())
}
pub fn move_gcov_files(cov_build_path: &Path, extension: &OsStr) -> Result<()> {
let mut rng = thread_rng();
let mut dest_path = join_2(cov_build_path, extension, "*");
let mut lock_file = LockFile::new(cov_build_path)?;
let it = WalkDir::new(cov_build_path).into_iter().filter_entry(|entry| {
let file_type = entry.file_type();
let path = entry.path();
if file_type.is_dir() && entry.depth() == 1 {
let file_name = path.file_name();
if file_name == Some(OsStr::new("gcda")) || file_name == Some(OsStr::new("gcno")) {
return false;
}
} else if file_type.is_file() && path.extension() != Some(extension) {
return false;
}
true
});
for entry in it {
let entry = entry?;
if !entry.file_type().is_file() {
continue;
}
let source_path = entry.path();
loop {
let mut filename = OsString::from(format!("{:016x}.", rng.gen::<u64>()));
filename.push(source_path.file_stem().unwrap_or_else(|| OsStr::new("?")));
filename.push(OsStr::new("."));
filename.push(extension);
dest_path.set_file_name(filename);
if !dest_path.exists() {
break;
}
}
trace!("mv {:?} {:?}", source_path, dest_path);
rename(source_path, &dest_path).chain_err(|| format!("cannot move `{}` to `{}`", source_path.display(), dest_path.display()))?;
}
lock_file.unlock()
}
struct LockFile(Option<File>);
impl LockFile {
fn new(cov_build_path: &Path) -> Result<LockFile> {
let lock_file = File::open(cov_build_path.join("rustc-shim.bat"))?;
lock_file.lock_exclusive()?;
Ok(LockFile(Some(lock_file)))
}
fn unlock(&mut self) -> Result<()> {
if let Some(lock_file) = self.0.take() {
lock_file.unlock()?;
}
Ok(())
}
}
impl Drop for LockFile {
fn drop(&mut self) {
let _ = self.unlock();
}
}