use std::{
collections::HashSet,
ffi::OsStr,
path::{Path, PathBuf},
};
use crate::{
arg_parser::{CompileMode, CompilerArgsInfo},
config::rllvm_config,
error::Error,
utils::{embed_bitcode_filepath_to_object_file, execute_command_for_status},
};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum CompilerKind {
#[default]
Clang,
ClangXX,
}
pub trait CompilerWrapper {
fn name(&self) -> &str;
fn wrapped_compiler(&self) -> &Path;
fn compiler_kind(&self) -> &CompilerKind;
#[must_use]
fn parse_args<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
where
S: AsRef<str>;
fn args(&self) -> &CompilerArgsInfo;
fn command(&self) -> Result<Vec<String>, Error> {
let args_info = self.args();
let compiler_filepath = self.wrapped_compiler();
let mut args = vec![String::from(compiler_filepath.to_string_lossy())];
if args_info.input_files().is_empty() && args_info.link_args().len() > 0 {
if args_info.is_lto() {
if let Some(lto_ldflags) = rllvm_config().lto_ldflags() {
args.extend(lto_ldflags.iter().cloned());
}
}
}
args.extend(args_info.input_args().iter().cloned());
if args_info.forbidden_flags().len() > 0 {
let forbidden_flags_set: HashSet<String> =
HashSet::from_iter(args_info.forbidden_flags().iter().cloned());
args = args
.into_iter()
.filter(|x| !forbidden_flags_set.contains(x))
.collect();
}
Ok(args)
}
fn silence(&mut self, value: bool) -> &'_ mut Self;
fn is_silent(&self) -> bool;
fn run(&mut self) -> Result<Option<i32>, Error> {
if let Some(code) = self.build_target()? {
if code != 0 {
return Ok(Some(code));
}
}
if self.args().is_bitcode_generation_skipped() {
return Ok(Some(0));
}
self.generate_bitcode_files_and_embed_filepaths()
}
fn execute_command<S>(&self, args: &[S], mode: CompileMode) -> Result<Option<i32>, Error>
where
S: AsRef<OsStr> + std::fmt::Debug,
{
if !self.is_silent() {
log::debug!("[{:?}] args={:?}", mode, args);
}
if args.is_empty() {
return Err(Error::InvalidArguments(
"The number of arguments cannot be 0".into(),
));
}
let status = execute_command_for_status(args[0].as_ref(), &args[1..])?;
if !self.is_silent() {
log::debug!("[{:?}] exit_status={}", mode, status);
}
if !status.success() {
return Err(Error::ExecutionFailure(format!(
"Failed to execute the command: args={:?}, exit_status={}",
args, status
)));
}
Ok(status.code())
}
fn build_target(&self) -> Result<Option<i32>, Error> {
let args = self.command()?;
let mode = self.args().mode();
self.execute_command(&args, mode)
}
fn generate_bitcode_files_and_embed_filepaths(&self) -> Result<Option<i32>, Error> {
let is_compile_only = self.args().is_compile_only();
let artifact_filepaths = self.args().artifact_filepaths()?;
let mut object_filepaths = vec![];
for (src_filepath, object_filepath, bitcode_filepath) in artifact_filepaths {
if !is_compile_only {
self.build_object_file(&src_filepath, &object_filepath)?;
object_filepaths.push(object_filepath.clone());
}
let src_bitcode_filepath = if src_filepath.extension().map_or(false, |x| x == "bc") {
src_filepath
} else {
if let Some(code) = self.generate_bitcode_file(&src_filepath, &bitcode_filepath)? {
if code != 0 {
return Ok(Some(code));
}
}
bitcode_filepath
};
embed_bitcode_filepath_to_object_file(&src_bitcode_filepath, &object_filepath, None)?;
}
let output_filepath = PathBuf::from(self.args().output_filename()).canonicalize()?;
self.link_object_files(&object_filepaths, output_filepath)
}
fn generate_bitcode_file<P>(
&self,
src_filepath: P,
bitcode_filepath: P,
) -> Result<Option<i32>, Error>
where
P: AsRef<Path>,
{
let src_filepath = src_filepath.as_ref();
let bitcode_filepath = bitcode_filepath.as_ref();
let compiler_filepath = self.wrapped_compiler();
let mut args = vec![String::from(compiler_filepath.to_string_lossy())];
args.extend(self.args().compile_args().iter().cloned());
if let Some(bitcode_generation_flags) = rllvm_config().bitcode_generation_flags() {
args.extend(bitcode_generation_flags.iter().cloned());
}
args.extend_from_slice(&[
"-emit-llvm".to_string(),
"-c".to_string(),
"-o".to_string(),
String::from(bitcode_filepath.to_string_lossy()),
String::from(src_filepath.to_string_lossy()),
]);
let mode = CompileMode::BitcodeGeneration;
self.execute_command(&args, mode)
}
fn build_object_file<P>(
&self,
src_filepath: P,
object_filepath: P,
) -> Result<Option<i32>, Error>
where
P: AsRef<Path>,
{
let src_filepath = src_filepath.as_ref();
let object_filepath = object_filepath.as_ref();
let wrapped_compiler = self.wrapped_compiler();
let mut args = vec![String::from(wrapped_compiler.to_string_lossy())];
args.extend(self.args().compile_args().iter().cloned());
args.extend_from_slice(&[
"-c".to_string(),
"-o".to_string(),
String::from(object_filepath.to_string_lossy()),
String::from(src_filepath.to_string_lossy()),
]);
let mode = CompileMode::Compiling;
self.execute_command(&args, mode)
}
fn link_object_files<P>(
&self,
object_filepaths: &[P],
output_filepath: P,
) -> Result<Option<i32>, Error>
where
P: AsRef<Path>,
{
let output_filepath = output_filepath.as_ref();
let wrapped_compiler = self.wrapped_compiler();
let mut args = vec![String::from(wrapped_compiler.to_string_lossy())];
if self.args().is_lto() {
if let Some(lto_ldflags) = rllvm_config().lto_ldflags() {
args.extend(lto_ldflags.iter().cloned());
}
}
args.extend(self.args().link_args().iter().cloned());
args.extend_from_slice(&[
"-o".to_string(),
String::from(output_filepath.to_string_lossy()),
]);
args.extend(
object_filepaths
.iter()
.map(|x| String::from(x.as_ref().to_string_lossy())),
);
let mode = CompileMode::Linking;
self.execute_command(&args, mode)
}
}
pub trait CompilerWrapperBuilder {
type OutputType;
fn build(&self) -> Self::OutputType;
#[must_use]
fn name(self, name: &str) -> Self;
#[must_use]
fn compiler_kind(self, compiler_kind: CompilerKind) -> Self;
fn wrapped_compiler<P>(self, wrapped_compiler: P) -> Self
where
P: AsRef<Path>;
fn silence(self, value: bool) -> Self;
}