vmrunner-sysroot 0.0.6-alpha.0

micro-vm runner sysroot extraction helpers
use std::{
    env,
    path::{Path, PathBuf},
};

/// Package-manager prefixes common enough to be worth an existence check when pkg-config's own
/// `Cflags`/`Libs` come up empty. Anything else is still reachable via `BINDGEN_EXTRA_CLANG_ARGS`
/// (read directly by bindgen) or `RUSTFLAGS=-L...` (read directly by rustc).
const FALLBACK_PREFIXES: &[&str] = &["/opt/local", "/opt/homebrew", "/usr/local"];

fn main() {
    println!("cargo:rerun-if-changed=libguestfs-wrapper.h");

    let libguestfs = pkg_config::Config::new().probe("libguestfs").expect(
        "libguestfs development files were not found; install libguestfs-devel or equivalent",
    );

    // `probe()` already emits the `cargo:rustc-link-search`/`cargo:rustc-link-lib` directives
    // Cargo's linker needs, but bindgen invokes clang independently of those and never sees
    // pkg-config's `-I` output unless it is passed through explicitly.
    let mut builder = bindgen::Builder::default().header("libguestfs-wrapper.h");
    for include_path in &libguestfs.include_paths {
        builder = builder.clang_arg(format!("-I{}", include_path.display()));
    }

    // Some packagings report a successful probe with an empty `Cflags`/`Libs` even though the
    // library is installed at a well-known, non-default prefix: MacPorts' own `libguestfs.pc`
    // ships with no `Cflags` line and a bare `-lguestfs` (no `-L`), so `include_paths` above is
    // empty and both bindgen ("'guestfs.h' file not found") and the final link
    // ("library 'guestfs' not found") fail despite `probe()` having already succeeded. Fall back
    // to the conventional package-manager prefixes so this is not something every MacPorts user
    // has to rediscover and work around by hand.
    if libguestfs.include_paths.is_empty() {
        if let Some(prefix) = FALLBACK_PREFIXES
            .iter()
            .find(|prefix| Path::new(prefix).join("include/guestfs.h").is_file())
        {
            builder = builder.clang_arg(format!("-I{prefix}/include"));
        }
    }
    if libguestfs.link_paths.is_empty() {
        if let Some(prefix) = FALLBACK_PREFIXES.iter().find(|prefix| {
            Path::new(prefix)
                .join("lib")
                .join("libguestfs.dylib")
                .is_file()
                || Path::new(prefix)
                    .join("lib")
                    .join("libguestfs.so")
                    .is_file()
        }) {
            println!("cargo:rustc-link-search=native={prefix}/lib");
        }
    }

    let bindings = builder
        .allowlist_type("guestfs_h")
        .allowlist_type("guestfs_add_drive_opts_argv")
        .allowlist_function(
            "guestfs_(create|close|set_error_handler|last_error|last_errno|add_drive_opts_argv|launch|inspect_os|inspect_get_mountpoints|mount_ro|mount_options|mount_vfs|copy_out|exists|is_symlink|umount_all|shutdown)",
        )
        .allowlist_var("GUESTFS_ADD_DRIVE_OPTS_(READONLY|FORMAT)")
        .layout_tests(false)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
        .generate()
        .expect("generate libguestfs bindings");

    let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set by Cargo"));
    bindings
        .write_to_file(out_dir.join("guestfs_bindings.rs"))
        .expect("write generated libguestfs bindings");
}