diff --git a/README.md b/README.md index 5cb34ba..868e507 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Install on `Cargo.toml` as `build-dependencies` and set up `bindgen::Builder` on ```toml [build-dependencies] -csbindgen = "1.3.0" +csbindgen = "1.4.0" ``` ### Rust to C#. @@ -192,6 +192,15 @@ namespace {csharp_namespace} `csharp_dll_name_if` is optional. If specified, `#if` allows two DllName to be specified, which is useful if the name must be `__Internal` at iOS build. +`input_extern_file` allows mulitple call, if you need to add dependent struct, use this. + +```rust +csbindgen::Builder::default() + .input_extern_file("src/lib.rs") + .input_extern_file("src/struct_modules.rs") + .generate_csharp_file("../dotnet-sandbox/NativeMethods.cs"); +``` + ### Unity Callback `csharp_use_function_pointer` configures how generate function pointer. The default is to generate a `delegate*`, but Unity does not support it; setting it to `false` will generate a `Func/Action` that can be used with `MonoPInvokeCallback`. diff --git a/csbindgen-tests/build.rs b/csbindgen-tests/build.rs index c72ef29..99575c2 100644 --- a/csbindgen-tests/build.rs +++ b/csbindgen-tests/build.rs @@ -32,10 +32,10 @@ fn main() -> Result<(), Box> { // .write_to_file("src/quiche.rs")?; - bindgen::Builder::default() - .header("c/sqlite3/sqlite3.h") - .generate()? - .write_to_file("src/sqlite3.rs")?; + // bindgen::Builder::default() + // .header("c/sqlite3/sqlite3.h") + // .generate()? + // .write_to_file("src/sqlite3.rs")?; // bindgen::Builder::default() // .header("c/bullet3/PhysicsClientC_API.h") @@ -66,21 +66,21 @@ fn main() -> Result<(), Box> { .unwrap(); - csbindgen::Builder::default() - .input_bindgen_file("src/sqlite3.rs") - .method_filter(|x| x.starts_with("sqlite3_")) - .rust_method_prefix("csbindgen_") - .rust_file_header("use super::sqlite3::*;") - // .rust_method_type_path("sqlite3") - .csharp_class_name("LibSqlite3") - .csharp_namespace("CsBindgen") - .csharp_dll_name("csbindgen_tests") - .csharp_dll_name_if("UNITY_IOS && !UNITY_EDITOR", "__Internal") - .csharp_entry_point_prefix("csbindgen_") - .csharp_method_prefix("") - .csharp_class_accessibility("public") - .generate_to_file("src/sqlite3_ffi.rs", "../dotnet-sandbox/sqlite3_bindgen.cs") - .unwrap(); + // csbindgen::Builder::default() + // .input_bindgen_file("src/sqlite3.rs") + // .method_filter(|x| x.starts_with("sqlite3_")) + // .rust_method_prefix("csbindgen_") + // .rust_file_header("use super::sqlite3::*;") + // // .rust_method_type_path("sqlite3") + // .csharp_class_name("LibSqlite3") + // .csharp_namespace("CsBindgen") + // .csharp_dll_name("csbindgen_tests") + // .csharp_dll_name_if("UNITY_IOS && !UNITY_EDITOR", "__Internal") + // .csharp_entry_point_prefix("csbindgen_") + // .csharp_method_prefix("") + // .csharp_class_accessibility("public") + // .generate_to_file("src/sqlite3_ffi.rs", "../dotnet-sandbox/sqlite3_bindgen.cs") + // .unwrap(); csbindgen::Builder::default() .input_extern_file("src/lib.rs") @@ -92,29 +92,29 @@ fn main() -> Result<(), Box> { .generate_csharp_file("../dotnet-sandbox/NativeMethods.cs") .unwrap(); - csbindgen::Builder::new() - .input_bindgen_file("src/zstd.rs") - .method_filter(|x| x.starts_with("ZSTD_")) - .rust_file_header("use super::zstd::*;") - .csharp_class_name("LibZstd") - .csharp_dll_name("libzsd") - .generate_to_file("src/zstd_ffi.rs", "../dotnet-sandbox/zstd_bindgen.cs")?; + // csbindgen::Builder::new() + // .input_bindgen_file("src/zstd.rs") + // .method_filter(|x| x.starts_with("ZSTD_")) + // .rust_file_header("use super::zstd::*;") + // .csharp_class_name("LibZstd") + // .csharp_dll_name("libzsd") + // .generate_to_file("src/zstd_ffi.rs", "../dotnet-sandbox/zstd_bindgen.cs")?; - csbindgen::Builder::new() - .input_bindgen_file("src/quiche.rs") - .method_filter(|x| x.starts_with("quiche_")) - .rust_file_header("use super::quiche::*;") - .csharp_class_name("LibQuiche") - .csharp_dll_name("libquiche") - .generate_to_file("src/quiche_ffi.rs", "../dotnet-sandbox/quiche_bindgen.cs")?; + // csbindgen::Builder::new() + // .input_bindgen_file("src/quiche.rs") + // .method_filter(|x| x.starts_with("quiche_")) + // .rust_file_header("use super::quiche::*;") + // .csharp_class_name("LibQuiche") + // .csharp_dll_name("libquiche") + // .generate_to_file("src/quiche_ffi.rs", "../dotnet-sandbox/quiche_bindgen.cs")?; - csbindgen::Builder::new() - .input_bindgen_file("src/bullet3.rs") - .method_filter(|x| x.starts_with("b3")) - .rust_file_header("use super::bullet3::*;") - .csharp_class_name("LibBullet3") - .csharp_dll_name("libbullet3") - .generate_to_file("src/bullet3_ffi.rs", "../dotnet-sandbox/bullet3_bindgen.cs")?; + // csbindgen::Builder::new() + // .input_bindgen_file("src/bullet3.rs") + // .method_filter(|x| x.starts_with("b3")) + // .rust_file_header("use super::bullet3::*;") + // .csharp_class_name("LibBullet3") + // .csharp_dll_name("libbullet3") + // .generate_to_file("src/bullet3_ffi.rs", "../dotnet-sandbox/bullet3_bindgen.cs")?; Ok(()) } diff --git a/csbindgen-tests/src/lib.rs b/csbindgen-tests/src/lib.rs index 479e68b..74d34a7 100644 --- a/csbindgen-tests/src/lib.rs +++ b/csbindgen-tests/src/lib.rs @@ -3,8 +3,6 @@ use std::{ ffi::{c_char, c_long, c_ulong, c_void, CString}, }; - - #[allow(dead_code)] #[allow(non_snake_case)] #[allow(non_camel_case_types)] @@ -60,31 +58,48 @@ mod lz4_ffi; // #[allow(non_camel_case_types)] // mod zstd_ffi; -mod others; -pub use others::HogeMoge; +// mod others; +// pub use others::HogeMoge; + +// #[no_mangle] +// pub extern "C" fn other_1(hoge: HogeMoge) { +// println!("{:?}", hoge); +// } + +#[repr(C)] +pub struct NfcCard { + pub delegate: unsafe extern "C" fn(ByteArray) -> ByteArray +} #[no_mangle] -pub extern "C" fn other_1(hoge: HogeMoge){ - println!("{}", hoge); +pub extern "C" fn other_2(_hoge: NfcCard) {} + +#[repr(C)] +pub struct ByteArray { + pub i: i32, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct event { + pub a: i32 +} + +#[no_mangle] +pub extern "C" fn event(event: event ) { + println!("{:?}", event); } #[no_mangle] pub extern "C" fn nest_test( _f: ::std::option::Option< unsafe extern "C" fn( - pxFunc: *mut ::std::option::Option< - unsafe extern "C" fn( - arg2: ::std::os::raw::c_int, - ), - >, + pxFunc: *mut ::std::option::Option, ) -> ::std::os::raw::c_int, - > + >, ) { } - - - #[allow(non_camel_case_types)] pub type LONG_PTR = ::std::os::raw::c_longlong; #[allow(non_camel_case_types)] diff --git a/csbindgen-tests/src/others.rs b/csbindgen-tests/src/others.rs index 82d6950..d311c91 100644 --- a/csbindgen-tests/src/others.rs +++ b/csbindgen-tests/src/others.rs @@ -6,4 +6,5 @@ pub enum HogeMoge{ X = 0, Y = 1, + event = 2 } \ No newline at end of file diff --git a/csbindgen/Cargo.toml b/csbindgen/Cargo.toml index 0e3b072..d2acd4e 100644 --- a/csbindgen/Cargo.toml +++ b/csbindgen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "csbindgen" -version = "1.3.0" +version = "1.4.0" edition = "2021" authors = [ "Yoshifumi Kawai ", diff --git a/csbindgen/src/emitter.rs b/csbindgen/src/emitter.rs index d456e32..c616375 100644 --- a/csbindgen/src/emitter.rs +++ b/csbindgen/src/emitter.rs @@ -107,7 +107,12 @@ pub fn emit_csharp( let mut method_list_string = String::new(); for item in methods { - let method_name = &item.method_name; + let mut method_name = &item.method_name; + let method_name_temp: String; + if method_prefix.is_empty() { + method_name_temp = escape_name(method_name); + method_name = &method_name_temp; + } if let Some(x) = &item.return_type { if let Some(delegate_method) = build_method_delegate_if_required( @@ -165,7 +170,7 @@ pub fn emit_csharp( type_name = "[MarshalAs(UnmanagedType.U1)] bool".to_string(); } - format!("{} {}", type_name, p.escape_name()) + format!("{} {}", type_name, escape_name(p.name.as_str())) }) .collect::>() .join(", "); @@ -189,7 +194,7 @@ pub fn emit_csharp( let mut structs_string = String::new(); for item in structs { - let name = &item.struct_name; + let name = escape_name(&item.struct_name); let layout_kind = if item.is_union { "Explicit" } else { @@ -220,7 +225,7 @@ pub fn emit_csharp( }; structs_string - .push_str(format!(" {}public {} {}", attr, type_name, field.name).as_str()); + .push_str(format!(" {}public {} {}", attr, type_name, escape_name(field.name.as_str())).as_str()); if let TypeKind::FixedArray(digits, _) = &field.rust_type.type_kind { let mut digits = digits.clone(); if digits == "0" { diff --git a/csbindgen/src/type_meta.rs b/csbindgen/src/type_meta.rs index 3d74bb7..c01aee0 100644 --- a/csbindgen/src/type_meta.rs +++ b/csbindgen/src/type_meta.rs @@ -1,31 +1,30 @@ use crate::{alias_map::AliasMap, builder::BindgenOptions}; +pub fn escape_name(str: &str) -> String { + match str { + // C# keywords: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ + "abstract" | "as" | "base" | "bool" | "break" | "byte" | "case" | "catch" | "char" + | "checked" | "class" | "const" | "continue" | "decimal" | "default" | "delegate" + | "do" | "double" | "else" | "enum" | "event" | "explicit" | "extern" | "false" + | "finally" | "fixed" | "float" | "for" | "foreach" | "goto" | "if" | "implicit" | "in" + | "int" | "interface" | "internal" | "is" | "lock" | "long" | "namespace" | "new" + | "null" | "object" | "operator" | "out" | "override" | "params" | "private" + | "protected" | "public" | "readonly" | "ref" | "return" | "sbyte" | "sealed" | "short" + | "sizeof" | "stackalloc" | "static" | "string" | "struct" | "switch" | "this" + | "throw" | "true" | "try" | "typeof" | "uint" | "ulong" | "unchecked" | "unsafe" + | "ushort" | "using" | "virtual" | "void" | "volatile" | "while" => { + "@".to_string() + str + } + x => x.to_string(), + } +} + #[derive(Clone, Debug)] pub struct Parameter { pub name: String, pub rust_type: RustType, } -impl Parameter { - pub fn escape_name(&self) -> String { - match self.name.as_str() { - // C# keywords: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/ - "abstract" | "as" | "base" | "bool" | "break" | "byte" | "case" | "catch" | "char" - | "checked" | "class" | "const" | "continue" | "decimal" | "default" | "delegate" - | "do" | "double" | "else" | "enum" | "event" | "explicit" | "extern" | "false" - | "finally" | "fixed" | "float" | "for" | "foreach" | "goto" | "if" | "implicit" - | "in" | "int" | "interface" | "internal" | "is" | "lock" | "long" | "namespace" - | "new" | "null" | "object" | "operator" | "out" | "override" | "params" - | "private" | "protected" | "public" | "readonly" | "ref" | "return" | "sbyte" - | "sealed" | "short" | "sizeof" | "stackalloc" | "static" | "string" | "struct" - | "switch" | "this" | "throw" | "true" | "try" | "typeof" | "uint" | "ulong" - | "unchecked" | "unsafe" | "ushort" | "using" | "virtual" | "void" | "volatile" - | "while" => "@".to_string() + self.name.as_str(), - x => x.to_string(), - } - } -} - #[derive(Clone, Debug)] pub struct FieldMember { pub name: String, @@ -153,7 +152,7 @@ impl RustType { .map(|x| { format!( "{}: {}", - x.escape_name(), + escape_name(x.name.as_str()), x.rust_type.to_rust_string(type_path) ) }) @@ -184,7 +183,8 @@ impl RustType { method_name: &String, parameter_name: &String, ) -> String { - fn convert_type_name(type_name: &str) -> &str { + fn convert_type_name(type_name: &str) -> String { + let temp_string: String; let name = match type_name { // std::os::raw https://doc.rust-lang.org/std/os/raw/index.html // std::ffi::raw https://doc.rust-lang.org/core/ffi/index.html @@ -220,9 +220,12 @@ impl RustType { "bool" => "bool", "usize" => "nuint", // C# 9.0 "()" => "void", - _ => type_name, // as is + _ => { + temp_string = escape_name(type_name); + temp_string.as_str() + } }; - name + name.to_string() } // resolve alias @@ -233,7 +236,13 @@ impl RustType { // if alias if Option, unwrap. let type_csharp_string = if use_alias { - use_type.to_csharp_string(options, alias_map, emit_from_struct, method_name, parameter_name) + use_type.to_csharp_string( + options, + alias_map, + emit_from_struct, + method_name, + parameter_name, + ) } else { convert_type_name(use_type.type_name.as_str()).to_string() }; @@ -421,7 +430,7 @@ pub fn build_method_delegate_if_required( method_name, parameter_name, ); - format!("{} {}", cs, p.escape_name()) + format!("{} {}", cs, escape_name(p.name.as_str())) }) .collect::>() .join(", "); diff --git a/dotnet-sandbox/NativeMethods.cs b/dotnet-sandbox/NativeMethods.cs index ac66d26..f3ba96c 100644 --- a/dotnet-sandbox/NativeMethods.cs +++ b/dotnet-sandbox/NativeMethods.cs @@ -12,8 +12,11 @@ namespace CsBindgen { const string __DllName = "csbindgen_tests"; - [DllImport(__DllName, EntryPoint = "other_1", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] - public static extern void other_1(HogeMoge hoge); + [DllImport(__DllName, EntryPoint = "other_2", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void other_2(NfcCard _hoge); + + [DllImport(__DllName, EntryPoint = "@event", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] + public static extern void @event(@event @event); [DllImport(__DllName, EntryPoint = "nest_test", CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] public static extern void nest_test(delegate* unmanaged[Cdecl]*, int> _f); @@ -130,6 +133,24 @@ namespace CsBindgen } + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct NfcCard + { + public delegate* unmanaged[Cdecl] @delegate; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct ByteArray + { + public int i; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe partial struct @event + { + public int a; + } + [StructLayout(LayoutKind.Explicit)] internal unsafe partial struct MyUnion { @@ -176,12 +197,6 @@ namespace CsBindgen C = 10, } - internal enum HogeMoge : int - { - X = 0, - Y = 1, - } - } \ No newline at end of file