use std::env;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::path::Path;
use std::path::PathBuf;
use anyhow::anyhow;
use anyhow::Context as _;
use anyhow::Result;
use tempfile::tempdir;
use tempfile::TempDir;
mod build;
mod r#gen;
mod make;
mod metadata;
pub mod util;
#[cfg(test)]
mod test;
use build::BpfObjBuilder;
#[derive(Debug)]
pub struct SkeletonBuilder {
source: Option<PathBuf>,
obj: Option<PathBuf>,
clang: Option<PathBuf>,
clang_args: Vec<OsString>,
rustfmt: PathBuf,
dir: Option<TempDir>,
reference_obj: bool,
original_obj_name: Option<String>,
}
impl Default for SkeletonBuilder {
fn default() -> Self {
Self::new()
}
}
impl SkeletonBuilder {
pub fn new() -> Self {
Self {
source: None,
obj: None,
clang: None,
clang_args: Vec::new(),
rustfmt: "rustfmt".into(),
dir: None,
reference_obj: false,
original_obj_name: None,
}
}
pub fn source<P: AsRef<Path>>(&mut self, source: P) -> &mut Self {
self.source = Some(source.as_ref().to_path_buf());
self
}
pub fn obj<P: AsRef<Path>>(&mut self, obj: P) -> &mut Self {
self.obj = Some(obj.as_ref().to_path_buf());
self
}
pub fn clang<P: AsRef<Path>>(&mut self, clang: P) -> &mut Self {
self.clang = Some(clang.as_ref().to_path_buf());
self
}
pub fn clang_args<A, S>(&mut self, args: A) -> &mut Self
where
A: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
self.clang_args = args
.into_iter()
.map(|arg| arg.as_ref().to_os_string())
.collect();
self
}
pub fn rustfmt<P: AsRef<Path>>(&mut self, rustfmt: P) -> &mut Self {
self.rustfmt = rustfmt.as_ref().to_path_buf();
self
}
pub fn reference_obj(&mut self, reference: bool) -> &mut Self {
self.reference_obj = reference;
self
}
pub fn build_and_generate<P: AsRef<Path>>(&mut self, output: P) -> Result<()> {
self.build()?;
self.generate(output)?;
Ok(())
}
pub fn build(&mut self) -> Result<()> {
let source = self
.source
.as_ref()
.ok_or_else(|| anyhow!("No source file provided"))?;
let filename = source
.file_name()
.ok_or_else(|| anyhow!("Missing file name"))?
.to_str()
.ok_or_else(|| anyhow!("Invalid unicode in file name"))?;
if !filename.ends_with(".bpf.c") {
return Err(anyhow!(
"Source `{}` does not have .bpf.c suffix",
source.display()
));
}
if self.obj.is_none() {
let name = filename.split('.').next().unwrap();
if self.reference_obj {
let out_dir = env::var("OUT_DIR")
.context("reference_obj requires OUT_DIR or an explicit obj path")?;
let hash = {
use std::collections::hash_map::DefaultHasher;
use std::hash::Hash;
use std::hash::Hasher;
let mut h = DefaultHasher::new();
source.hash(&mut h);
h.finish()
};
let objfile = PathBuf::from(out_dir).join(format!("{name}_{hash:016x}.o"));
self.obj = Some(objfile);
self.original_obj_name = Some(name.to_owned());
} else {
let dir = tempdir().context("failed to create temporary directory")?;
let objfile = dir.path().join(format!("{name}.o"));
self.obj = Some(objfile);
self.dir = Some(dir);
}
}
let mut builder = BpfObjBuilder::default();
if let Some(clang) = &self.clang {
builder.compiler(clang);
}
builder.compiler_args(&self.clang_args);
builder
.build(source, self.obj.as_ref().unwrap())
.with_context(|| format!("failed to build `{}`", source.display()))
}
pub fn generate<P: AsRef<Path>>(&mut self, output: P) -> Result<()> {
let objfile = self.obj.as_ref().ok_or_else(|| anyhow!("No object file"))?;
r#gen::gen_single(
objfile,
r#gen::OutputDest::File(output.as_ref()),
Some(&self.rustfmt),
self.reference_obj,
self.original_obj_name.as_deref(),
)
.with_context(|| format!("failed to generate `{}`", objfile.display()))?;
Ok(())
}
}
#[doc(hidden)]
pub mod __private {
pub mod build {
pub use crate::build::build_project;
}
pub mod r#gen {
pub use crate::r#gen::generate;
}
pub mod make {
pub use crate::make::make;
}
}