#![deny(rustdoc::broken_intra_doc_links)]
#![deny(clippy::all)]
#![deny(clippy::pedantic)]
#![allow(
clippy::unreadable_literal,
clippy::type_repetition_in_bounds,
clippy::missing_errors_doc,
clippy::cast_possible_truncation,
clippy::used_underscore_binding,
clippy::ptr_as_ptr,
clippy::missing_panics_doc,
clippy::missing_docs_in_private_items,
clippy::module_name_repetitions,
clippy::unreadable_literal
)]
#![cfg_attr(not(test), warn(
missing_debug_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
))]
#![cfg_attr(test, deny(
missing_debug_implementations,
missing_docs,
trivial_numeric_casts,
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_must_use,
missing_docs,
))]
#![cfg_attr(
test,
deny(
bad_style,
dead_code,
improper_ctypes,
non_shorthand_field_patterns,
no_mangle_generic_items,
overflowing_literals,
path_statements,
patterns_in_fns_without_body,
unconditional_recursion,
unused,
unused_allocation,
unused_comparisons,
unused_parens,
while_true
)
)]
use std::{path::Path, process::Command};
pub mod ar;
pub use ar::ArWrapper;
pub mod cfg;
pub use cfg::{CfgEdge, ControlFlowGraph, EntryBasicBlockInfo, HasWeight};
pub mod clang;
pub use clang::{ClangWrapper, LLVMPasses};
pub mod libtool;
pub use libtool::LibtoolWrapper;
#[derive(Debug)]
pub enum Error {
InvalidArguments(String),
Io(std::io::Error),
Unknown(String),
}
#[derive(Debug, Clone)]
pub enum Configuration {
Default,
AddressSanitizer,
UndefinedBehaviorSanitizer,
GenerateCoverageMap,
GenerateCoverageProfile,
CmpLog,
Compound(Vec<Self>),
}
impl Configuration {
pub fn to_flags(&self) -> Result<Vec<String>, Error> {
Ok(match self {
Configuration::Default => vec![],
#[cfg(all(
any(target_os = "linux", target_os = "android"),
target_arch = "aarch64"
))]
Configuration::AddressSanitizer => vec!["-fsanitize=hwaddress".to_string()],
#[cfg(not(all(
any(target_os = "linux", target_os = "android"),
target_arch = "aarch64"
)))]
Configuration::AddressSanitizer => vec!["-fsanitize=address".to_string()],
Configuration::UndefinedBehaviorSanitizer => vec!["-fsanitize=undefined".to_string()],
Configuration::GenerateCoverageMap => {
vec!["-fsanitize-coverage=trace-pc-guard".to_string()]
}
Configuration::CmpLog => vec!["-fsanitize-coverage=trace-cmp".to_string()],
Configuration::GenerateCoverageProfile => {
vec![
"-fprofile-instr-generate".to_string(),
"-fcoverage-mapping".to_string(),
]
}
Configuration::Compound(configurations) => {
let mut result: Vec<String> = vec![];
for configuration in configurations {
result.extend(configuration.to_flags()?);
}
result
}
})
}
#[must_use]
pub fn replace_extension(&self, path: &Path) -> std::path::PathBuf {
let mut parent = if let Some(parent) = path.parent() {
parent.to_path_buf()
} else {
std::path::PathBuf::from("")
};
let output = path.file_name().unwrap();
let output = output.to_str().unwrap();
let new_filename = if let Some((filename, extension)) = output.split_once('.') {
if let Configuration::Default = self {
format!("{filename}.{extension}")
} else {
format!("{filename}.{self}.{extension}")
}
} else if let Configuration::Default = self {
output.to_string()
} else {
format!("{output}.{self}")
};
parent.push(new_filename);
parent
}
}
impl std::str::FromStr for Configuration {
type Err = ();
fn from_str(input: &str) -> Result<Configuration, Self::Err> {
Ok(match input {
"asan" => Configuration::AddressSanitizer,
"ubsan" => Configuration::UndefinedBehaviorSanitizer,
"coverage" => Configuration::GenerateCoverageMap,
"llvm-cov" => Configuration::GenerateCoverageProfile,
"cmplog" => Configuration::CmpLog,
_ => Configuration::Default,
})
}
}
impl std::fmt::Display for Configuration {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Configuration::Default => write!(f, ""),
Configuration::AddressSanitizer => write!(f, "asan"),
Configuration::UndefinedBehaviorSanitizer => write!(f, "ubsan"),
Configuration::GenerateCoverageMap => write!(f, "coverage"),
Configuration::GenerateCoverageProfile => write!(f, "llvm-cov"),
Configuration::CmpLog => write!(f, "cmplog"),
Configuration::Compound(configurations) => {
let mut result: Vec<String> = vec![];
for configuration in configurations {
result.push(format!("{configuration}"));
}
write!(f, "{}", result.join("_"))
}
}
}
}
#[cfg(windows)]
pub const LIB_EXT: &str = "lib";
#[cfg(not(windows))]
pub const LIB_EXT: &str = "a";
#[cfg(windows)]
pub const LIB_PREFIX: &str = "";
#[cfg(not(windows))]
pub const LIB_PREFIX: &str = "lib";
pub trait ToolWrapper {
fn parse_args<S>(&mut self, args: &[S]) -> Result<&'_ mut Self, Error>
where
S: AsRef<str>;
fn add_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>;
fn add_args<S>(&mut self, args: &[S]) -> &'_ mut Self
where
S: AsRef<str>,
{
for arg in args {
self.add_arg(arg);
}
self
}
fn add_configuration(&mut self, configuration: Configuration) -> &'_ mut Self;
fn command(&mut self) -> Result<Vec<String>, Error>;
#[allow(clippy::too_many_lines)]
fn command_for_configuration(
&mut self,
configuration: Configuration,
) -> Result<Vec<String>, Error>;
fn configurations(&self) -> Result<Vec<Configuration>, Error>;
fn ignore_configurations(&self) -> Result<bool, Error>;
fn is_linking(&self) -> bool;
fn filter(&self, _args: &mut Vec<String>) {}
fn silence(&mut self, value: bool) -> &'_ mut Self;
fn is_silent(&self) -> bool;
fn run(&mut self) -> Result<Option<i32>, Error> {
let mut last_status = Ok(None);
let configurations = if self.ignore_configurations()? {
vec![Configuration::Default]
} else {
self.configurations()?
};
for configuration in configurations {
let mut args = self.command_for_configuration(configuration)?;
self.filter(&mut args);
if !self.is_silent() {
dbg!(args.clone());
}
if args.is_empty() {
last_status = Err(Error::InvalidArguments(
"The number of arguments cannot be 0".into(),
));
continue;
}
let status = match Command::new(&args[0]).args(&args[1..]).status() {
Ok(s) => s,
Err(e) => {
last_status = Err(Error::Io(e));
continue;
}
};
if !self.is_silent() {
dbg!(status);
}
last_status = Ok(status.code());
}
last_status
}
}
pub trait CompilerWrapper: ToolWrapper {
fn add_cc_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>;
fn add_link_arg<S>(&mut self, arg: S) -> &'_ mut Self
where
S: AsRef<str>;
fn add_cc_args<S>(&mut self, args: &[S]) -> &'_ mut Self
where
S: AsRef<str>,
{
for arg in args {
self.add_cc_arg(arg);
}
self
}
fn add_link_args<S>(&mut self, args: &[S]) -> &'_ mut Self
where
S: AsRef<str>,
{
for arg in args {
self.add_link_arg(arg);
}
self
}
fn link_staticlib<S>(&mut self, dir: &Path, name: S) -> &'_ mut Self
where
S: AsRef<str>;
}