use std::{
env,
error::Error,
fs,
path::{Path, PathBuf},
};
fn main() -> Result<(), Box<dyn Error>> {
let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
let vendor_rbs = manifest_dir.join("vendor/rbs");
let include = vendor_rbs.join("include");
let c_src = vendor_rbs.join("src");
build(&include, &c_src)?;
let bindings = generate_bindings(&include)?;
write_bindings(&bindings)?;
Ok(())
}
fn build(include_dir: &Path, src_dir: &Path) -> Result<(), Box<dyn Error>> {
let mut build = cc::Build::new();
build.include(include_dir);
build.flag_if_supported("-Wno-unused-parameter");
build.files(source_files(src_dir)?);
build.try_compile("rbs")?;
Ok(())
}
fn source_files<P: AsRef<Path>>(root_dir: P) -> Result<Vec<String>, Box<dyn Error>> {
let mut files = Vec::new();
for entry in fs::read_dir(root_dir.as_ref()).map_err(|e| {
format!(
"Failed to read source directory {:?}: {e}",
root_dir.as_ref()
)
})? {
let entry = entry.map_err(|e| format!("Failed to read directory entry: {e}"))?;
let path = entry.path();
if path.is_file() {
let path_str = path
.to_str()
.ok_or_else(|| format!("Invalid UTF-8 in filename: {path:?}"))?;
if Path::new(path_str)
.extension()
.is_some_and(|ext| ext.eq_ignore_ascii_case("c"))
{
files.push(path_str.to_string());
}
} else if path.is_dir() {
files.extend(source_files(path)?);
}
}
Ok(files)
}
fn generate_bindings(include_path: &Path) -> Result<bindgen::Bindings, Box<dyn Error>> {
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.clang_arg(format!("-I{}", include_path.display()))
.parse_callbacks(Box::new(bindgen::CargoCallbacks::new()))
.generate_comments(true)
.allowlist_type("rbs_ast_annotation_t")
.allowlist_type("rbs_ast_bool_t")
.allowlist_type("rbs_ast_comment_t")
.allowlist_type("rbs_ast_declarations_class_alias_t")
.allowlist_type("rbs_ast_declarations_class_super_t")
.allowlist_type("rbs_ast_declarations_class_t")
.allowlist_type("rbs_ast_declarations_constant_t")
.allowlist_type("rbs_ast_declarations_global_t")
.allowlist_type("rbs_ast_declarations_interface_t")
.allowlist_type("rbs_ast_declarations_module_alias_t")
.allowlist_type("rbs_ast_declarations_module_self_t")
.allowlist_type("rbs_ast_declarations_module_t")
.allowlist_type("rbs_ast_declarations_type_alias_t")
.allowlist_type("rbs_ast_directives_use_single_clause_t")
.allowlist_type("rbs_ast_directives_use_t")
.allowlist_type("rbs_ast_directives_use_wildcard_clause_t")
.allowlist_type("rbs_ast_integer_t")
.allowlist_type("rbs_attr_ivar_name_t")
.allowlist_type("rbs_ast_members_alias_t")
.allowlist_type("rbs_ast_members_attr_accessor_t")
.allowlist_type("rbs_ast_members_attr_reader_t")
.allowlist_type("rbs_ast_members_attr_writer_t")
.allowlist_type("rbs_ast_members_class_instance_variable_t")
.allowlist_type("rbs_ast_members_class_variable_t")
.allowlist_type("rbs_ast_members_extend_t")
.allowlist_type("rbs_ast_members_include_t")
.allowlist_type("rbs_ast_members_instance_variable_t")
.allowlist_type("rbs_ast_members_method_definition_overload_t")
.allowlist_type("rbs_ast_members_method_definition_t")
.allowlist_type("rbs_ast_members_prepend_t")
.allowlist_type("rbs_ast_members_private_t")
.allowlist_type("rbs_ast_members_public_t")
.allowlist_type("rbs_ast_ruby_annotations_class_alias_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_colon_method_type_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_instance_variable_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_method_types_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_module_alias_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_node_type_assertion_t")
.allowlist_type("rbs_ast_ruby_annotations_return_type_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_skip_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_type_application_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_block_param_type_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_param_type_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_splat_param_type_annotation_t")
.allowlist_type("rbs_ast_ruby_annotations_double_splat_param_type_annotation_t")
.allowlist_type("rbs_ast_string_t")
.allowlist_type("rbs_ast_symbol_t")
.allowlist_type("rbs_ast_type_param_t")
.allowlist_type("rbs_encoding_t")
.allowlist_type("rbs_encoding_type_t")
.allowlist_type("rbs_location_range")
.allowlist_type("rbs_location_range_list_t")
.allowlist_type("rbs_location_range_list_node_t")
.allowlist_type("rbs_method_type_t")
.allowlist_type("rbs_namespace_t")
.allowlist_type("rbs_node_list_t")
.allowlist_type("rbs_signature_t")
.allowlist_type("rbs_string_t")
.allowlist_type("rbs_type_name_t")
.allowlist_type("rbs_types_alias_t")
.allowlist_type("rbs_types_bases_any_t")
.allowlist_type("rbs_types_bases_bool_t")
.allowlist_type("rbs_types_bases_bottom_t")
.allowlist_type("rbs_types_bases_class_t")
.allowlist_type("rbs_types_bases_instance_t")
.allowlist_type("rbs_types_bases_nil_t")
.allowlist_type("rbs_types_bases_self_t")
.allowlist_type("rbs_types_bases_top_t")
.allowlist_type("rbs_types_bases_void_t")
.allowlist_type("rbs_types_block_t")
.allowlist_type("rbs_types_class_instance_t")
.allowlist_type("rbs_types_class_singleton_t")
.allowlist_type("rbs_types_function_param_t")
.allowlist_type("rbs_types_function_t")
.allowlist_type("rbs_types_interface_t")
.allowlist_type("rbs_types_intersection_t")
.allowlist_type("rbs_types_literal_t")
.allowlist_type("rbs_types_optional_t")
.allowlist_type("rbs_types_proc_t")
.allowlist_type("rbs_types_record_field_type_t")
.allowlist_type("rbs_types_record_t")
.allowlist_type("rbs_types_tuple_t")
.allowlist_type("rbs_types_union_t")
.allowlist_type("rbs_types_untyped_function_t")
.allowlist_type("rbs_types_variable_t")
.constified_enum_module("rbs_alias_kind")
.constified_enum_module("rbs_attribute_kind")
.constified_enum_module("rbs_attribute_visibility")
.constified_enum_module("rbs_encoding_type_t")
.constified_enum_module("rbs_attr_ivar_name_tag")
.constified_enum_module("rbs_method_definition_kind")
.constified_enum_module("rbs_method_definition_visibility")
.constified_enum_module("rbs_node_type")
.constified_enum_module("rbs_type_param_variance")
.allowlist_var("rbs_encodings")
.allowlist_function("rbs_parse_signature")
.allowlist_function("rbs_parser_free")
.allowlist_function("rbs_parser_new")
.allowlist_function("rbs_string_new")
.allowlist_function("rbs_location_range_list_new")
.allowlist_function("rbs_location_range_list_append")
.allowlist_function("rbs_constant_pool_free")
.allowlist_function("rbs_constant_pool_id_to_constant")
.allowlist_function("rbs_constant_pool_init")
.generate()
.map_err(|_| "Unable to generate rbs bindings")?;
Ok(bindings)
}
fn write_bindings(bindings: &bindgen::Bindings) -> Result<(), Box<dyn Error>> {
let out_path = PathBuf::from(
env::var("OUT_DIR").map_err(|e| format!("OUT_DIR environment variable not set: {e}"))?,
);
bindings
.write_to_file(out_path.join("bindings.rs"))
.map_err(|e| {
format!(
"Failed to write bindings to {:?}: {e}",
out_path.join("bindings.rs")
)
})?;
Ok(())
}