alef 0.65.0

Opinionated polyglot binding generator for Rust libraries
Documentation
// This file is auto-generated by alef. DO NOT EDIT.

/// Uppercase the alphanumerics and turn every other character into `_` -- the exact transform
/// alef applies when it fills cbindgen's `[defines]` table, so this lands on the same macro
/// name the header's `#if defined(...)` guards test.
fn feature_macro_suffix(feature: &str) -> String {
    let mut name = feature.replace(|c: char| !c.is_ascii_alphanumeric(), "_");
    name.make_ascii_uppercase();
    name
}

fn main() {
    let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
    cbindgen::generate(crate_dir)
        .expect("Unable to generate C bindings")
        .write_to_file("include/{{ header_name }}");
{{ capsule_header_fixup }}
    // Stamp the Cargo features THIS build enabled into the header, so it describes the cdylib
    // sitting beside it.
    //
    // cbindgen wraps every `#[cfg(feature = "x")]` export in
    // `#if defined({{ prefix_upper }}_FEATURE_X)` (its `[defines]` table), and it does so from
    // the unparameterised source, so the guards are present no matter how the crate was built.
    // Nothing ever defined those macros: every consumer that runs the C preprocessor over this
    // header -- cgo, the C snippet and e2e harnesses, Zig, hand-written C -- saw every gated
    // declaration deleted and failed on symbols this library really does export.
    //
    // The feature set is read from Cargo's own `CARGO_FEATURE_*`, not from a list baked in at
    // generation time, so there is no second derivation that can drift from the artifact. A
    // slim build simply emits fewer defines and its guards correctly stay false, which is what
    // keeps the guards load-bearing instead of decorative.
    //
    // The sanitiser mirrors alef's `cbindgen_feature_defines`: alphanumerics uppercased, every
    // other character to `_`. Cargo's own `-` to `_` mapping is a subset of that, so composing
    // the two lands on exactly the macro cbindgen guards with, including for feature names
    // carrying `.` or `+`, which Cargo leaves untouched in the variable name.
    {
        let header_path = "include/{{ header_name }}";
        let mut header = std::fs::read_to_string(header_path).expect("read generated header");
        let marker = "/* Cargo features enabled in this build. */";
        if !header.contains(marker) && header.contains("{{ prefix_upper }}_FEATURE_") {
            // `vars_os`, not `vars`: the latter panics if *any* variable in the environment is
            // not valid Unicode, which would fail the consumer's build over something that has
            // nothing to do with this crate.
            let mut defines: Vec<String> = std::env::vars_os()
                .filter_map(|(key, _)| {
                    let feature = key.to_str()?.strip_prefix("CARGO_FEATURE_")?;
                    let name = feature_macro_suffix(feature);
                    Some(format!("#define {{ prefix_upper }}_FEATURE_{name} 1"))
                })
                .collect();
            defines.sort();
            defines.dedup();
            let block = format!("\n{marker}\n{}\n", defines.join("\n"));
            // Anchor on the include guard cbindgen opens the file with, so the defines land
            // ahead of every `#if` that tests them. Prepending is the fallback: still valid C,
            // still ahead of every guard, if cbindgen ever stops emitting the guard.
            let anchor = "#define {{ prefix_upper }}_H\n";
            match header.find(anchor) {
                Some(at) => header.insert_str(at + anchor.len(), &block),
                None => header.insert_str(0, &block),
            }
            std::fs::write(header_path, header).expect("write header feature defines");
        }
    }
    // Set @rpath-relative install_name on macOS so the cdylib can be relocated
    // (bundled into language packages like packages/go/.lib/<rid>/, packages/
    // java/src/main/resources/natives/<rid>/, etc.) and located via the consumer
    // binary's rpath at runtime. Without this, the install_name embeds the CI
    // runner build path (`/Users/runner/work/.../target/.../deps/lib<name>.dylib`)
    // and dyld fails to load the bundled copy from its actual location.
    //
    // Also add `@loader_path` as an LC_RPATH so transitively-linked dylibs
    // (e.g. `@rpath/libonnxruntime.<ver>.dylib` referenced by ort-bundled) can
    // be resolved against the same directory the consumer ships the FFI
    // cdylib in. NuGet/Maven/wheels all co-locate the bundled native deps
    // alongside the cdylib, so `@loader_path` is the universally correct rpath.
    // Without LC_RPATH entries, dyld reports `no LC_RPATH's found` and the
    // load fails even though the sibling dylib is present.
    if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") {
        println!("cargo:rustc-link-arg-cdylib=-Wl,-install_name,@rpath/{{ lib_name }}.dylib");
        println!("cargo:rustc-link-arg-cdylib=-Wl,-rpath,@loader_path");
    }
{{ go_copy_step }}}