use super::build_script_directives::BuildScriptDirectives;
use super::types::{BuildMode, PlannedCompilationUnit};
use anyhow::{Context, Result};
use std::fs;
use std::path::{Path, PathBuf};
pub use super::compiler_resolve::*;
pub struct RustcInvocation<'a> {
pub unit: &'a PlannedCompilationUnit,
pub action_key: &'a str,
pub out_dir: &'a Path,
pub dest_binary: Option<&'a Path>,
pub extern_crates: &'a [(String, PathBuf)],
pub release: bool,
pub features: &'a [String],
pub directives: Option<&'a BuildScriptDirectives>,
pub mode: BuildMode,
pub extra_compiler_args: &'a [String],
pub jobserver_auth: Option<&'a str>,
pub deps_dir: Option<&'a Path>,
pub target_dir: Option<&'a Path>,
pub cache_root: Option<&'a Path>,
}
impl<'a> RustcInvocation<'a> {
pub fn execute_pipelined<F>(self, on_metadata: F) -> Result<RustcOutput>
where
F: FnOnce(&Path),
{
let (mut cmd, clean_crate, is_pipelined_lib) =
super::compiler_args::build_compiler_command(&self)?;
let command_line = format!("{:?}", cmd);
let (success, mut exit_code, stdout, stderr, mut primary_output, mut metadata_output) =
if is_pipelined_lib {
let res = super::pipelining::run_pipelined_rustc(cmd, on_metadata)?;
(
res.success,
res.exit_code,
res.stdout,
res.stderr,
res.primary_output,
res.metadata_output,
)
} else {
let output = cmd.output().with_context(|| {
format!("Failed to execute rustc for unit '{}'", self.unit.unit_id())
})?;
let code = output.status.code().unwrap_or(1);
let success = output.status.success();
let stdout = String::from_utf8_lossy(&output.stdout).to_string();
let stderr = String::from_utf8_lossy(&output.stderr).to_string();
(success, code, stdout, stderr, None, None)
};
if !success {
exit_code = 101;
}
if success {
let is_bin = self.unit.kind == "bin";
let is_test = self.unit.kind == "test" || self.unit.kind == "custom-test";
let is_check_or_clippy_ws = self.unit.is_workspace
&& (self.mode == BuildMode::Check || self.mode == BuildMode::Clippy);
if (is_bin || is_test) && !is_check_or_clippy_ws {
let bin_path = self.out_dir.join(&clean_crate);
if let Some(dest) = self.dest_binary {
if bin_path.is_file() {
let _ = dest.parent().map(fs::create_dir_all);
if fs::rename(&bin_path, dest).is_err() {
let mut roots = vec![self.out_dir];
if let Some(td) = self.target_dir {
roots.push(td);
}
let _ =
crate::core::link_intra_workspace(&bin_path, dest, &roots, true);
}
}
primary_output = Some(dest.to_path_buf());
} else if bin_path.is_file() {
primary_output = Some(bin_path);
}
}
if primary_output.is_none() || metadata_output.is_none() {
if let Ok(entries) = fs::read_dir(self.out_dir) {
for p in entries.flatten().map(|e| e.path()) {
match p.extension().and_then(|e| e.to_str()) {
Some("rlib" | "dylib" | "so") if primary_output.is_none() => {
primary_output = Some(p)
}
Some("rmeta") if metadata_output.is_none() => metadata_output = Some(p),
_ => {}
}
}
}
}
if primary_output.is_none()
&& (self.mode == BuildMode::Check || self.mode == BuildMode::Clippy)
{
primary_output = metadata_output.clone();
}
}
Ok(RustcOutput {
success,
exit_code,
stdout,
stderr,
command_line,
primary_output,
metadata_output,
})
}
pub fn execute(self) -> Result<RustcOutput> {
self.execute_pipelined(|_| {})
}
}