llgo/rust/readme: update readme
This commit is contained in:
@@ -11,6 +11,9 @@ csv = "1.1"
|
|||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
crate-type = ["cdylib"] # The generated dynamic library will conform to the C standard
|
crate-type = ["cdylib"] # The generated dynamic library will conform to the C standard
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cbindgen = "0.26.0"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Import C Language Types
|
### 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:
|
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.
|
- `#[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.
|
- `unsafe` is used to mark operations that are unsafe, especially when dealing with raw pointers.
|
||||||
|
|
||||||
- `extern "C"` specifies the use of C calling conventions.
|
- `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<dyn Error>> {
|
||||||
|
// 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
|
### Memory Management
|
||||||
|
|
||||||
Use `Box` to manage dynamic memory to ensure correct memory release between Rust and C:
|
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
|
### 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:
|
Use cbindgen to generate a C header file, automating this process through a `build.rs` script:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
@@ -94,32 +121,124 @@ fn main() {
|
|||||||
let config = cbindgen::Config::from_file("cbindgen.toml").expect("Config file not found.");
|
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");
|
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 <dylib_lib> <header_file_lib>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check the Installation
|
||||||
|
|
||||||
|
You can check the installation by running the following command:
|
||||||
|
|
||||||
|
```SH
|
||||||
|
pkg-config --libs --cflags <lib_name>
|
||||||
|
```
|
||||||
|
|
||||||
|
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<lib_name>
|
||||||
```
|
```
|
||||||
|
|
||||||
### LLGO Package Mapping
|
### LLGO Package Mapping
|
||||||
|
|
||||||
Map functions from the Rust library to an LLGO package, ensuring type consistency:
|
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
|
```go
|
||||||
const (
|
const (
|
||||||
LLGoPackage = "link: $(pkg-config --libs csv_wrapper); -lcsv_wrapper"
|
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 {
|
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
|
//go:linkname NewReader C.csv_reader_new
|
||||||
func NewReader(file_path *c.Char) *Reader
|
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() {}
|
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 }
|
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
|
### Writing Examples and README
|
||||||
|
|
||||||
Provide example code and a detailed README file to help users understand how to use the generated library.
|
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.
|
||||||
|
|||||||
Reference in New Issue
Block a user