use std::{path::{Path, PathBuf}, fmt::{Debug}, error::Error, fs::create_dir_all};
use bindgen::callbacks::ParseCallbacks;
pub mod env_install;
fn main() -> Result<(), Box<dyn std::error::Error>> {
if std::env::var("DOCS_RS").is_ok() {
return empty_bindings();
}
let suite = find_suite()?;
add_linker_search_dir(&suite.lib_dir());
for lib in suite.lib_names() {
link_lib(&lib);
}
bindgen_for_suite(suite.as_ref())?;
Ok(())
}
fn empty_bindings() -> Result<(), Box<dyn Error>> {
let out_dir = PathBuf::from(std::env::var("OUT_DIR")?);
let bindings_module_path = out_dir.join("bindings.rs");
std::fs::write(&bindings_module_path, "")?;
Ok(())
}
pub trait FfmpegSuite: Debug {
fn include_dir(&self) -> PathBuf;
fn lib_dir(&self) -> PathBuf;
fn lib_names(&self) -> Vec<String>;
fn main_include_for_lib(&self, lib: &str) -> String;
}
fn find_suite() -> Result<Box<dyn FfmpegSuite>, Box<dyn std::error::Error>> {
let mut not_found_reasons: Vec<String> = Vec::new();
if let Ok(suite) = env_install::find() {
return Ok(suite);
} else if let Err(err) = env_install::find() {
not_found_reasons.push(format!("env_install not found: {}", err));
}
Err(format!("No FFMPEG suite found: \n{}", not_found_reasons.join("\n\n")).into())
}
fn add_linker_search_dir(dir: &Path) {
println!("cargo:rustc-link-search=native={}", dir.to_string_lossy());
}
fn link_lib(lib: &str) {
println!("cargo:rustc-link-lib=dylib={}", lib);
}
fn bindgen_for_suite(suite: &dyn FfmpegSuite) -> Result<(), Box<dyn Error>> {
let clang_arg = format!("-I{}", suite.include_dir().to_string_lossy());
let out_dir = PathBuf::from(std::env::var("OUT_DIR")?);
let bindings_outdir = out_dir.clone();
create_dir_all(&bindings_outdir)?;
let mut builder = bindgen::Builder::default()
.clang_arg(&clang_arg)
.parse_callbacks(Box::new(CommentProcessor));
for lib in suite.lib_names() {
let header = suite.include_dir()
.join(format!("lib{lib}"))
.join(suite.main_include_for_lib(&lib));
if !header.exists() {
return Err(format!("Could not find {header:?}").into());
}
builder = builder.header(header.to_string_lossy());
}
let bindings = builder
.generate()
.expect("Unable to generate bindings");
let bindings_module_path = out_dir.join("bindings.rs");
bindings
.write_to_file(bindings_module_path)
.expect("Couldn't write bindings!");
Ok(())
}
#[derive(Debug)]
struct CommentProcessor;
impl ParseCallbacks for CommentProcessor {
fn process_comment(&self, comment: &str) -> Option<String> {
Some(doxygen_rs::transform(comment))
}
}