extern crate bindgen;
extern crate pkg_config;
use std::env;
use std::fs;
use std::iter;
use std::path::PathBuf;
const FUSE_DEFAULT_API_VERSION: u32 = 30;
macro_rules! version {
($version_var:ident, $feature:literal, $version:literal) => {
#[cfg(feature = $feature)]
{
if $version_var.is_some() {
panic!("More than one FUSE API version feature is enabled");
}
$version_var = Some($version);
}
};
}
fn fuse_binding_filter(builder: bindgen::Builder) -> bindgen::Builder {
let mut builder = builder
.allowlist_recursively(false)
.allowlist_type("fuse.*")
.allowlist_function("fuse.*")
.allowlist_var("FUSE.*")
.allowlist_type("libfuse.*")
.blocklist_type("fuse_log_func_t")
.blocklist_function("fuse_set_log_func");
if cfg!(target_os = "macos") {
builder = builder.allowlist_type("setattr_x").allowlist_type("statx");
}
builder
}
#[cfg(feature = "cuse_lowlevel")]
fn cuse_binding_filter(builder: bindgen::Builder) -> bindgen::Builder {
builder
.allowlist_recursively(false)
.allowlist_type("cuse.*")
.allowlist_function("cuse.*")
.allowlist_var("CUSE.*")
}
fn generate_fuse_bindings(
header: &str,
api_version: u32,
fuse_lib: &pkg_config::Library,
binding_filter: fn(bindgen::Builder) -> bindgen::Builder,
) {
let mut header_path: Option<PathBuf> = None;
for include_path in fuse_lib.include_paths.iter() {
let test_path = include_path.join(header);
if test_path.exists() {
header_path = Some(test_path);
break;
}
}
let header_path = header_path
.unwrap_or_else(|| panic!("Cannot find {}", header))
.to_str()
.unwrap_or_else(|| panic!("Path to {} contains invalid unicode characters", header))
.to_string();
let defines = fuse_lib.defines.iter().map(|(key, val)| match val {
Some(val) => format!("-D{}={}", key, val),
None => format!("-D{}", key),
});
let includes = fuse_lib
.include_paths
.iter()
.map(|dir| format!("-I{}", dir.display()));
let api_define = iter::once(format!("-DFUSE_USE_VERSION={}", api_version));
let compile_flags = defines.chain(includes).chain(api_define);
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let statics_file = out_dir.join(header.replace(".h", "-statics.c"));
let mut builder = bindgen::builder()
.clang_args(compile_flags)
.derive_default(true)
.derive_copy(true)
.derive_debug(true)
.wrap_static_fns(true)
.wrap_static_fns_path(&statics_file)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()));
builder = binding_filter(builder);
let bindings = builder
.header(header_path)
.generate()
.unwrap_or_else(|_| panic!("Failed to generate {} bindings", header));
let bindings_path = out_dir.join(header.replace(".h", ".rs"));
bindings
.write_to_file(&bindings_path)
.unwrap_or_else(|_| panic!("Failed to write {}", bindings_path.display()));
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let target_dir = manifest_dir.join("target");
if target_dir.is_dir() {
let debug_bindings_path = target_dir.join(bindings_path.file_name().unwrap());
fs::copy(bindings_path, debug_bindings_path).unwrap();
}
if fs::exists(&statics_file).unwrap() {
let api_version_string = api_version.to_string();
cc::Build::new()
.file(&statics_file)
.define("FUSE_USE_VERSION", Some(api_version_string.as_str()))
.compile(header.strip_suffix(".h").unwrap())
}
}
fn main() {
#[allow(unused_mut)]
let mut api_version: Option<u32> = None;
version!(api_version, "fuse_11", 11);
version!(api_version, "fuse_21", 21);
version!(api_version, "fuse_22", 22);
version!(api_version, "fuse_24", 24);
version!(api_version, "fuse_25", 25);
version!(api_version, "fuse_26", 26);
version!(api_version, "fuse_29", 29);
version!(api_version, "fuse_30", 30);
version!(api_version, "fuse_31", 31);
version!(api_version, "fuse_35", 35);
version!(api_version, "fuse_37", 37);
version!(api_version, "fuse_38", 38);
version!(api_version, "fuse_312", 312);
version!(api_version, "fuse_317", 317);
version!(api_version, "fuse_318", 318);
if api_version.is_none() {
println!(
"cargo:warning=No FUSE API version feature selected. Defaulting to version {}.",
FUSE_DEFAULT_API_VERSION
);
}
let api_version = api_version.unwrap_or(FUSE_DEFAULT_API_VERSION);
let mut pkgcfg = pkg_config::Config::new();
pkgcfg.cargo_metadata(false);
let try_fuse_lib = pkgcfg.probe("fuse");
let try_fuse3_lib = pkgcfg.probe("fuse3");
let fuse_lib = match (try_fuse_lib, try_fuse3_lib) {
(Err(err), Err(err3)) => panic!(
"Failed to find pkg-config modules fuse ({}) or fuse3 ({})",
err, err3
),
(Ok(_), Err(_)) => "fuse",
(Err(_), Ok(_)) => "fuse3",
(Ok(_), Ok(_)) => {
if api_version < 30 {
"fuse"
} else {
"fuse3"
}
}
};
let fuse_lib = pkgcfg.cargo_metadata(true).probe(fuse_lib).unwrap();
#[cfg(feature = "fuse_highlevel")]
generate_fuse_bindings("fuse.h", api_version, &fuse_lib, fuse_binding_filter);
#[cfg(feature = "fuse_lowlevel")]
generate_fuse_bindings(
"fuse_lowlevel.h",
api_version,
&fuse_lib,
fuse_binding_filter,
);
#[cfg(feature = "cuse_lowlevel")]
generate_fuse_bindings(
"cuse_lowlevel.h",
api_version,
&fuse_lib,
cuse_binding_filter,
);
}