use {
bindgen::builder,
std::{
env::var,
fs::{copy, create_dir_all, read_dir},
path::{Path, PathBuf},
},
};
const LINK_LIBS: &str = r#"
aws-c-cal
crypto
ssl
"#;
const INCLUDE_PATH: &str = "aws/cal";
const DEP_LIBRARIES: &str = r#"aws-c-common
aws-lc"#;
const FUNCTIONS: &str = r#"
aws_cal_library_init
aws_cal_library_clean_up
aws_ecc_key_pair_acquire
aws_ecc_key_pair_release
aws_ecc_key_pair_new_from_private_key
aws_ecc_key_pair_new_generate_random
aws_ecc_key_pair_new_from_public_key
aws_ecc_key_pair_new_from_asn1
aws_ecc_key_new_from_hex_coordinates
aws_ecc_key_pair_derive_public_key
aws_ecc_curve_name_from_oid
aws_ecc_oid_from_curve_name
aws_ecc_key_pair_sign_message
aws_ecc_key_pair_verify_signature
aws_ecc_key_pair_signature_length
aws_ecc_key_pair_get_public_key
aws_ecc_key_pair_get_private_key
aws_ecc_key_coordinate_byte_size_from_curve_name
aws_sha256_new
aws_sha1_new
aws_md5_new
aws_hash_destroy
aws_hash_update
aws_hash_finalize
aws_md5_compute
aws_sha256_compute
aws_sha1_compute
aws_set_md5_new_fn
aws_set_sha256_new_fn
aws_set_sha1_new_fn
aws_sha256_hmac_new
aws_hmac_destroy
aws_hmac_update
aws_hmac_finalize
aws_sha256_hmac_compute
aws_set_sha256_hmac_new_fn
"#;
const TYPES: &str = r#"
aws_cal_errors
aws_cal_log_subject
aws_ecc_curve_name
aws_ecc_key_pair_destroy_fn
aws_ecc_key_pair_sign_message_fn
aws_ecc_key_pair_derive_public_key_fn
aws_ecc_key_pair_verify_signature_fn
aws_ecc_key_pair_signature_length_fn
aws_ecc_key_pair_vtable
aws_ecc_key_pair
aws_hash_vtable
aws_hash
aws_hash_new_fn
aws_hmac_vtable
aws_hmac
aws_hmac_new_fn
"#;
const VARS: &str = "";
fn get_include_dir<P: AsRef<Path>>(dir: P) -> PathBuf {
let mut result = PathBuf::from(dir.as_ref());
for folder in INCLUDE_PATH.split('/') {
result.push(folder);
}
result
}
fn main() {
let root = PathBuf::from(var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"));
let out_dir = PathBuf::from(var("OUT_DIR").expect("OUT_DIR not set"));
let src_include_dir = root.join("include");
let dst_include_dir = out_dir.join("include");
let src_lib_include_dir = get_include_dir(&src_include_dir);
let dst_lib_include_dir = get_include_dir(&dst_include_dir);
let src_include_dir_str = src_include_dir.to_string_lossy();
let dst_include_dir_str = dst_include_dir.to_string_lossy();
let src_lib_include_dir_str = src_lib_include_dir.to_string_lossy();
let dst_lib_include_dir_str = dst_lib_include_dir.to_string_lossy();
println!("cargo:include={dst_include_dir_str}");
println!("cargo:rerun-if-changed=include");
println!("cargo:rerun-if-env-changed=AWS_CRT_PREFIX");
if let Ok(aws_crt_prefix) = var("AWS_CRT_PREFIX") {
println!("cargo:rustc-link-search={aws_crt_prefix}/lib");
}
for library_name in LINK_LIBS.split('\n') {
let library_name = library_name.trim();
if !library_name.is_empty() {
println!("cargo:rustc-link-lib={library_name}");
}
}
create_dir_all(&dst_lib_include_dir)
.unwrap_or_else(|e| panic!("Unable to create directory {dst_lib_include_dir_str}: {e}"));
let mut builder = builder()
.clang_arg(format!("-I{src_include_dir_str}"))
.derive_debug(true)
.derive_default(true)
.derive_partialeq(true)
.derive_eq(true)
.allowlist_recursively(false);
for dep in DEP_LIBRARIES.split('\n') {
let dep = dep.trim();
if dep.is_empty() {
continue;
}
let dep = String::from(dep).replace('-', "_").to_uppercase();
let dep_include_dir = PathBuf::from(var(format!("DEP_{dep}_INCLUDE")).unwrap_or_else(|_| panic!("DEP_{dep}_INCLUDE not set")));
let dep_include_dir_str = dep_include_dir.to_string_lossy();
builder = builder.clang_arg(format!("-I{dep_include_dir_str}"));
}
let mut n_includes = 0;
for entry in read_dir(&src_lib_include_dir)
.unwrap_or_else(|e| panic!("Unable to list header files in {src_lib_include_dir_str}: {e}"))
{
let entry =
entry.unwrap_or_else(|e| panic!("Unable to read directory entry in {src_lib_include_dir_str}: {e}"));
let file_name_string = entry.file_name();
let src_path = src_lib_include_dir.join(&file_name_string);
let src_path_str = src_path.to_string_lossy();
let dst_path = dst_lib_include_dir.join(&file_name_string);
if entry.file_type().unwrap_or_else(|e| panic!("Unable to read file type of {src_path_str}: {e}")).is_file() {
let file_name_utf8 = file_name_string.to_str().expect("Unable to convert file name to UTF-8");
if file_name_utf8.ends_with(".h") {
builder = builder.header(src_path_str.to_string());
n_includes += 1;
}
copy(&src_path, &dst_path).unwrap_or_else(|e| {
panic!(
"Failed to copy from {src_path_str} to {dst_path_str}: {e}",
dst_path_str = dst_path.to_string_lossy()
)
});
}
}
if n_includes == 0 {
panic!("No header files found in {src_lib_include_dir_str}");
}
for function_pattern in FUNCTIONS.split('\n') {
let function_pattern = function_pattern.trim();
if !function_pattern.is_empty() {
builder = builder.allowlist_function(function_pattern);
}
}
for type_pattern in TYPES.split('\n') {
let type_pattern = type_pattern.trim();
if !type_pattern.is_empty() {
builder = builder.allowlist_type(type_pattern);
}
}
for var_pattern in VARS.split('\n') {
let var_pattern = var_pattern.trim();
if !var_pattern.is_empty() {
builder = builder.allowlist_var(var_pattern);
}
}
let bindings_filename = out_dir.join("bindings.rs");
let bindings = builder.generate().expect("Unable to generate bindings");
bindings.write_to_file(&bindings_filename).unwrap_or_else(|e| {
panic!(
"Failed to write bindings to {bindings_filename_str}: {e}",
bindings_filename_str = bindings_filename.to_string_lossy()
)
});
if cfg!(any(target_os = "ios", target_os = "macos")) {
println!("cargo:rustc-link-arg=-framework");
println!("cargo:rustc-link-arg=CoreFoundation");
}
}