mirror of
https://github.com/Sarsoo/csbindgen.git
synced 2024-12-23 15:06:26 +00:00
rm to
This commit is contained in:
parent
e1941c5519
commit
b71706ebf3
208
README.md
208
README.md
@ -3,3 +3,211 @@
|
|||||||
|
|
||||||
Generate C# FFI from Rust for brings C native library to .NET and Unity easily.
|
Generate C# FFI from Rust for brings C native library to .NET and Unity easily.
|
||||||
|
|
||||||
|
There are usually many pains involved in using the C Library with C#. Not only is it difficult to create bindings, but cross-platform builds are very difficult. In this day and age, you have to build for multiple platforms and architectures, windows, osx, linux, android, ios, each with x64, x86, arm.
|
||||||
|
|
||||||
|
[Rust](https://www.rust-lang.org/) has an excellent toolchain for cross-platform builds, as well as [cc crate](https://crates.io/crates/cc), [cmake crate](https://crates.io/crates/cmake) allow C source code to be integrated into the build. And [rust-bindgen](https://crates.io/crates/bindgen), which generates bindings from `.h`, is highly functional and very stable.
|
||||||
|
|
||||||
|
csbindgen can easily bring native C libraries into C# through Rust. csbindgen generates Rust extern code and C# DllImport code to work with C# from code generated from C by bindgen. With cc crate or cmake crate, C code is linked to the single rust native library.
|
||||||
|
|
||||||
|
Of course, you can also output pure FFI Rust code (or a wrapper layer to make it easier to bring C, C++ libraries into C#) to C#.
|
||||||
|
|
||||||
|
Getting Started
|
||||||
|
---
|
||||||
|
Install on `Cargo.toml` as `build-dependencies` and set up `bindgen::Builder` on `build.rs`.
|
||||||
|
|
||||||
|
```toml
|
||||||
|
[build-dependencies]
|
||||||
|
csbindgen = "0.1.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
### C (to Rust) to C#
|
||||||
|
|
||||||
|
For example, build [lz4](https://github.com/lz4/lz4) compression library.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// using bindgen, generate binding code
|
||||||
|
bindgen::Builder::default()
|
||||||
|
.header("c/lz4/lz4.h")
|
||||||
|
.generate().unwrap()
|
||||||
|
.write_to_file("lz4.rs").unwrap();
|
||||||
|
|
||||||
|
// using cc, build and link c code
|
||||||
|
cc::Build::new().file("lz4.c").compile("lz4");
|
||||||
|
|
||||||
|
// csbindgen code, generate both rust ffi and C# dll import
|
||||||
|
csbindgen::Builder::default()
|
||||||
|
.input_bindgen_file("lz4.rs") // read from bindgen generated code
|
||||||
|
.csharp_dll_name("liblz4")
|
||||||
|
.generate_to_file("lz4_ffi.rs", "../dotnet/NativeMethods.lz4.g.cs")
|
||||||
|
.unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
It will generates like these code.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// lz4_ffi.rs
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
|
use ::std::os::raw::*;
|
||||||
|
|
||||||
|
use super::lz4;
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn csbindgen_LZ4_compress_default(src: *const c_char, dst: *mut c_char, srcSize: c_int, dstCapacity: c_int) -> c_int
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
return lz4::LZ4_compress_default(src, dst, srcSize, dstCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// NativeMethods.lz4.g.cs
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace CsBindgen
|
||||||
|
{
|
||||||
|
public static unsafe partial class NativeMethods
|
||||||
|
{
|
||||||
|
const string __DllName = "liblz4";
|
||||||
|
|
||||||
|
[DllImport(__DllName, EntryPoint = "csbindgen_LZ4_compress_default", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int LZ4_compress_default(byte* src, byte* dst, int srcSize, int dstCapacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally import generated module on `lib.rs`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// lib.rs, import generated codes.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
mod lz4;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[allow(non_camel_case_types)]
|
||||||
|
mod lz4_ffi;
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rust to C#.
|
||||||
|
|
||||||
|
You can bring simple Rust FFI code to C#.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// lib.rs, simple FFI code
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn my_add(x: i32, y: i32) -> i32 {
|
||||||
|
x + y
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Setup csbindgen code to `build.rs`.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
csbindgen::Builder::default()
|
||||||
|
.input_extern_file("lib.rs")
|
||||||
|
.csharp_dll_name("nativelib")
|
||||||
|
.generate_csharp_file("../dotnet/NativeMethods.g.cs")
|
||||||
|
.unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
It will generate this C# code.
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
// NativeMethods.g.cs
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace CsBindgen
|
||||||
|
{
|
||||||
|
public static unsafe partial class NativeMethods
|
||||||
|
{
|
||||||
|
const string __DllName = "nativelib";
|
||||||
|
|
||||||
|
[DllImport(__DllName, EntryPoint = "my_add", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int my_add(int x, int y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Builder options(configure template)
|
||||||
|
---
|
||||||
|
`input_bindgen_file` -> setup options -> `generate_to_file` to use C to C# workflow. Here are full option guide.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
csbindgen::Builder::default()
|
||||||
|
.input_bindgen_file("src/lz4.rs")
|
||||||
|
.method_filter(|x| { !x.starts_with("_") && !x.starts_with("XXH") } )
|
||||||
|
.rust_method_prefix("csbindgen_")
|
||||||
|
.rust_file_header("use super::lz4;")
|
||||||
|
.rust_method_type_path("lz4")
|
||||||
|
.csharp_class_name("LibLz4")
|
||||||
|
.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_c_long_convert("int")
|
||||||
|
.csharp_c_long_convert("uint")
|
||||||
|
.generate_to_file("src/lz4_ffi.rs", "../dotnet-sandbox/lz4_bindgen.cs")
|
||||||
|
.unwrap();
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
#[allow(unused)]
|
||||||
|
use ::std::os::raw::*;
|
||||||
|
|
||||||
|
{rust_file_header}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn {rust_method_prefix}LZ4_versionNumber() -> c_int
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
return {rust_method_type_path}::LZ4_versionNumber()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```csharp
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace {csharp_namespace}
|
||||||
|
{
|
||||||
|
public static unsafe partial class {csharp_class_name}
|
||||||
|
{
|
||||||
|
#if {csharp_dll_name_if(if_symbol,...)}
|
||||||
|
const string __DllName = "{csharp_dll_name_if(...,if_dll_name)}";
|
||||||
|
#else
|
||||||
|
const string __DllName = "{csharp_dll_name}";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
[DllImport(__DllName, EntryPoint = "{csharp_entry_point_prefix}LZ4_versionNumber", CallingConvention = CallingConvention.Cdecl)]
|
||||||
|
public static extern int {csharp_method_prefix}LZ4_versionNumber();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Builder options: Rust to C#
|
||||||
|
---
|
||||||
|
|
||||||
|
csbindgen::Builder::default()
|
||||||
|
.input_extern_file("src/lib.rs")
|
||||||
|
.csharp_class_name("LibRust")
|
||||||
|
.csharp_dll_name("csbindgen_tests")
|
||||||
|
.generate_csharp_file("../dotnet-sandbox/method_call.cs")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
License
|
||||||
|
---
|
||||||
|
This library is licensed under the MIT License.
|
@ -1,13 +1,16 @@
|
|||||||
use std::error::Error;
|
// use std::error::Error;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn Error>> {
|
// fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
fn main() {
|
||||||
bindgen::Builder::default()
|
bindgen::Builder::default()
|
||||||
.header("c/lz4/lz4.h")
|
.header("c/lz4/lz4.h")
|
||||||
.header("c/lz4/lz4hc.h")
|
.header("c/lz4/lz4hc.h")
|
||||||
.header("c/lz4/lz4frame.h")
|
.header("c/lz4/lz4frame.h")
|
||||||
.header("c/lz4/xxhash.h")
|
.header("c/lz4/xxhash.h")
|
||||||
.generate()?
|
.generate()
|
||||||
.write_to_file("src/lz4.rs")?;
|
.unwrap()
|
||||||
|
.write_to_file("src/lz4.rs")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.files([
|
.files([
|
||||||
@ -39,21 +42,27 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
|
|
||||||
csbindgen::Builder::default()
|
csbindgen::Builder::default()
|
||||||
.input_bindgen_file("src/lz4.rs")
|
.input_bindgen_file("src/lz4.rs")
|
||||||
|
.method_filter(|x| { !x.starts_with("_") && !x.starts_with("XXH") } )
|
||||||
.rust_method_prefix("csbindgen_")
|
.rust_method_prefix("csbindgen_")
|
||||||
.rust_file_header("use super::lz4;")
|
.rust_file_header("use super::lz4;")
|
||||||
.rust_method_type_path("lz4")
|
.rust_method_type_path("lz4")
|
||||||
.csharp_class_name("LibLz4")
|
.csharp_class_name("LibLz4")
|
||||||
|
.csharp_namespace("CsBindgen")
|
||||||
.csharp_dll_name("csbindgen_tests")
|
.csharp_dll_name("csbindgen_tests")
|
||||||
.csharp_dll_name_if("UNITY_IOS && !UNITY_EDITOR", "__Internal")
|
.csharp_dll_name_if("UNITY_IOS && !UNITY_EDITOR", "__Internal")
|
||||||
.csharp_entry_point_prefix("csbindgen_")
|
.csharp_entry_point_prefix("csbindgen_")
|
||||||
.csharp_method_prefix("")
|
.csharp_method_prefix("")
|
||||||
.generate_to_file("src/lz4_ffi.rs", "../dotnet-sandbox/lz4_bindgen.cs")?;
|
.csharp_c_long_convert("int")
|
||||||
|
.csharp_c_long_convert("uint")
|
||||||
|
.generate_to_file("src/lz4_ffi.rs", "../dotnet-sandbox/lz4_bindgen.cs")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
csbindgen::Builder::default()
|
csbindgen::Builder::default()
|
||||||
.input_extern_file("src/lib.rs")
|
.input_extern_file("src/lib.rs")
|
||||||
.csharp_class_name("LibRust")
|
.csharp_class_name("LibRust")
|
||||||
.csharp_dll_name("csbindgen_tests")
|
.csharp_dll_name("csbindgen_tests")
|
||||||
.generate_csharp_file("../dotnet-sandbox/method_call.cs")?;
|
.generate_csharp_file("../dotnet-sandbox/method_call.cs")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// csbindgen::Builder::new()
|
// csbindgen::Builder::new()
|
||||||
// .input_bindgen_file("src/zstd.rs")
|
// .input_bindgen_file("src/zstd.rs")
|
||||||
@ -76,5 +85,5 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||||||
// .csharp_dll_name("libbullet3")
|
// .csharp_dll_name("libbullet3")
|
||||||
// .generate_to_file("src/bullet3_ffi.rs", "../dotnet-sandbox/bullet3_bindgen.cs")?;
|
// .generate_to_file("src/bullet3_ffi.rs", "../dotnet-sandbox/bullet3_bindgen.cs")?;
|
||||||
|
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
}
|
||||||
|
2208
csbindgen-tests/src/lz4_mac.rs
Normal file
2208
csbindgen-tests/src/lz4_mac.rs
Normal file
File diff suppressed because it is too large
Load Diff
@ -37,7 +37,7 @@ impl Default for Builder {
|
|||||||
input_extern_file: "".to_string(),
|
input_extern_file: "".to_string(),
|
||||||
method_filter: |x| !x.starts_with('_'),
|
method_filter: |x| !x.starts_with('_'),
|
||||||
rust_method_type_path: "".to_string(),
|
rust_method_type_path: "".to_string(),
|
||||||
rust_method_prefix: "".to_string(),
|
rust_method_prefix: "csbindgen_".to_string(),
|
||||||
rust_file_header: "".to_string(),
|
rust_file_header: "".to_string(),
|
||||||
csharp_namespace: "CsBindgen".to_string(),
|
csharp_namespace: "CsBindgen".to_string(),
|
||||||
csharp_class_name: "NativeMethods".to_string(),
|
csharp_class_name: "NativeMethods".to_string(),
|
||||||
@ -83,7 +83,7 @@ impl Builder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// add method prefix to rust wrapper,
|
/// add method prefix to rust wrapper, default is `csbindgen_`
|
||||||
/// `pub extern "C" fn {rust_method_prefix}foo()`
|
/// `pub extern "C" fn {rust_method_prefix}foo()`
|
||||||
pub fn rust_method_prefix<T: Into<String>>(mut self, rust_method_prefix: T) -> Builder {
|
pub fn rust_method_prefix<T: Into<String>>(mut self, rust_method_prefix: T) -> Builder {
|
||||||
self.options.rust_method_prefix = rust_method_prefix.into();
|
self.options.rust_method_prefix = rust_method_prefix.into();
|
||||||
|
Loading…
Reference in New Issue
Block a user