Rust bindgen笔记

原创
2023/09/08 10:55
阅读数 176
  1. 添加build dependencies

    使用cargo命令添加bindgen的build dependencies,即:

    # cargo add --build bindgen

    执行完毕后,在Cargo.toml文件中会新增如下内容:

    [build-dependenices]

    bindgen = "0.68.1"

2. 创建C库源代码示例

    在crate根目录下创建hello目录,在该目录下创建C库源代码文件hello.c,即:

    int hello(void) {
        return 42;
    }

    在hello目录下创建hello.h头文件,即:

    #ifndef __HELLO_H
    #define __HELLO_H
        int hello(void);
    #endif

3. 创建build.rs文件

    Build.rs文件位于crate的根目录,当执行cargo build命令时,cargo先执行build.rs内的指令,之后再去编译整个rust工程。因此可以在build.rs文件中通过bindgen生成Rust绑定C/C++头文件的内容。

    build.rs文件示例如下:

use std::{path::PathBuf, env};
fn main() {
    let libdir_path = PathBuf::from("hello").canonicalize().expect("cannot cannonicalize path");
    let headers_path = libdir_path.join("hello.h");
    let headers_path_str = headers_path.to_str().expect("Path is not a valid string");
    let obj_path = libdir_path.join("hello.o");
    let lib_path = libdir_path.join("libhello.a");

    println!("cargo:rustc-link-search={}", lib_path.to_str().unwrap());
    println!("cargo:rustc-link-lib=hello");
    println!("cargo:rerun-if-changed={}", headers_path_str);

    if !std::process::Command::new("clang")
        .arg("-c")
        .arg("-o")
        .arg(&obj_path)
        .arg(libdir_path.join("hello.c"))
        .output()
        .expect("could not spawn 'clang'")
        .status
        .success() {
            panic!("could not compile object file");
        }
    if !std::process::Command::new("ar")
        .arg("rcs")
        .arg(lib_path)
        .arg(obj_path)
        .output()
        .expect("could not spawn 'ar'")
        .status
        .success() {
            panic!("could not emit library file")
        }
    let bindings = bindgen::Builder::default()
        .header(headers_path_str)
        .parse_callbacks(Box::new(bindgen::CargoCallbacks))
        .generate()
        .expect("Unable to generate bindings");
    let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()).join("bindings.rs");
    bindings.write_to_file(out_path).expect("Couldn't write bindings!");

}

4. 在Rust文件中引用generated bindings文件

    

在Rust源代码中需要调用generated bindings文件的地方,使用如下指令包含在build.rs中生成的bindings文件,即:

#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));

pub fn call_hello() -> usize {
    unsafe {
        let result = hello();
        result as usize
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        let result = call_hello();
        assert_eq!(result, 42);
    }
}

5. 执行编译和运行测试

    执行如下命令进行编译,即:

    #cargo build

    执行如下目录进行单元测试,即:

    # RUSTFLAGS="-L ./hello" LD_LIBRARY_PATH="./hello" cargo test

 

展开阅读全文
加载中
点击引领话题📣 发布并加入讨论🔥
0 评论
0 收藏
0
分享
返回顶部
顶部