ntgcalls-sys 3.0.0-beta12

Raw FFI bindings to the ntgcalls C ABI
use std::env;
use std::fs;
use std::path::PathBuf;

fn shared_name() -> Option<&'static str> {
    let os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
    let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
    match (os.as_str(), arch.as_str()) {
        ("linux", "x86_64") => Some("linux-x86_64"),
        ("linux", "aarch64") => Some("linux-arm64"),
        ("macos", _) => Some("macos-arm64"),
        ("windows", "x86_64") => Some("windows-x86_64"),
        _ => None,
    }
}

fn download_lib(out: &PathBuf) -> PathBuf {
    let name = shared_name().expect("unsupported target platform");
    let version = env::var("CARGO_PKG_VERSION").unwrap();
    let base = env::var("NTGCALLS_RELEASE_BASE")
        .unwrap_or_else(|_| "https://github.com/pytgcalls/ntgcalls/releases/download".into());
    let url = format!("{base}/v{version}/ntgcalls.{name}-static_libs.zip");
    let dir = out.join("ntgcalls-lib");
    if !dir.join("lib").exists() {
        let bytes = ureq::get(&url)
            .call()
            .unwrap_or_else(|e| panic!("failed to fetch {url}: {e}"))
            .into_body()
            .into_with_config()
            .limit(u64::MAX)
            .read_to_vec()
            .expect("read release body");
        let reader = std::io::Cursor::new(bytes);
        let mut zip = zip::ZipArchive::new(reader).expect("open zip");
        fs::create_dir_all(&dir).unwrap();
        zip.extract(&dir).expect("extract zip");
    }
    dir.join("lib")
}

fn lib_dir() -> PathBuf {
    if let Ok(dir) = env::var("NTGCALLS_LIB_DIR") {
        return PathBuf::from(dir);
    }
    let local = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../../static-output/lib");
    if local.exists() {
        return local;
    }
    let out = PathBuf::from(env::var("OUT_DIR").unwrap());
    download_lib(&out)
}

#[cfg(target_env = "gnu")]
fn host_glibc_ge_234() -> bool {
    use std::ffi::CStr;
    use std::os::raw::c_char;
    extern "C" {
        fn gnu_get_libc_version() -> *const c_char;
    }
    let raw = unsafe { CStr::from_ptr(gnu_get_libc_version()) };
    let ver = raw.to_string_lossy();
    let mut parts = ver.split('.');
    let major: u32 = parts.next().and_then(|x| x.trim().parse().ok()).unwrap_or(0);
    let minor: u32 = parts.next().and_then(|x| x.trim().parse().ok()).unwrap_or(0);
    major > 2 || (major == 2 && minor >= 34)
}

#[cfg(not(target_env = "gnu"))]
fn host_glibc_ge_234() -> bool {
    false
}

fn main() {
    println!("cargo:rustc-check-cfg=cfg(glibc_resolv_compat)");
    if env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux")
        && env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("gnu")
        && host_glibc_ge_234()
    {
        println!("cargo:rustc-cfg=glibc_resolv_compat");
    }

    let dir = lib_dir();
    println!("cargo:rerun-if-env-changed=NTGCALLS_LIB_DIR");
    println!("cargo:rerun-if-env-changed=NTGCALLS_DYLIB");
    println!("cargo:rustc-link-search=native={}", dir.display());

    if env::var("NTGCALLS_DYLIB").is_ok() {
        println!("cargo:rustc-link-lib=dylib=ntgcalls");
        println!("cargo:rustc-link-arg=-Wl,-rpath,{}", dir.display());
        println!("cargo:rustc-link-arg=-Wl,--allow-shlib-undefined");
        return;
    }

    println!("cargo:rustc-link-lib=static=ntgcalls");

    match env::var("CARGO_CFG_TARGET_OS").as_deref() {
        Ok("linux") => {
            for lib in ["stdc++", "m", "z", "resolv"] {
                println!("cargo:rustc-link-lib=dylib={lib}");
            }
        }
        Ok("macos") => {
            println!("cargo:rustc-link-lib=dylib=c++");
            println!("cargo:rustc-link-lib=dylib=z");
            println!("cargo:rustc-link-lib=framework=Foundation");
            println!("cargo:rustc-link-lib=framework=CoreFoundation");
        }
        Ok("windows") => {
            for lib in [
                "winmm", "ws2_32", "strmiids", "dmoguids", "iphlpapi", "msdmo",
                "secur32", "wmcodecdspuuid", "d3d11", "dxgi", "dwmapi", "shcore",
                "bcrypt", "ntdll", "crypt32", "userenv", "gdi32", "user32",
            ] {
                println!("cargo:rustc-link-lib=dylib={lib}");
            }
        }
        _ => {}
    }
}