From e5396b12de13899dd9469681def872494ba51ec2 Mon Sep 17 00:00:00 2001 From: Evan Pratten Date: Thu, 13 Apr 2023 11:37:32 -0400 Subject: [PATCH] wip struct wrapping --- Cargo.toml | 4 +++ build/bind.rs | 1 + build/main.rs | 14 ++++++-- build/wrap/mod.rs | 5 ++- build/wrap/profiling.rs | 26 ++++++++++++++ build/wrap/raylib_api.rs | 16 +++++++++ build/wrap/structs.rs | 35 +++++++++++++++++++ build/wrap/type_xlat.rs | 73 ++++++++++++++++++++++++++++++++++++++++ 8 files changed, 170 insertions(+), 4 deletions(-) create mode 100644 build/wrap/profiling.rs create mode 100644 build/wrap/structs.rs create mode 100644 build/wrap/type_xlat.rs diff --git a/Cargo.toml b/Cargo.toml index 5d3f1dd..2a43c7b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,10 @@ exclude = [ ] build = "build/main.rs" +[dependencies] +profiling = "^1" +libc = "^0.2" + [build-dependencies] bindgen = "^0.65.0" cmake = "^0.1.49" diff --git a/build/bind.rs b/build/bind.rs index ea41907..773ff90 100644 --- a/build/bind.rs +++ b/build/bind.rs @@ -77,6 +77,7 @@ pub fn generate_bindings(header_file: &str) { let mut builder = bindgen::Builder::default() .header(header_file) .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + .wrap_unsafe_ops(true) .blocklist_item("DEG2RAD") .blocklist_item("PI") .blocklist_item("RAD2DEG") diff --git a/build/main.rs b/build/main.rs index facf5b1..68f3fcf 100644 --- a/build/main.rs +++ b/build/main.rs @@ -1,4 +1,7 @@ -use crate::wrap::{raylib_api::RayLibApiDefinition, enums::wrap_exposed_enums, colors::wrap_default_colors}; +use crate::wrap::{ + colors::wrap_default_colors, enums::wrap_exposed_enums, profiling::auto_profile_exported_fns, + raylib_api::RayLibApiDefinition, structs::wrap_exposed_structs, +}; mod bind; mod wrap; @@ -17,9 +20,14 @@ pub fn main() { bind::generate_bindings("src/wrapper.h"); // Load the API definitions - let api_defs = RayLibApiDefinition::load("third_party/raylib/parser/output/raylib_api.json").unwrap(); + let api_defs = + RayLibApiDefinition::load("third_party/raylib/parser/output/raylib_api.json").unwrap(); // Generate safe wrappers wrap_exposed_enums(api_defs.clone()); - wrap_default_colors(api_defs); + wrap_default_colors(api_defs.clone()); + wrap_exposed_structs(api_defs); + + // Make everything profile-able + // auto_profile_exported_fns(&format!("{}/bindings.rs", std::env::var("OUT_DIR").unwrap())); } diff --git a/build/wrap/mod.rs b/build/wrap/mod.rs index d333496..c4319cd 100644 --- a/build/wrap/mod.rs +++ b/build/wrap/mod.rs @@ -1,3 +1,6 @@ pub mod raylib_api; +pub mod type_xlat; pub mod enums; -pub mod colors; \ No newline at end of file +pub mod colors; +pub mod structs; +pub mod profiling; \ No newline at end of file diff --git a/build/wrap/profiling.rs b/build/wrap/profiling.rs new file mode 100644 index 0000000..b402e61 --- /dev/null +++ b/build/wrap/profiling.rs @@ -0,0 +1,26 @@ +use std::{fs::File, io::{Read, Write}}; + +use regex::Regex; + +/// Finds all `pub fn` in a file and wraps them with `#[profiling::function]` +pub fn auto_profile_exported_fns(rust_file_path: &str) { + // Open the file + let mut file = File::open(rust_file_path).unwrap(); + let mut contents = String::new(); + file.read_to_string(&mut contents).unwrap(); + + // Find all `pub fn` and wrap them with `#[profiling::function]` + let exported_fn_re = Regex::new(r"\s{4}pub fn").unwrap(); + let mut new_contents = String::new(); + for line in contents.lines() { + if exported_fn_re.is_match(line) { + new_contents.push_str(&format!(" #[profiling::function]\n{}\n", line)); + } else { + new_contents.push_str(&format!("{}\n", line)); + } + } + + // Re-write the file + let mut file = File::create(rust_file_path).unwrap(); + file.write_all(new_contents.as_bytes()).unwrap(); +} diff --git a/build/wrap/raylib_api.rs b/build/wrap/raylib_api.rs index 646e1bc..bf63512 100644 --- a/build/wrap/raylib_api.rs +++ b/build/wrap/raylib_api.rs @@ -18,6 +18,21 @@ pub struct EnumVariant { pub value: u32, } +#[derive(Debug, Clone, serde::Deserialize)] +pub struct StructureMember { + pub name: String, + #[serde(rename = "type")] + pub kind: String, + pub description: String, +} + +#[derive(Debug, Clone, serde::Deserialize)] +pub struct Structure { + pub name: String, + pub description: String, + pub fields: Vec, +} + #[derive(Debug, Clone, serde::Deserialize)] pub struct Enum { pub name: String, @@ -29,6 +44,7 @@ pub struct Enum { pub struct RayLibApiDefinition { pub defines: Vec, pub enums: Vec, + pub structs: Vec, } impl RayLibApiDefinition { diff --git a/build/wrap/structs.rs b/build/wrap/structs.rs new file mode 100644 index 0000000..d479510 --- /dev/null +++ b/build/wrap/structs.rs @@ -0,0 +1,35 @@ +use super::{raylib_api::RayLibApiDefinition, type_xlat::translate_c_type_to_rust}; + + +pub fn wrap_exposed_structs(api_defs: RayLibApiDefinition) { + // Allocate an output buffer for lines + let mut lines = Vec::new(); + + // Handle each struct + for st in api_defs.structs { + // Write a doc comment with raylib's provided struct description + lines.push("".to_string()); + lines.push(format!("/// {}", st.description)); + + // Write the struct definition + lines.push(format!("#[repr(C)]")); + lines.push(format!("pub struct {} {{", st.name)); + + // Write each field + for field in st.fields { + // Write a doc comment with raylib's provided field description + lines.push(format!(" /// {}", field.description)); + + // Write the field definition + lines.push(format!(" pub {}: {},", field.name, translate_c_type_to_rust(&field.kind))); + } + + // Close the struct definition + lines.push(format!("}}")); + } + + // Write the output file + let out_dir = std::env::var("OUT_DIR").unwrap(); + let out_path = format!("{}/structs.rs", out_dir); + std::fs::write(out_path, lines.join("\n")).unwrap(); +} diff --git a/build/wrap/type_xlat.rs b/build/wrap/type_xlat.rs new file mode 100644 index 0000000..8d6814a --- /dev/null +++ b/build/wrap/type_xlat.rs @@ -0,0 +1,73 @@ +pub fn translate_c_type_to_rust(c_type: &str) -> String { + // match c_type { + // "float" => "f32".to_string(), + // "float *" => "std::ptr::null_mut()".to_string(), + // "int" => "i32".to_string(), + // "unsigned int" => "u32".to_string(), + // "unsigned char *" => "std::ptr::null_mut()".to_string(), + // "unsigned short *" => "std::ptr::null_mut()".to_string(), + // "bool" => "bool".to_string(), + // "void *" => "std::ptr::null_mut()".to_string(), + // "Texture" => "crate::structs::Texture".to_string(), + // "Rectangle" => "crate::structs::Rectangle".to_string(), + // "Rectangle *" => "std::ptr::null_mut()".to_string(), + // "Image" => "crate::structs::Image".to_string(), + // "Texture2D" => "crate::structs::Texture2D".to_string(), + // "GlyphInfo *" => "std::ptr::null_mut()".to_string(), + // "Vector3" => "crate::structs::Vector3".to_string(), + // "Vector2" => "crate::structs::Vector2".to_string(), + // "Color" => "crate::structs::Color".to_string(), + // "float[4]" => "[f32; 4]".to_string(), + // "MaterialMap *" => "std::ptr::null_mut()".to_string(), + // "Quaternion" => "crate::structs::Quaternion".to_string(), + // "char[32]" => "[u8; 32]".to_string(), + // _ => {panic!("Unknown C type: {}", c_type)} + // } + + // Translate basic C types to their Rust equivalents + if let Some(ty) = match c_type { + "char" => Some("i8"), + "unsigned char" => Some("u8"), + "short" => Some("i16"), + "unsigned short" => Some("u16"), + "int" => Some("i32"), + "unsigned int" => Some("u32"), + "long" => Some("i64"), + "unsigned long" => Some("u64"), + "float" => Some("f32"), + "double" => Some("f64"), + "bool" => Some("bool"), + "char *" => Some("String"), + "void *" => Some("*mut libc::c_void"), + _ => None, + } { + return ty.to_string(); + } + + // For some reason, an internal data type is exposed. + if c_type == "rAudioBuffer *" || c_type == "rAudioProcessor *" { + return "std::ptr::null_mut()".to_string(); + } + + + // If the type ends with a *, it's a pointer to an array of the type without the * + if c_type.ends_with("*") { + let ty = &c_type[..c_type.len() - 1].trim(); + return format!("Vec<{}>", translate_c_type_to_rust(ty)); + } + + // If the type ends with `[N]`, it's an array of N elements of the type without the `[N]` + let arr_len_re = regex::Regex::new(r"\[(\d+)\]$").unwrap(); + if let Some(caps) = arr_len_re.captures(c_type) { + let ty = &c_type[..c_type.len() - caps[0].len()].trim(); + let len = &caps[1]; + return format!("[{}; {}]", translate_c_type_to_rust(ty), len); + } + + // Uppercase types are assumed to be structs + if c_type.chars().next().unwrap().is_uppercase() { + return format!("crate::structs::{}", c_type); + } + + panic!("Unknown C type: {}", c_type) +}