ferret/crates/mpv-bindings/build.rs

84 lines
3.2 KiB
Rust
Executable File

// build.rs — generate raw FFI bindings to libmpv via bindgen.
//
// We resolve libmpv through pkg-config. The setup script generates
// mpv-prefix/usr/lib/x86_64-linux-gnu/pkgconfig/mpv.pc and exports
// PKG_CONFIG_PATH via mpv-prefix/env.sh — `cargo build` picks it up
// automatically once env.sh is sourced.
//
// The generated bindings end up in OUT_DIR/bindings.rs and are re-exported
// from `crate::sys`.
use std::env;
use std::path::PathBuf;
fn main() {
// Re-run if the env or any pkg-config .pc file under the prefix changes.
println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH");
if let Ok(prefix) = env::var("MPV_PREFIX") {
let pc = PathBuf::from(&prefix)
.join("usr/lib/x86_64-linux-gnu/pkgconfig/mpv.pc");
println!("cargo:rerun-if-changed={}", pc.display());
}
let mpv = pkg_config::Config::new()
.atleast_version("2.0")
.probe("mpv")
.expect("failed to locate libmpv via pkg-config (did you `source ./mpv-prefix/env.sh`?)");
// Tell cargo to link libmpv.
for path in &mpv.link_paths {
println!("cargo:rustc-link-search=native={}", path.display());
}
println!("cargo:rustc-link-lib=dylib=mpv");
// Embed rpath so the binary finds libmpv.so.2 at runtime without
// requiring LD_LIBRARY_PATH. We use the first link path from pkg-config.
if let Some(first) = mpv.link_paths.first() {
println!("cargo:rustc-link-arg=-Wl,-rpath,{}", first.display());
}
// Find the canonical client.h — it #includes the other public headers.
let client_h = mpv
.include_paths
.iter()
.map(|p| p.join("mpv/client.h"))
.find(|p| p.exists())
.expect("could not find mpv/client.h in include paths");
// Clang's builtin headers (stddef.h etc.) — needed because we ship our
// own libclang without a system sysroot.
let clang_resource_dir = env::var("CLANG_RESOURCE_DIR")
.unwrap_or_else(|_| "/usr/lib/llvm-19/lib/clang/19".to_string());
let clang_include = format!("{clang_resource_dir}/include");
let bindings = bindgen::Builder::default()
.header(client_h.to_string_lossy().into_owned())
// Pull in the other public headers transitively.
.clang_arg("-include")
.clang_arg("mpv/render.h")
.clang_arg("-include")
.clang_arg("mpv/render_gl.h")
.clang_arg("-include")
.clang_arg("mpv/stream_cb.h")
// Clang builtin headers (stddef.h, stdarg.h, etc.)
.clang_arg("-isystem")
.clang_arg(&clang_include)
// Allowlist: only the public mpv API surface.
.allowlist_function("mpv_.*")
.allowlist_type("mpv_.*")
.allowlist_var("MPV_.*")
.allowlist_var("mpv_.*")
// Layout tests break across glibc versions for some reason; we don't need them.
.layout_tests(false)
.derive_default(true)
.derive_debug(true)
.fit_macro_constants(false)
.generate()
.expect("bindgen failed to generate mpv bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("failed to write bindings.rs");
}