use std::env;
use std::fs;
use std::path::{Path, PathBuf};
fn main() {
println!("cargo:rerun-if-changed=vendor/b4");
if !generate_embedded_assets() {
println!("cargo:warning=vendor/b4 not found, embedded b4 fallback disabled");
}
}
fn generate_embedded_assets() -> bool {
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR must be available"));
let manifest_path = out_dir.join("b4_vendor_assets.rs");
let vendor_root = Path::new("vendor/b4");
if !vendor_root.exists() {
write_empty_asset_manifest(&manifest_path).expect("failed to write empty b4 asset list");
return false;
}
let mut files = Vec::new();
for path in [
PathBuf::from("vendor/b4/b4.sh"),
PathBuf::from("vendor/b4/COPYING"),
PathBuf::from("vendor/b4/patatt/COPYING"),
] {
if path.exists() {
files.push(path);
}
}
collect_runtime_assets(Path::new("vendor/b4/src/b4"), &mut files)
.expect("failed to scan vendor/b4/src/b4");
collect_runtime_assets(Path::new("vendor/b4/patatt/patatt"), &mut files)
.expect("failed to scan vendor/b4/patatt/patatt");
files.sort();
let mut manifest =
String::from("// Auto-generated by build.rs.\nconst ASSETS: &[Asset] = &[\n");
for path in files {
let relative = path
.strip_prefix(vendor_root)
.expect("asset path must stay under vendor/b4");
let relative = relative.to_string_lossy().replace('\\', "/");
let absolute = path
.canonicalize()
.expect("failed to canonicalize embedded b4 asset path");
let executable = relative == "b4.sh";
manifest.push_str(&format!(
" Asset {{ relative_path: {:?}, contents: include_bytes!({:?}), executable: {} }},\n",
relative,
absolute.display().to_string(),
executable
));
}
manifest.push_str("];\n");
fs::write(&manifest_path, manifest).expect("failed to write b4 asset manifest");
true
}
fn write_empty_asset_manifest(path: &Path) -> std::io::Result<()> {
fs::write(path, "const ASSETS: &[Asset] = &[];\n")
}
fn collect_runtime_assets(root: &Path, out: &mut Vec<PathBuf>) -> std::io::Result<()> {
if !root.exists() {
return Ok(());
}
for entry in fs::read_dir(root)? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
if path.file_name().is_some_and(|name| name == "__pycache__") {
continue;
}
collect_runtime_assets(&path, out)?;
continue;
}
out.push(path);
}
Ok(())
}