From c0eeedc71ac4360f31f1963ce9926ff17a5ad2b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E8=8B=B1=E6=9D=B0?= Date: Wed, 17 Jul 2024 10:00:48 +0800 Subject: [PATCH] llgo/rust/readme: update readme --- doc/Rust-to-LLGO-Migration-Guide.md | 149 +++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 15 deletions(-) diff --git a/doc/Rust-to-LLGO-Migration-Guide.md b/doc/Rust-to-LLGO-Migration-Guide.md index 253aca82..48ae4609 100644 --- a/doc/Rust-to-LLGO-Migration-Guide.md +++ b/doc/Rust-to-LLGO-Migration-Guide.md @@ -11,6 +11,9 @@ csv = "1.1" [lib] crate-type = ["cdylib"] # The generated dynamic library will conform to the C standard + +[build-dependencies] +cbindgen = "0.26.0" ``` ### Import C Language Types @@ -26,11 +29,37 @@ use libc::{c_int, c_char, strlen}; To ensure that Rust functions can be correctly called by C and LLGO, use the following decorators: - `#[no_mangle]` prevents the compiler from mangling the function name. - - `unsafe` is used to mark operations that are unsafe, especially when dealing with raw pointers. - - `extern "C"` specifies the use of C calling conventions. +For example, in Rust, we write CSV code like this: + +```rust +use csv::ReaderBuilder; +use std::error::Error; + +fn main() -> Result<(), Box> { + // Define the CSV file path + let file_path = "example.csv"; + + // Creating a CSV Reader + let mut reader = ReaderBuilder::new().from_path(file_path)?; + + // Define a container to store records + let mut record = csv::StringRecord::new(); + + // Read records one by one + while reader.read_record(&mut record)? { + // Print each record + println!("{:?}", record); + } + + Ok(()) +} +``` + +If we need to migrate the CSV library to LLGO, we need to encapsulate each CSV method with a C API, such as the following: + ### Memory Management Use `Box` to manage dynamic memory to ensure correct memory release between Rust and C: @@ -75,18 +104,16 @@ pub extern "C" fn free_string(s: *mut c_char) { } ``` -### Compilation and Installation - -Build the dynamic library and use `dylib-installer` to install it to the system path: - -```sh -cargo build --release -cargo install --git https://github.com/hackerchai/dylib-installer -sudo dylib_installer ./target/release/ -``` - ### Generate Header File +Edit `cbindgen.toml` to configure the header file generation rules: + +```toml +# See https://github.com/mozilla/cbindgen/blob/master/docs.md#cbindgentoml for +# a list of possible configuration values. +language = "C" +``` + Use cbindgen to generate a C header file, automating this process through a `build.rs` script: ```rust @@ -94,32 +121,124 @@ fn main() { let config = cbindgen::Config::from_file("cbindgen.toml").expect("Config file not found."); cbindgen::generate_with_config(&crate_dir, config).unwrap().write_to_file("target/include/csv_wrapper.h"); } +``` +### Compilation and Installation + +Build the dynamic library: + +```sh +cargo build --release +``` + +### Install dylib-installer + +Install the [dylib-installer](https://github.com/hackerchai/dylib-installer) tool, which is used to install dynamic libraries: + +```SH +brew tap hackerchai/tap +brew install dylib-installer +``` + +Or you can install it using Cargo: + +```SH +cargo install dylib_installer +``` + +### Install Dynamic Library + +Use dylib-installer to install the built dynamic library and the header file into the system directory: + +```SH +sudo dylib_installer +``` + +### Check the Installation + +You can check the installation by running the following command: + +```SH +pkg-config --libs --cflags +``` + +if everything is installed correctly, you will see the output like this (depending on your system): + +```SH +-I/usr/local/include -L/usr/local/lib -l ``` ### LLGO Package Mapping Map functions from the Rust library to an LLGO package, ensuring type consistency: +- LLGoPackage + +Specify `LLGoPackage` and use `pkg-config` to find the location of the lib library. + ```go const ( LLGoPackage = "link: $(pkg-config --libs csv_wrapper); -lcsv_wrapper" ) +``` +- Type + +If you want to use variables inside the struct, you can add them accordingly. +If it can't be represented directly or is not needed, it can be represented in the form `Unused []byte`, the length of the array is determined by its size, and if the struct is only used as a pointer, then the array length can be `0`. + +```go type Reader struct { - Unused [8]byte + Unused [0]byte } +// type Reader struct { +// Unused [8]byte +// } +``` + +- Ordinary functions + +Ordinary functions can be mapped in the form of `//go:linkname`. + +```go //go:linkname NewReader C.csv_reader_new func NewReader(file_path *c.Char) *Reader +``` -//llgo:link (*Reader).Free C.csv_reader_free +- Method + +Methods need to be mapped in the form of `// llgo:link (*Receiver)`. + +```go +// llgo:link (*Reader).Free C.csv_reader_free func (reader *Reader) Free() {} -//llgo:link (*Reader).ReadRecord C.csv_reader_read_record +// llgo:link (*Reader).ReadRecord C.csv_reader_read_record func (reader *Reader) ReadRecord() *c.Char { return nil } ``` +- Function pointer + +If you use a function pointer, that is, declare the function as a type separately, you need to use `// llgo:type C`to declare it. + +```go +// llgo:type C +type IoReadCallback func(c.Pointer, *Context, *uint8, uintptr) uintptr + +// llgo:link (*Io).SetRead C.hyper_io_set_read +func (io *Io) SetRead(callback IoReadCallback) {} +``` + +Or declare the function directly in the parameter. + +```go +// llgo:link (*Io).SetRead C.hyper_io_set_read +func (io *Io) SetRead(callback func(c.Pointer, *Context, *uint8, uintptr) uintptr) {} +``` + ### Writing Examples and README Provide example code and a detailed README file to help users understand how to use the generated library. + +You can find the migrated instance from [llgoexamples](https://github.com/goplus/llgoexamples), in the lib directory is the migrated Rust library, and in the rust directory, the migrated mapping file and go demo.