use std::{env, path::Path, process::Command};
#[cfg(all(not(windows), not(nobindgen)))]
fn create_bindings(include_path: &Path, home_dir: &Path) {
let hacl_includes = vec![
format!("-I{}", include_path.display()),
format!("-I{}", include_path.join("hacl").display()),
format!("-I{}", include_path.join("krml").display()),
format!("-I{}", include_path.join("vale").display()),
];
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.clang_args(hacl_includes.iter())
.allowlist_function("EverCrypt_AutoConfig2_.*")
.allowlist_function("EverCrypt_AEAD_.*")
.allowlist_function("EverCrypt_Curve25519_.*")
.allowlist_function("EverCrypt_Ed25519_.*")
.allowlist_function("EverCrypt_Hash_.*")
.allowlist_function("EverCrypt_HKDF_.*")
.allowlist_function("EverCrypt_HMAC_.*")
.allowlist_function("Hacl_P256_.*")
.allowlist_function("Hacl_RSAPSS_.*")
.allowlist_function("Hacl_SHA3_.*")
.allowlist_function("Hacl_Chacha20Poly1305_.*")
.allowlist_function("Hacl_Hash_.*")
.allowlist_function("Hacl_Streaming_.*")
.allowlist_function("Hacl_Blake2.*")
.allowlist_function("Hacl_Curve25519_.*")
.allowlist_function("Hacl_HKDF_.*")
.allowlist_function("Hacl_HMAC_.*")
.allowlist_function("Hacl_HMAC_DRBG_.*")
.allowlist_function("Hacl_Bignum64_.*")
.allowlist_function("Hacl_Ed25519_.*")
.allowlist_var("EverCrypt_Error_.*")
.allowlist_var("Spec_.*")
.allowlist_type("Spec_.*")
.allowlist_type("Hacl_Streaming_SHA2_state.*")
.allowlist_type("Hacl_Streaming_Keccak_state.*")
.allowlist_type("Hacl_HMAC_DRBG_.*")
.blocklist_type("EverCrypt_AEAD_state_s.*")
.blocklist_type("FStar_UInt128_uint128")
.blocklist_function("Hacl_Streaming_SHA2_update_last_384")
.blocklist_function("Hacl_Streaming_SHA2_update_last_512")
.blocklist_function("Hacl_Blake2b_32_blake2b_update_multi")
.blocklist_function("Hacl_Blake2b_32_blake2b_update_last")
.blocklist_function("Hacl_Blake2b_256_blake2b_update_multi")
.blocklist_function("Hacl_Blake2b_256_blake2b_update_last")
.layout_tests(false)
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.generate()
.expect("Unable to generate bindings");
let home_bindings = home_dir.join("src/bindings/bindings.rs");
bindings
.write_to_file(home_bindings)
.expect("Couldn't write bindings!");
}
#[cfg(any(windows, nobindgen))]
fn create_bindings(_: &Path, _: &Path) {}
fn build_hacl_c(path: &Path, cross_target: Option<String>) {
println!(" >>> Building HACL C in {}", path.display());
let mut cmake_cmd = Command::new("cmake");
let toolchain_file = cross_target
.map(|s| match s.as_str() {
"x86_64-apple-darwin" => "-DCMAKE_TOOLCHAIN_FILE=config/x64-darwin.cmake",
"aarch64-apple-darwin" => "-DCMAKE_TOOLCHAIN_FILE=config/aarch64-darwin.cmake",
_ => "",
})
.unwrap_or_default();
let cmake_status = cmake_cmd
.current_dir(path)
.args(&[
"-B",
"build",
"-G",
"Ninja",
"-D",
"CMAKE_BUILD_TYPE=Release",
toolchain_file,
])
.status()
.expect("Failed to run cmake.");
if !cmake_status.success() {
panic!("Failed to run cmake.")
}
let mut ninja_cmd = Command::new("ninja");
let ninja_status = ninja_cmd
.current_dir(path)
.args(&["-f", "build.ninja", "-C", "build"])
.status()
.expect("Failed to run ninja.");
if !ninja_status.success() {
panic!("Failed to run ninja.")
}
let install_path = path.join("build").join("installed");
println!(" >>> Installing HACL C into {}", install_path.display());
let mut cmake_cmd = Command::new("cmake");
let cmake_status = cmake_cmd
.current_dir(path)
.args(&[
"--install",
"build",
"--prefix",
install_path.to_str().unwrap(),
])
.status()
.expect("Failed to install C library.");
if !cmake_status.success() {
panic!("Failed to install C library.")
}
}
fn copy_hacl_to_out(out_dir: &Path) {
use fs_extra::{
dir::{copy, create_all, CopyOptions},
file,
};
let build_dir = out_dir.join("build");
create_all(&build_dir, true).unwrap();
let local_c_path = Path::new(".c");
let options = CopyOptions::new().overwrite(true);
copy(&local_c_path.join("config"), &out_dir, &options).unwrap();
copy(&local_c_path.join("src"), &out_dir, &options).unwrap();
copy(&local_c_path.join("vale"), &out_dir, &options).unwrap();
copy(&local_c_path.join("karamel"), &out_dir, &options).unwrap();
copy(&local_c_path.join("include"), &out_dir, &options).unwrap();
let options = file::CopyOptions::new().overwrite(true);
file::copy(
&local_c_path.join("config").join("default_config.cmake"),
&out_dir.join("build").join("config.cmake"),
&options,
)
.unwrap();
file::copy(
&local_c_path.join("CMakeLists.txt"),
out_dir.join("CMakeLists.txt"),
&options,
)
.unwrap();
}
fn main() {
let home_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
let home_dir = Path::new(&home_dir);
let mach_build = env::var("MACH_BUILD").ok().is_some();
let target = env::var("TARGET").unwrap();
let host = env::var("HOST").unwrap();
let out_dir = env::var("OUT_DIR").unwrap();
let out_dir = Path::new(&out_dir);
println!("mach_build: {}", mach_build);
let cross_target = if target != host { Some(target) } else { None };
let hacl_path = if !mach_build {
let c_out_dir = out_dir.join("c");
if !c_out_dir.join("build").join("installed").exists() {
println!(" >>> Copying HACL C file");
println!(" from {}", home_dir.join(".c").display());
println!(" to {}", c_out_dir.display());
copy_hacl_to_out(&c_out_dir);
}
build_hacl_c(&c_out_dir, cross_target);
c_out_dir.join("build").join("installed")
} else {
home_dir
.join("..")
.join("..")
.join("build")
.join("installed")
};
let hacl_lib_path = hacl_path.join("lib");
let hacl_include_path = hacl_path.join("include");
let library_name = "hacl_static";
println!("cargo:rerun-if-changed=build.rs");
create_bindings(&hacl_include_path, home_dir);
let mode = "static";
println!("cargo:rustc-link-lib={}={}", mode, library_name);
println!("cargo:rustc-link-search=native={}", hacl_lib_path.display());
println!("cargo:lib={}", hacl_lib_path.display());
}