use std::{
fs,
path::{Path, PathBuf},
process::Command,
};
use crate::{
arg_parser::without_dependency_flags,
compiler_wrapper::CompilerKind,
config::try_rllvm_config,
error::Error,
lto::{is_save_temps_artifact, is_saved_module, marker_source},
utils::{link_bitcode_files, recorded_bitcode_filepath},
};
pub(crate) fn inject_marker(
object: &Path,
bitcode: &Path,
compile_args: &[String],
compiler: &Path,
kind: CompilerKind,
) -> Result<(), Error> {
let workspace = tempfile::tempdir()?;
let extension = match kind {
CompilerKind::Clang => "c",
CompilerKind::ClangXX => "cpp",
};
let source = workspace.path().join(format!("rllvm_marker.{extension}"));
let marker = workspace.path().join("rllvm_marker.bc");
fs::write(&source, marker_source(&recorded_bitcode_filepath(bitcode)?))?;
let status = Command::new(compiler)
.args(without_dependency_flags(compile_args))
.args(["-emit-llvm", "-c", "-o"])
.arg(&marker)
.arg(&source)
.status()?;
if !status.success() {
return Err(Error::ExecutionFailure(format!(
"Failed to compile the LTO marker for {object:?}: exit_status={status}. \
On a target that is neither ELF nor Mach-O, set lto_mode = \"skip\"."
)));
}
let config = try_rllvm_config()?;
let status = Command::new(config.llvm_link_filepath())
.arg(object)
.arg(&marker)
.arg("-o")
.arg(object)
.status()?;
if !status.success() {
return Err(Error::ExecutionFailure(format!(
"Failed to merge the LTO marker into {object:?}: exit_status={status}"
)));
}
Ok(())
}
pub(crate) fn collect_saved_module(output: &Path, cleanup: bool) -> Result<PathBuf, Error> {
let darwin = cfg!(target_vendor = "apple");
let dir = match output.parent() {
Some(parent) if !parent.as_os_str().is_empty() => parent,
_ => Path::new("."),
};
let output_name = output.file_name().unwrap_or_default().to_string_lossy();
let destination = PathBuf::from(format!("{}.rllvm.bc", output.display()));
let mut saved = vec![];
let mut litter = vec![];
for entry in fs::read_dir(dir)? {
let entry = entry?;
let name = entry.file_name().to_string_lossy().into_owned();
if is_saved_module(&output_name, &name, darwin) {
saved.push(entry.path());
} else if cleanup && is_save_temps_artifact(&output_name, &name) {
litter.push(entry.path());
}
}
saved.sort();
match saved.len() {
0 => {
for path in litter {
let _ = fs::remove_file(path);
}
return Err(Error::MissingFile(format!(
"The LTO link produced no merged module for {output:?}. Expected {}. \
The link's inputs are probably not LTO bitcode: `-flto` in LDFLAGS alone \
is not enough if the objects were compiled without it. {output:?} now \
records {destination:?}, which does not exist, so `rllvm-get-bc` will \
fail on it.",
if darwin {
format!("{output_name}.lto.opt.bc")
} else {
format!("{output_name}.*.precodegen.bc")
}
)));
}
1 => fs::rename(&saved[0], &destination)?,
_ => {
let code = link_bitcode_files(&saved, destination.clone())?;
if code != Some(0) {
return Err(Error::ExecutionFailure(format!(
"Failed to merge {} save-temps partitions into {destination:?}: exit_status={code:?}",
saved.len()
)));
}
for module in &saved {
let _ = fs::remove_file(module);
}
}
}
for path in litter {
let _ = fs::remove_file(path);
}
tracing::info!("Collected the LTO merged module: {:?}", destination);
Ok(destination)
}