利用 build.rs 构建脚本,https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#rustc-link-lib
主要是使用以下两个字段来构建:
- cargo:rustc-link-lib=[KIND=]NAME — 告知 Cargo 通过 -l 去链接一个指定的库,往往用于链接一个本地库,通过 FFI
其中 KIND 可以是以下值之一:
-
dylib
:一个本地的动态库 -
static
:一个本地的静态库 -
framework
:一个 MacOS 框架 -
cargo:rustc-link-search=[KIND=]PATH — 告知 Cargo 通过 -L 将一个目录添加到依赖库的搜索路径中
其中 KIND 可以是以下值之一:
dependency
: 表示要链接的库是一个 Cargo 依赖项。crate
: 表示要链接的库是一个 Rust crate。native
: 表示要链接的库是一个本地库,例如 C 或 C++ 库。framework
: 表示要链接的库是一个 MacOS 框架。all
: 表示要链接所有类型的库。这是默认值。
例如,我们使用 cargo new rust_c_exam
创建一个项目
- 首先根目录下编写
add.c
文件
#include "add.h"
int add(int a, int b){
return a + b;
}
并生成库文件
cc -c add.c -o add.o
ar rcs libadd.a add.o // 生成静态库文件
gcc -shared add.o -o libadd.so // 生成动态库文件
然后在 main.rs 中编写如下内容:
extern "C" {
fn add(a: i32, b: i32) -> i32;
}
fn main() {
println!("Hello, world!,{}", unsafe { add(4, 10) });
}
这时候使用 build.rs
构建脚本:
fn main() {
println!("cargo:rustc-link-search=native=/Users/feiwu/Project/rust/rust_c_example");
println!("cargo:rustc-link-lib=add"); // 静态链接库名称
}
这里,我们也可以使用 #[link]
宏 配合 -L
参数来实现。
- 修改 main.rs 中的代码
#[link(name = "add", kind = "dylib")]
extern "C" {
fn add(a: i32, b: i32) -> i32;
}
fn main() {
println!("Hello, world!,{}", unsafe { add(4, 10) });
}
使用 rsutc
编译,假设在根目录下:
# 指定链接库的位置
rustc src/main.rs -L .
# -L 的完整指定:
rustc src/main.rs -L native=.
[lib]
下的 crate-type
属性,用于配置生成库的类型
bin
:生成一个可执行文件(二进制文件)。lib
:生成由编译器首选的库类型,默认是rlib
。rlib
:生成一个只包含 Rust 代码的静态库。staticlib
:生成一个本地的静态链接库。dylib
:生成一个 Rust 动态链接库。cdylib
:生成一个本地的动态链接库(类 C 得 dylib)。proc-macro
:生成一个过程宏库,用于编译时代码转换。
一个可执行文件产生的过程:预处理(Preprocess)、编译(Compilation)、汇编(Assembly)以及链接(Linking)四个过程。
cc -E main.c -o main.i # 预编译
cc -S main.i -o main.s # 编译
as main.s -o main.o # 汇编 cc -c main.s -o main.o
ld # 链接
在 Linux 下,可以使用 ldd
查看可执行文件的链接库所在位置,而在 macOS 中则使用 otool -L
去查看。