use std::env; use std::path::{Path, PathBuf}; const MAINNET_FIELD_ELEMENTS_PER_BLOB: usize = 4096; const MINIMAL_FIELD_ELEMENTS_PER_BLOB: usize = 4; /// Compiles blst. // // NOTE: This code is taken from https://github.com/supranational/blst `build.rs` `main`. The crate // is not used as a depedency to avoid double link issues on dependants. fn compile_blst(blst_base_dir: PathBuf) { // account for cross-compilation [by examining environment variables] let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); if target_os.ne("none") && !env::var("BLST_TEST_NO_STD").is_ok() { println!("cargo:rustc-cfg=feature=\"std\""); if target_arch.eq("wasm32") { println!("cargo:rustc-cfg=feature=\"no-threads\""); } } println!("cargo:rerun-if-env-changed=BLST_TEST_NO_STD"); println!("Using blst source directory {}", blst_base_dir.display()); // Set CC environment variable to choose alternative C compiler. // Optimization level depends on whether or not --release is passed // or implied. #[cfg(target_env = "msvc")] if env::var("CARGO_CFG_TARGET_POINTER_WIDTH").unwrap().eq("32") && !env::var("CC").is_ok() { match std::process::Command::new("clang-cl") .arg("--version") .output() { Ok(out) => { if String::from_utf8(out.stdout) .unwrap_or("unintelligible".to_string()) .contains("Target: i686-") { env::set_var("CC", "clang-cl"); } } Err(_) => { /* no clang-cl in sight, just ignore the error */ } } } let mut cc = cc::Build::new(); let c_src_dir = blst_base_dir.join("src"); println!("cargo:rerun-if-changed={}", c_src_dir.display()); let mut file_vec = vec![c_src_dir.join("server.c")]; if target_arch.eq("x86_64") || target_arch.eq("aarch64") { let asm_dir = blst_base_dir.join("build"); println!("cargo:rerun-if-changed={}", asm_dir.display()); blst_assembly(&mut file_vec, &asm_dir, &target_arch); } else { cc.define("__BLST_NO_ASM__", None); } match (cfg!(feature = "portable"), cfg!(feature = "force-adx")) { (true, false) => { println!("Compiling in portable mode without ISA extensions"); cc.define("__BLST_PORTABLE__", None); } (false, true) => { if target_arch.eq("x86_64") { println!("Enabling ADX support via `force-adx` feature"); cc.define("__ADX__", None); } else { println!("`force-adx` is ignored for non-x86_64 targets"); } } (false, false) => { #[cfg(target_arch = "x86_64")] if target_arch.eq("x86_64") && std::is_x86_feature_detected!("adx") { println!("Enabling ADX because it was detected on the host"); cc.define("__ADX__", None); } } (true, true) => panic!("Cannot compile with both `portable` and `force-adx` features"), } if env::var("CARGO_CFG_TARGET_ENV").unwrap().eq("msvc") { cc.flag("-Zl"); } cc.flag_if_supported("-mno-avx") // avoid costly transitions .flag_if_supported("-fno-builtin") .flag_if_supported("-Wno-unused-function") .flag_if_supported("-Wno-unused-command-line-argument"); if target_arch.eq("wasm32") { cc.flag_if_supported("-ffreestanding"); } if !cfg!(debug_assertions) { cc.opt_level(2); } cc.files(&file_vec).compile("blst"); } /// Adds assembly files for blst compilation. fn blst_assembly(file_vec: &mut Vec, base_dir: &Path, _arch: &String) { #[cfg(target_env = "msvc")] if env::var("CARGO_CFG_TARGET_ENV").unwrap().eq("msvc") { let sfx = match _arch.as_str() { "x86_64" => "x86_64", "aarch64" => "armv8", _ => "unknown", }; let files = glob::glob(&format!("{}/win64/*-{}.asm", base_dir.display(), sfx)) .expect("unable to collect assembly files"); for file in files { file_vec.push(file.unwrap()); } return; } file_vec.push(base_dir.join("assembly.S")); } 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"); let field_elements_per_blob = if cfg!(feature = "minimal-spec") { MINIMAL_FIELD_ELEMENTS_PER_BLOB } else { MAINNET_FIELD_ELEMENTS_PER_BLOB }; eprintln!("Using FIELD_ELEMENTS_PER_BLOB={}", field_elements_per_blob); let blst_base_dir = root_dir.join("blst"); compile_blst(blst_base_dir.clone()); // Obtain the header files of 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(windows)] cc.flag("-D_CRT_SECURE_NO_WARNINGS"); cc.include(blst_headers_dir.clone()); cc.warnings(false); cc.flag(format!("-DFIELD_ELEMENTS_PER_BLOB={}", field_elements_per_blob).as_str()); cc.file(c_src_dir.join("c_kzg_4844.c")); cc.try_compile("ckzg").expect("Failed to compile ckzg"); // Tell cargo to search for the static blst exposed by the blst-bindings' crate. println!("cargo:rustc-link-lib=static=blst"); let bindings_out_path = cargo_dir.join("src").join("bindings").join("generated.rs"); let header_file_path = c_src_dir.join("c_kzg_4844.h"); let header_file = header_file_path.to_str().expect("valid header file"); make_bindings( field_elements_per_blob, header_file, &blst_headers_dir.to_string_lossy(), bindings_out_path, ); // Finally, tell cargo this provides ckzg println!("cargo:rustc-link-lib=ckzg"); } fn make_bindings

( field_elements_per_blob: usize, header_path: &str, blst_headers_dir: &str, bindings_out_path: P, ) where P: AsRef, { use bindgen::Builder; #[derive(Debug)] struct Callbacks; impl bindgen::callbacks::ParseCallbacks for Callbacks { fn int_macro(&self, name: &str, _value: i64) -> Option { 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. */ // Inject the constant as C code so that the C compiler can use it. // -D is not supported by bindgen https://github.com/rust-lang/rust-bindgen/issues/2394 .header_contents( "consts", &format!("#define FIELD_ELEMENTS_PER_BLOB {field_elements_per_blob}"), ) .header(header_path) .clang_args([format!("-I{blst_headers_dir}")]) // Since this is not part of the header file, needs to be allowed explicitly. .allowlist_var("FIELD_ELEMENTS_PER_BLOB") // 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)) .generate() .unwrap(); bindings .write_to_file(bindings_out_path) .expect("Failed to write bindings"); }