162 lines
6.1 KiB
Rust
162 lines
6.1 KiB
Rust
use std::env;
|
|
use std::path::PathBuf;
|
|
|
|
fn main() {
|
|
let cargo_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
|
let root_dir = cargo_dir
|
|
.parent()
|
|
.expect("rust dir is nested")
|
|
.parent()
|
|
.expect("bindings dir is nested");
|
|
|
|
// Obtain the header files of blst
|
|
let blst_base_dir = root_dir.join("blst");
|
|
let blst_headers_dir = blst_base_dir.join("bindings");
|
|
|
|
let c_src_dir = root_dir.join("src");
|
|
|
|
let mut cc = cc::Build::new();
|
|
|
|
#[cfg(all(windows, target_env = "msvc"))]
|
|
{
|
|
cc.flag("-D_CRT_SECURE_NO_WARNINGS");
|
|
|
|
// In blst, if __STDC_VERSION__ isn't defined as c99 or greater, it will typedef a bool to
|
|
// an int. There is a bug in bindgen associated with this. It assumes that a bool in C is
|
|
// the same size as a bool in Rust. This is the root cause of the issues on Windows. If/when
|
|
// this is fixed in bindgen, it should be safe to remove this compiler flag.
|
|
cc.flag("/std:c11");
|
|
}
|
|
|
|
cc.include(blst_headers_dir.clone());
|
|
cc.warnings(false);
|
|
cc.file(c_src_dir.join("c_kzg_4844.c"));
|
|
|
|
cc.try_compile("ckzg").expect("Failed to compile ckzg");
|
|
|
|
#[cfg(feature = "generate-bindings")]
|
|
{
|
|
let header_path = c_src_dir.join("c_kzg_4844.h");
|
|
let bindings_out_path = concat!(env!("CARGO_MANIFEST_DIR"), "/src/bindings/generated.rs");
|
|
make_bindings(
|
|
header_path.to_str().expect("valid header path"),
|
|
blst_headers_dir.to_str().expect("valid blst header path"),
|
|
bindings_out_path.as_ref(),
|
|
);
|
|
}
|
|
|
|
// Finally, tell cargo this provides ckzg/ckzg_min
|
|
println!("cargo:rustc-link-lib=ckzg");
|
|
}
|
|
|
|
#[cfg(feature = "generate-bindings")]
|
|
fn make_bindings(header_path: &str, blst_headers_dir: &str, bindings_out_path: &std::path::Path) {
|
|
use bindgen::Builder;
|
|
|
|
#[derive(Debug)]
|
|
struct Callbacks;
|
|
impl bindgen::callbacks::ParseCallbacks for Callbacks {
|
|
fn int_macro(&self, name: &str, _value: i64) -> Option<bindgen::callbacks::IntKind> {
|
|
match name {
|
|
"FIELD_ELEMENTS_PER_BLOB"
|
|
| "BYTES_PER_COMMITMENT"
|
|
| "BYTES_PER_PROOF"
|
|
| "BYTES_PER_FIELD_ELEMENT"
|
|
| "BYTES_PER_BLOB" => Some(bindgen::callbacks::IntKind::Custom {
|
|
name: "usize",
|
|
is_signed: false,
|
|
}),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
let bindings = Builder::default()
|
|
/*
|
|
* Header definitions.
|
|
*/
|
|
.header(header_path)
|
|
.clang_args([format!("-I{blst_headers_dir}")])
|
|
// Get bindings only for the header file.
|
|
.allowlist_file(".*c_kzg_4844.h")
|
|
/*
|
|
* Cleanup instructions.
|
|
*/
|
|
// Remove stdio definitions related to FILE.
|
|
.opaque_type("FILE")
|
|
// Remove the definition of FILE to use the libc one, which is more convenient.
|
|
.blocklist_type("FILE")
|
|
// Inject rust code using libc's FILE
|
|
.raw_line("use libc::FILE;")
|
|
// Do no generate layout tests.
|
|
.layout_tests(false)
|
|
// Extern functions do not need individual extern blocks.
|
|
.merge_extern_blocks(true)
|
|
// We implement Drop for this type. Copy is not allowed for types with destructors.
|
|
.no_copy("KZGSettings")
|
|
/*
|
|
* API improvements.
|
|
*/
|
|
// Do not create individual constants for enum variants.
|
|
.rustified_enum("C_KZG_RET")
|
|
// Make constants used as sizes `usize`.
|
|
.parse_callbacks(Box::new(Callbacks))
|
|
// Add PartialEq and Eq impls to types.
|
|
.derive_eq(true)
|
|
// Blobs are big, we don't want rust to liberally copy them around.
|
|
.no_copy("Blob")
|
|
// Do not make fields public. If we want to modify them we can create setters/mutable
|
|
// getters when necessary.
|
|
.default_visibility(bindgen::FieldVisibilityKind::Private)
|
|
// Blocklist this type alias to use a custom implementation. If this stops being a type
|
|
// alias this line needs to be removed.
|
|
.blocklist_type("KZGCommitment")
|
|
// Blocklist this type alias to use a custom implementation. If this stops being a type
|
|
// alias this line needs to be removed.
|
|
.blocklist_type("KZGProof")
|
|
/*
|
|
* Re-build instructions
|
|
*/
|
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
|
|
.generate()
|
|
.unwrap();
|
|
|
|
let mut bindings = bindings.to_string();
|
|
bindings = replace_ckzg_ret_repr(bindings);
|
|
std::fs::write(bindings_out_path, bindings).expect("Failed to write bindings");
|
|
}
|
|
|
|
// Here we hardcode the C_KZG_RET enum to use C representation. Bindgen
|
|
// will use repr(u32) on Unix and repr(i32) on Windows. We would like to
|
|
// use the same generated bindings for all platforms. This can be removed
|
|
// if/when bindgen fixes this or allows us to choose our own representation
|
|
// for types. Using repr(C) is equivalent to repr(u*) for fieldless enums,
|
|
// so this should be safe to do. The alternative was to modify C_KZG_RET in
|
|
// C and we decided this was the lesser of two evils. There should be only
|
|
// one instance where repr(C) isn't used: C_KZG_RET.
|
|
// See: https://github.com/rust-lang/rust-bindgen/issues/1907
|
|
#[cfg(feature = "generate-bindings")]
|
|
fn replace_ckzg_ret_repr(mut bindings: String) -> String {
|
|
let target = env::var("TARGET").unwrap_or_default();
|
|
let repr_to_replace = if target.contains("windows") {
|
|
"#[repr(i32)]"
|
|
} else {
|
|
"#[repr(u32)]"
|
|
};
|
|
|
|
// Find `repr_to_replace` as an attribute of `enum C_KZG_RET`.
|
|
let ckzg_ret = bindings
|
|
.find("enum C_KZG_RET")
|
|
.expect("Could not find C_KZG_RET in bindings");
|
|
let repr_start = bindings[..ckzg_ret]
|
|
.rfind(repr_to_replace)
|
|
.expect("Could not find repr to replace in bindings");
|
|
|
|
// Sanity check that it's an attribute of `C_KZG_RET` and not another type.
|
|
assert!(repr_start > bindings[..ckzg_ret].rfind('}').unwrap());
|
|
|
|
bindings.replace_range(repr_start..repr_start + repr_to_replace.len(), "#[repr(C)]");
|
|
|
|
bindings
|
|
}
|