在写 Rust 项目的时候,免不了需要用到一些非 Rust 的第三方库,这些库要么是位于系统中已编译好的,要么是需要从源代码自己编译的。不管哪种形式,这都需要 Cargo 提供一种功能来满足这种要求。Cargo 就提供了一个 build.rs 自定义编译文件来满足这种要求。

Build Script

Rust 中和其他语言混编的构建脚本就是一个在 Rust 包根目录下名为 build.rs 的 Rust文件,而这个 build.rs 里面就是可正常编译的 Rust 代码。如下:

  1. fn main() {
  2. // 指定的src/hello.c文件有变化后,重运行该脚本。
  3. println!("cargo:rerun-if-changed=src/hello.c");
  4. // 使用 cc crate 编译src/hello.c文件并静态链接它。
  5. cc::Build::new()
  6. .file("src/hello.c")
  7. .compile("hello");
  8. }

注意:
你同样可以在项目的 Cargo.toml 配置文件的 _[package]_ 配置段中配置 _build_ 配置项指定其他的一个名称,如下:

  1. [package]
  2. name = "compile"
  3. version = "0.1.0"
  4. authors = ["zmant <zmant724@aliyun.com>"]
  5. edition = "2018"
  6. build = "custom_build.rs"

这样你的构建脚本就是 _custome_build.rs_ 文件了。当然,一般情况下都不需要重新指定其他的名称。以下都直接以 _build.rs_ 文件来指代构建脚本。

build.rs 的作用如下:

  • 构建绑定的 C/C++ 库
  • 查找系统中的 C/C++ 库
  • 生成符合特定规范的 Rust 模块
  • 执行任何特定于平台的配置

build.rs 会在 Rust 包代码编译之前被编译成一个可执行文件并执行。

默认情况下,构建脚本会在包内任意源码文件或依赖发生变化之后,下次执行 cargo 命令时就会重新运行。不过,通常情况下你都应该使用 rerun-if-changed 命令来指定只有当特定文件或依赖改变之后才需要重新编译执行构建脚本文件以提高整个编译的速度。

编译脚本的输入输出

编译脚本既然被编译成可执行文件并执行,那么,它肯定就需要有输入输出。这样的程序才有意义。编译脚本的输入比较简单,就是环境变量。

构建脚本的所有输入都以环境变量的方式传递。

构建脚本可以将任何的输出文件保存到 OUT_DIR 环境变量指定的目录中,除此之外,构建脚本不应该改变该目录以外的任何文件。

构建脚本是通过向终端打印以 cargo: 开头的特定格式的命令来和 cargo 进行通信的,cargo 将会解释每一条以 cargo: 开头的行。所有其他的行都会被忽视。

构建脚本其他到终端的输出默认都会被隐藏,这有时候不方便我们调试构建脚本。如果要显示构建脚本的输出,只需要我们在使用 cargo 命令的时候加上 -vv 标签(表示“very verbose”)就可以看到。其实,构建脚本打印到标准输出的所有内容都会被写入到 target/debug/<pkg>/output 文件中。通过构建脚本编译的第三库也是保存在 target/debug/<pkg>/out 目录下。

构建脚本中最常用到的一些指令如下:

cargo:rerun-if-changed=PATH: 某源文件改变,则重运行脚本 cargo:rerun-if-env-changed=VAR: 某环境变量改变,则重运行脚本 cargo:rustc-link-lib=[KIND=]NAME: 添加一个需要链接的库 cargo:rustc-link-search=[KIND=]PATH: 添加库的搜索路径 cargo:rustc-flags=FLAGS: 给编译器传递某些编译 FLAG cargo:rustc-cfg=KEY[=”VALUES”]: 启用编译时的 cfg 设置 cargo:rustc-env=VAR=VALUE: 设置一个环境变量 cargo:rustc-cdylib-link-arg=FLAG: 将自定义标志传递给 cdylib crates的链接器。 cargo:warning=MESSAGE: 在终端中显示一条警告信息 cargo:KEY=VALUE: 链接脚本使用的元数据

构建脚本的依赖

构建脚本也被可以有它自己的依赖,构建脚本的依赖是通过 [build-dependencies] 配置段指定的,如下:

  1. [build-dependencies]
  2. cc = "1.0.69"

注意,构建脚本的 [build-dependencies] 和代码使用的 [dependencies] 以及 [dev-dependencies] 都是不一样的。各是各的,不可混用。