- 添加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