#![allow(
clippy::expect_used,
clippy::panic,
clippy::print_stderr,
clippy::unwrap_used,
missing_docs
)]
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
const GITHUB_REPO: &str = "qntx/bux";
fn main() {
println!("cargo:rerun-if-env-changed=BUX_E2FS_DIR");
println!("cargo:rerun-if-env-changed=BUX_E2FS_VERSION");
println!("cargo:rerun-if-env-changed=BUX_UPDATE_BINDINGS");
println!("cargo:rerun-if-env-changed=DOCS_RS");
if env::var("DOCS_RS").is_ok() {
return;
}
let target = env::var("TARGET").expect("TARGET not set");
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR not set"));
#[cfg(feature = "regenerate")]
{
let headers_dir = obtain_headers(&target, &out_dir);
generate_bindings(&headers_dir, &out_dir);
}
if !is_supported_target(&target) {
return;
}
let lib_dir = obtain_libraries(&target, &out_dir);
println!("cargo:rustc-link-search=native={}", lib_dir.display());
println!("cargo:rustc-link-lib=static=ext2fs");
println!("cargo:rustc-link-lib=static=com_err");
println!("cargo:rustc-link-lib=static=e2p");
println!("cargo:rustc-link-lib=static=uuid_e2fs");
println!("cargo:rustc-link-lib=static=create_inode");
println!("cargo:LIB_DIR={}", lib_dir.display());
}
#[cfg(feature = "regenerate")]
fn obtain_headers(target: &str, out_dir: &Path) -> PathBuf {
if let Ok(dir) = env::var("BUX_E2FS_DIR") {
let inc = PathBuf::from(&dir).join("include");
if inc.exists() {
return inc;
}
eprintln!(
"bux-e2fs: BUX_E2FS_DIR set but {}/include not found, downloading",
dir
);
}
let lib_dir = obtain_libraries(target, out_dir);
let inc = lib_dir.parent().unwrap().join("include");
if inc.exists() {
return inc;
}
panic!(
"bux-e2fs: headers not found. Set BUX_E2FS_DIR or ensure the \
release archive contains an include/ directory."
);
}
#[cfg(feature = "regenerate")]
fn generate_bindings(headers_dir: &Path, out_dir: &Path) {
let wrapper = out_dir.join("wrapper.h");
fs::write(
&wrapper,
"#include \"ext2fs/ext2fs.h\"\n#include \"create_inode.h\"\n",
)
.expect("Failed to write wrapper.h");
let out_file = out_dir.join("bindings.rs");
let bindings = bindgen::Builder::default()
.header(wrapper.to_str().expect("path is not valid UTF-8"))
.clang_arg(format!("-I{}", headers_dir.display()))
.use_core()
.allowlist_function("ext2fs_initialize")
.allowlist_function("ext2fs_open")
.allowlist_function("ext2fs_close")
.allowlist_function("ext2fs_flush")
.allowlist_function("ext2fs_allocate_tables")
.allowlist_function("ext2fs_add_journal_inode")
.allowlist_function("ext2fs_mark_super_dirty")
.allowlist_function("ext2fs_mkdir")
.allowlist_function("ext2fs_link")
.allowlist_function("ext2fs_new_inode")
.allowlist_function("ext2fs_write_new_inode")
.allowlist_function("ext2fs_read_inode")
.allowlist_function("ext2fs_write_inode")
.allowlist_function("ext2fs_read_inode_full")
.allowlist_function("ext2fs_write_inode_full")
.allowlist_function("ext2fs_inode_alloc_stats2")
.allowlist_function("ext2fs_new_block2")
.allowlist_function("ext2fs_block_alloc_stats2")
.allowlist_function("populate_fs")
.allowlist_function("populate_fs2")
.allowlist_function("populate_fs3")
.allowlist_function("do_write_internal")
.allowlist_function("do_mkdir_internal")
.allowlist_function("do_symlink_internal")
.allowlist_function("set_inode_extra")
.allowlist_function("add_link")
.allowlist_var("unix_io_manager")
.allowlist_type("ext2_filsys")
.allowlist_type("ext2_ino_t")
.allowlist_type("ext2_super_block")
.allowlist_type("ext2_inode")
.allowlist_type("ext2_inode_large")
.allowlist_type("errcode_t")
.allowlist_type("io_manager")
.allowlist_type("hdlinks_s")
.allowlist_type("hdlink_s")
.allowlist_type("fs_ops_callbacks")
.allowlist_var("EXT2_FLAG_.*")
.allowlist_var("EXT2_ROOT_INO")
.allowlist_var("EXT2_DYNAMIC_REV")
.allowlist_var("EXT2_GOOD_OLD_.*")
.allowlist_var("EXT2_FT_.*")
.allowlist_var("POPULATE_FS_.*")
.allowlist_var("LINUX_S_IF.*")
.derive_debug(true)
.derive_default(true)
.derive_eq(true)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate()
.expect("bindgen failed to generate bindings from e2fsprogs headers");
bindings
.write_to_file(&out_file)
.expect("Failed to write bindings.rs");
if env::var("BUX_UPDATE_BINDINGS").is_ok() {
let manifest =
PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
let committed = manifest.join("src").join("bindings.rs");
fs::copy(&out_file, &committed).expect("Failed to copy bindings.rs to src/");
println!(
"cargo:warning=Updated committed bindings: {}",
committed.display()
);
}
}
fn obtain_libraries(target: &str, out_dir: &Path) -> PathBuf {
if let Ok(dir) = env::var("BUX_E2FS_DIR") {
let lib = PathBuf::from(&dir).join("lib");
if lib.exists() {
eprintln!("bux-e2fs: using local libs: {}", lib.display());
return lib;
}
let direct = PathBuf::from(&dir);
if direct.join("libext2fs.a").exists() {
eprintln!("bux-e2fs: using local libs: {}", direct.display());
return direct;
}
eprintln!("bux-e2fs: BUX_E2FS_DIR set but libs not found, downloading");
}
let version = env::var("BUX_E2FS_VERSION")
.unwrap_or_else(|_| env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set"));
let lib_dir = out_dir.join("e2fs").join("lib");
if !lib_dir.join("libext2fs.a").exists() {
download_libs(&version, target, out_dir);
}
lib_dir
}
fn is_supported_target(target: &str) -> bool {
let linux =
target.contains("linux") && (target.contains("x86_64") || target.contains("aarch64"));
let macos = target.contains("apple") && target.contains("aarch64");
linux || macos
}
fn download_libs(version: &str, target: &str, out_dir: &Path) {
let url = format!(
"https://github.com/{GITHUB_REPO}/releases/download/e2fs-v{version}/bux-e2fs-{target}.tar.gz"
);
eprintln!("bux-e2fs: downloading {url}");
let dest = out_dir.join("e2fs");
fs::create_dir_all(&dest).expect("Failed to create e2fs dir");
let resp = ureq::get(&url)
.call()
.unwrap_or_else(|e| panic!("Failed to download e2fs deps: {e}"));
tar::Archive::new(flate2::read::GzDecoder::new(resp.into_body().into_reader()))
.unpack(&dest)
.expect("Failed to extract e2fs archive");
assert!(
dest.join("lib").join("libext2fs.a").exists(),
"libext2fs.a not found after extraction. Check GitHub Release e2fs-v{version}."
);
}