overhaul nixide-sys/build.rs

This commit is contained in:
do butterflies cry? 2026-04-02 09:52:29 +10:00
parent 32cd7e0587
commit 46baf3cd2e
Signed by: cry
GPG key ID: F68745A836CA0412
7 changed files with 150 additions and 608 deletions

View file

@ -14,7 +14,7 @@ build = "build.rs"
targets = [ "x86_64-unknown-linux-gnu" ]
[lib]
path = "lib.rs"
path = "src/lib.rs"
# NOTE: `[features]` have a 1-1 correspondence to the
# NOTE: shared libraries produced by the Nix C API.
@ -28,15 +28,14 @@ nix-flake-c = []
nix-main-c = []
[build-dependencies]
itertools = "0.14.0"
heck = "0.5.0"
# rust-bindgen
bindgen = { default-features = false, features = [ "logging", "runtime" ], version = "0.72.1" }
doxygen-bindgen = "0.1.3"
itertools = "0.14.0"
pkg-config.workspace = true
cc.workspace = true
autocxx-build = "0.30.0"
[dev-dependencies]
serial_test = "3.4.0"
[dependencies]
autocxx = "0.30.0"

View file

@ -1,7 +1,11 @@
use std::env;
use std::fs;
use std::path::PathBuf;
use std::{env, fs};
use bindgen::callbacks::ParseCallbacks;
use bindgen::RustEdition;
use bindgen::callbacks::{ItemKind, ParseCallbacks};
use heck::ToSnekCase;
use heck::ToUpperCamelCase;
use itertools::Itertools;
#[derive(Debug)]
@ -19,7 +23,57 @@ impl ParseCallbacks for DoxygenCallbacks {
}
}
// WARNING: NOTE: the random panic occurs when you're missing imports!!
/// Bindfmt is just the name im giving to the callbacks
/// that handle renaming C/C++ tokens.
#[derive(Debug)]
struct BindfmtCallbacks;
#[inline]
fn strip_variant_prefix(
prefix: &'static str,
enum_name: &str,
variant: &str,
) -> Result<String, String> {
variant
.strip_prefix(prefix)
.map(str::to_owned)
.ok_or(format!(
"[bindfmt] enum {enum_name} expected prefix \"{prefix}\" but got {}",
&variant
))
}
impl ParseCallbacks for BindfmtCallbacks {
fn enum_variant_name(
&self,
_enum_name: Option<&str>,
_original_variant_name: &str,
_variant_value: bindgen::callbacks::EnumVariantValue,
) -> Option<String> {
let variant = _original_variant_name.to_upper_camel_case();
_enum_name.map(|enum_name| match enum_name.to_upper_camel_case().as_ref() {
"NixVerbosity" => strip_variant_prefix("NixLvl", enum_name, &variant).unwrap(),
"NixErr" => strip_variant_prefix("NixErr", enum_name, &variant)
.or_else(|_| strip_variant_prefix("Nix", enum_name, &variant))
.unwrap(),
"ValueType" => strip_variant_prefix("NixType", enum_name, &variant).unwrap(),
_ => variant,
})
}
fn item_name(&self, _item_info: bindgen::callbacks::ItemInfo) -> Option<String> {
Some(match _item_info.kind {
ItemKind::Type => _item_info.name.to_upper_camel_case(),
_ => _item_info.name.to_snek_case(),
})
}
fn include_file(&self, _filename: &str) {
eprintln!("[debug] including file: {}", _filename);
}
}
const LIBS: &[&'static str] = &[
#[cfg(feature = "nix-util-c")]
"nix-util-c",
@ -36,42 +90,61 @@ const LIBS: &[&'static str] = &[
];
fn main() {
// Invalidate the built crate if the binding headers change
// println!("cargo::rerun-if-changed=include");
let include_paths: Vec<PathBuf> = LIBS
.iter()
.map(|&name| {
let lib = pkg_config::probe_library(name)
.expect(&format!("Unable to find .pc file for {}", name));
let lib_args: Vec<String> = vec![ /* EXTRA ARGS */ ]
.into_iter()
.map(|s: &str| s.to_owned())
.chain(
LIBS.iter()
.map(|&name| {
let lib = pkg_config::probe_library(name)
.expect(&format!("Unable to find .pc file for {}", name));
for p in lib.link_files {
println!("cargo::rustc-link-lib={}", p.display());
}
for p in lib.link_files {
println!("cargo::rustc-link-lib={}", p.display());
}
lib.include_paths
.into_iter()
.map(|p| format!("-I{}", p.display()))
})
.flatten(),
)
lib.include_paths
})
.flatten()
.unique()
.collect();
dbg!(&lib_args);
let clang_args: Vec<String> = vec!["-x", "c++", "-std=c++23"]
.into_iter()
.map(|s: &str| s.to_owned())
.chain(include_paths.iter().map(|p| format!("-I{}", p.display())))
.collect();
dbg!(&clang_args);
let mut builder = bindgen::Builder::default()
// .enable_cxx_namespaces()
.clang_args(lib_args)
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.rust_edition(RustEdition::Edition2024)
.clang_args(clang_args)
// Add `doxygen_bindgen` callbacks
.parse_callbacks(Box::new(DoxygenCallbacks))
.parse_callbacks(Box::new(BindfmtCallbacks))
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
// Format generated bindings with rustfmt
.formatter(bindgen::Formatter::Rustfmt)
.rustfmt_configuration_file(std::fs::canonicalize("rustfmt.toml").ok());
.rustfmt_configuration_file(std::fs::canonicalize("rustfmt.toml").ok())
.allowlist_file(r".*nix_api_[a-z]+\.h")
// Wrap all unsafe operations in unsafe blocks
.layout_tests(true)
.use_core() // use ::core instead of ::std
.ctypes_prefix("::core::ffi") // use ::core::ffi instead of ::std::os::raw
.time_phases(true)
.wrap_unsafe_ops(true)
.trust_clang_mangling(true)
.respect_cxx_access_specs(true)
.default_enum_style(bindgen::EnumVariation::Rust { non_exhaustive: false })
.translate_enum_integer_types(false)
.size_t_is_usize(true)
.use_distinct_char16_t(false)
.generate_comments(false)
.generate_cstr(true) // use &CStr instead of &[u8]
.fit_macro_constants(true)
.explicit_padding(true)
.enable_cxx_namespaces()
.represent_cxx_operators(true)
.enable_function_attribute_detection()
.raw_line("/** These bindings were auto-generated for the Nixide project (https://github.com/cry128/nixide) */");
// Register the input headers we would like to generate bindings for
builder = LIBS
@ -80,7 +153,7 @@ fn main() {
let path = format!("include/{}.h", lib.strip_suffix("-c").unwrap());
assert!(fs::exists(&path).unwrap());
// Invalidate the built crate if the binding headers change
// println!("cargo::rerun-if-changed={path}");
println!("cargo::rerun-if-changed={path}");
path
})
.fold(builder, |builder, path| builder.header(path));

View file

@ -1,17 +0,0 @@
//! # nixide-sys
//!
//! Unsafe direct FFI bindings to libnix C API.
//!
//! ## Safety
//!
//! These bindings are generated automatically and map directly to the C API.
//! They are unsafe to use directly. Prefer using the high-level safe API in the
//! parent crate unless you know what you're doing.
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(rustdoc::bare_urls)]
#![allow(rustdoc::invalid_html_tags)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

23
nixide-sys/src/lib.rs Normal file
View file

@ -0,0 +1,23 @@
//! # nixide-sys
//!
//! Unsafe direct FFI bindings to `libnix` C API.
//!
//! ## Safety
//!
//! These bindings are generated automatically and map directly to the C API.
//! They are unsafe to use directly. Prefer using the high-level safe API in the
//! parent crate unless you know what you're doing.
//!
//! The [nixide] crate should also act as good explanation of how
//! these bindings, and hence `libnix` itself, are expected to be used.
mod bindings {
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(rustdoc::bare_urls)]
#![allow(rustdoc::invalid_html_tags)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub use bindings::root::*;

View file

@ -0,0 +1,2 @@
# `nixide-sys/tests`
These tests are modified from the ones provided by [github:nixops4/nix-bindings-rust](https://github.com/nixops4/nix-bindings-rust)