use std::{
ffi::OsStr,
fs,
path::{Path, PathBuf},
process::Command,
};
use super::{marker, rustc_args, rustc_marker};
use crate::{
compiler_wrapper::CompilerKind, config::try_rllvm_config, error::Error,
utils::embed_bitcode_filepath_to_object_file,
};
#[derive(Debug)]
pub struct RustcWrapper {
rustc_path: PathBuf,
is_silent: bool,
}
impl RustcWrapper {
pub fn new(rustc_path: PathBuf) -> Self {
Self {
rustc_path,
is_silent: false,
}
}
pub fn silence(&mut self, value: bool) -> &mut Self {
self.is_silent = value;
self
}
pub fn run<S>(&self, args: &[S]) -> Result<Option<i32>, Error>
where
S: AsRef<OsStr> + AsRef<str> + std::fmt::Debug,
{
let args: Vec<&str> = args.iter().map(|a| <S as AsRef<str>>::as_ref(a)).collect();
let Some(actions) = rustc_args::classify(&args) else {
return self.spawn(&args);
};
let store = try_rllvm_config()?.bitcode_store_path().cloned();
let bitcode = rustc_args::bitcode_path(&args, store.as_deref())?;
let owned: Vec<String> = args.iter().map(|arg| (*arg).to_string()).collect();
let mut rewritten = rustc_args::rewrite_emit(&owned, &bitcode);
let marker_dir = if actions.links {
Some(tempfile::tempdir()?)
} else {
None
};
if let Some(dir) = &marker_dir {
let clang = try_rllvm_config()?.clang_filepath();
let marker =
marker::build_marker_object(&bitcode, dir.path(), clang, CompilerKind::Clang, &[])?;
rewritten.push("-C".to_string());
rewritten.push(format!("link-arg={}", marker.display()));
}
if !self.is_silent {
tracing::debug!("rustc: bitcode={bitcode:?}, actions={actions:?}");
}
let code = self.spawn(&rewritten)?;
if code != Some(0) {
return Ok(code);
}
if actions.archives || actions.object {
self.embed_into_outputs(&args, &bitcode)?;
}
Ok(Some(0))
}
fn spawn<S>(&self, args: &[S]) -> Result<Option<i32>, Error>
where
S: AsRef<OsStr>,
{
let status = Command::new(&self.rustc_path).args(args).status()?;
Ok(status.code())
}
fn embed_into_outputs(&self, args: &[&str], bitcode: &Path) -> Result<(), Error> {
for artifact in self.output_artifacts(args)? {
if !artifact.exists() {
continue;
}
let data = fs::read(&artifact)?;
if object::read::archive::ArchiveFile::parse(&*data).is_ok() {
let patched = rustc_marker::patch_archive(&artifact, bitcode)?;
tracing::debug!("rustc: patched {patched} members of {artifact:?}");
} else if object::File::parse(&*data).is_ok() {
embed_bitcode_filepath_to_object_file::<&Path>(bitcode, &artifact, None)?;
tracing::debug!("rustc: embedded the bitcode path into {artifact:?}");
} else {
tracing::debug!("rustc: {artifact:?} is neither an archive nor an object");
}
}
Ok(())
}
fn output_artifacts(&self, args: &[&str]) -> Result<Vec<PathBuf>, Error> {
if let Some(output) = rustc_args::flag_value(args, "-o") {
return Ok(vec![PathBuf::from(output)]);
}
let output = Command::new(&self.rustc_path)
.args(args)
.arg("--print=file-names")
.output()?;
if !output.status.success() {
return Err(Error::ExecutionFailure(format!(
"rustc --print=file-names failed, so the output to record the bitcode path in is unknown: {}",
String::from_utf8_lossy(&output.stderr).trim()
)));
}
let out_dir = rustc_args::flag_value(args, "--out-dir").unwrap_or(".");
Ok(String::from_utf8_lossy(&output.stdout)
.lines()
.map(str::trim)
.filter(|name| !name.is_empty())
.map(|name| PathBuf::from(out_dir).join(name))
.collect())
}
}