in src/main.rs [312:596]
fn main() {
let opt = Opt::parse();
if let Some(path) = opt.llvm_path {
LLVM_PATH.set(path).unwrap();
}
let filter_option = opt.filter.map(|filter| match filter {
Filter::Covered => true,
Filter::Uncovered => false,
});
let stdout = Path::new("stdout");
let stderr = Path::new("stderr");
if opt.log == stdout {
let _ = TermLogger::init(
opt.log_level.0,
Config::default(),
TerminalMode::Stdout,
ColorChoice::Auto,
);
} else if opt.log == stderr {
let _ = TermLogger::init(
opt.log_level.0,
Config::default(),
TerminalMode::Stderr,
ColorChoice::Auto,
);
} else if let Ok(file) = File::create(&opt.log) {
let _ = WriteLogger::init(opt.log_level.0, Config::default(), file);
} else {
let _ = TermLogger::init(
opt.log_level.0,
Config::default(),
TerminalMode::Stderr,
ColorChoice::Auto,
);
error!(
"Unable to create log file: {}. Switch to stderr",
opt.log.display()
);
}
let file_filter = FileFilter::new(
opt.excl_line,
opt.excl_start,
opt.excl_stop,
opt.excl_br_line,
opt.excl_br_start,
opt.excl_br_stop,
);
let demangle = !opt.no_demangle;
panic::set_hook(Box::new(|panic_info| {
let (filename, line) = panic_info
.location()
.map(|loc| (loc.file(), loc.line()))
.unwrap_or(("<unknown>", 0));
let cause = panic_info
.payload()
.downcast_ref::<String>()
.map(String::deref);
let cause = cause.unwrap_or_else(|| {
panic_info
.payload()
.downcast_ref::<&str>()
.copied()
.unwrap_or("<cause unknown>")
});
error!("A panic occurred at {}:{}: {}", filename, line, cause);
}));
let num_threads: usize = opt.threads.unwrap_or_else(|| 1.max(num_cpus::get() - 1));
let source_root = opt
.source_dir
.filter(|source_dir| source_dir != Path::new(""))
.map(|source_dir| canonicalize_path(source_dir).expect("Source directory does not exist."));
let prefix_dir = opt.prefix_dir.or_else(|| source_root.clone());
let tmp_dir = tempfile::tempdir().expect("Failed to create temporary directory");
let tmp_path = tmp_dir.path().to_owned();
assert!(tmp_path.exists());
let result_map: Arc<SyncCovResultMap> = Arc::new(Mutex::new(
FxHashMap::with_capacity_and_hasher(20_000, Default::default()),
));
let (sender, receiver) = bounded(2 * num_threads);
let path_mapping: Arc<Mutex<Option<Value>>> = Arc::new(Mutex::new(None));
let producer = {
let sender: JobSender = sender.clone();
let tmp_path = tmp_path.clone();
let path_mapping_file = opt.path_mapping;
let path_mapping = Arc::clone(&path_mapping);
let paths = opt.paths;
let is_llvm = opt.llvm;
thread::Builder::new()
.name(String::from("Producer"))
.spawn(move || {
let producer_path_mapping_buf = producer(
&tmp_path,
&paths,
&sender,
filter_option.is_some() && filter_option.unwrap(),
is_llvm,
);
let mut path_mapping = path_mapping.lock().unwrap();
*path_mapping = if let Some(path) = path_mapping_file {
let file = File::open(path).unwrap();
Some(serde_json::from_reader(file).unwrap())
} else {
producer_path_mapping_buf.map(|producer_path_mapping_buf| {
serde_json::from_slice(&producer_path_mapping_buf).unwrap()
})
};
})
.unwrap()
};
let mut parsers = Vec::new();
for i in 0..num_threads {
let receiver = receiver.clone();
let result_map = Arc::clone(&result_map);
let working_dir = tmp_path.join(format!("{}", i));
let source_root = source_root.clone();
let binary_path = opt.binary_path.clone();
let branch_enabled = opt.branch;
let guess_directory = opt.guess_directory;
let t = thread::Builder::new()
.name(format!("Consumer {}", i))
.spawn(move || {
fs::create_dir(&working_dir).expect("Failed to create working directory");
consumer(
&working_dir,
source_root.as_deref(),
&result_map,
receiver,
branch_enabled,
guess_directory,
binary_path.as_deref(),
);
})
.unwrap();
parsers.push(t);
}
if producer.join().is_err() {
process::exit(1);
}
// Poison the receiver, now that the producer is finished.
for _ in 0..num_threads {
sender.send(None).unwrap();
}
for parser in parsers {
if parser.join().is_err() {
process::exit(1);
}
}
let result_map_mutex = Arc::try_unwrap(result_map).unwrap();
let result_map = result_map_mutex.into_inner().unwrap();
let path_mapping_mutex = Arc::try_unwrap(path_mapping).unwrap();
let path_mapping = path_mapping_mutex.into_inner().unwrap();
let iterator = rewrite_paths(
result_map,
path_mapping,
source_root.as_deref(),
prefix_dir.as_deref(),
opt.ignore_not_existing,
&opt.ignore_dir,
&opt.keep_dir,
filter_option,
file_filter,
);
let mut sorted_iterator: Option<Vec<ResultTuple>> = None;
let service_number = opt.service_number.unwrap_or_default();
let service_pull_request = opt.service_pull_request.unwrap_or_default();
let commit_sha = opt.commit_sha.unwrap_or_default();
let output_types = opt.output_types;
let output_path = match output_types.len() {
0 => unreachable!("Output types has a default value"),
1 => opt.output_path.as_deref(),
_ => match opt.output_path.as_deref() {
Some(output_path) => {
if output_path.is_dir() {
Some(output_path)
} else {
panic!("output_path must be a directory when using multiple outputs");
}
}
_ => None,
},
};
for output_type in &output_types {
let output_path = output_type.to_file_name(output_path);
let results = if opt.sort_output_types.contains(output_type) {
// compute and cache the sorted results if not already used
sorted_iterator = sorted_iterator.or_else(|| {
let mut results = iterator.clone();
results.sort_by_key(|result| result.0.display().to_string());
Some(results)
});
sorted_iterator.as_ref().unwrap()
} else {
&iterator
};
match output_type {
OutputType::Ade => output_activedata_etl(results, output_path.as_deref(), demangle),
OutputType::Lcov => output_lcov(results, output_path.as_deref(), demangle),
OutputType::Coveralls => output_coveralls(
results,
opt.token.as_deref(),
opt.service_name.as_deref(),
&service_number,
opt.service_job_id.as_deref(),
&service_pull_request,
opt.service_flag_name.as_deref(),
&commit_sha,
false,
output_path.as_deref(),
&opt.vcs_branch,
opt.parallel,
demangle,
),
OutputType::CoverallsPlus => output_coveralls(
results,
opt.token.as_deref(),
opt.service_name.as_deref(),
&service_number,
opt.service_job_id.as_deref(),
&service_pull_request,
opt.service_flag_name.as_deref(),
&commit_sha,
true,
output_path.as_deref(),
&opt.vcs_branch,
opt.parallel,
demangle,
),
OutputType::Files => output_files(results, output_path.as_deref()),
OutputType::Covdir => output_covdir(results, output_path.as_deref(), opt.precision),
OutputType::Html => output_html(
results,
output_path.as_deref(),
num_threads,
opt.branch,
opt.output_config_file.as_deref(),
opt.precision,
&opt.abs_link_prefix.clone(),
opt.no_date,
opt.html_resources,
),
OutputType::Cobertura => output_cobertura(
source_root.as_deref(),
results,
output_path.as_deref(),
demangle,
false,
),
OutputType::CoberturaPretty => output_cobertura(
source_root.as_deref(),
results,
output_path.as_deref(),
demangle,
true,
),
OutputType::Markdown => output_markdown(results, output_path.as_deref(), opt.precision),
};
}
}