cargo工具还允许用户在正式编译开始前执行一些自定义的逻辑。方法是在
Cargo.toml中配置一个build的属性,比如:
[package]
…
build = “build.rs”
自定义逻辑就写在build.rs文件里面。在执行cargo build的时候,cargo会先把这
个build.rs编译成一个可执行程序,然后运行这个程序,做完后再开始编译真正的
crate。build.rs一般用于下面这些情况:
·提前调用外部编译工具,比如调用gcc编译一个C库;
·在操作系统中查找C库的位置;
·根据某些配置,自动生成源码;
·执行某些平台相关的配置。
build.rs里面甚至可以再依赖其他的库。可以在build-dependencies里面指定:
[build-dependencies]
rand = “1.0”
build.rs里面如果需要读取当前crate的一些信息,可以通过环境变量来操作。
cargo在执行这个程序之前就预先设置好了一些环境变量,比较常用的有下面几种。
·CARGO_MANIFEST_DIR
当前crate的Cargo.toml文件的路径。
·CARGO_PKG_NAME
当前crate的名字。
·OUT_DIR
build.rs的输出路径。如果要在build.rs中生成代码,那么生成的代码就要存在这
个文件夹下。
·HOST
当前rustc编译器的平台特性。
·OPT_LEVEL
优化级别。
·PROFILE
判断是release还是debug版本。
更多的环境变量请参考cargo的标准文档。
下面还是用一个完整的示例演示一下build.rs功能如何使用。假设我们现在要把
当前项目最新的commit id记录到可执行程序里面。这种需求就必须使用自动代码生
成来完成。首先新建一个项目with_commit_hash:
cargo new –bin with_commit_hash
然后,到Cargo.toml里面加上:
build = “build.rs”
当然,对应的,要在项目文件夹下创建一个build.rs的文件。
我们希望能在编译过程中生成一份源代码文件,里面记录了一个常量,类似这
样:
const CURRENT_COMMIT_ID : &’static str = “123456789ABCDEF”;
查找当前git的最新commit id可以通过命令git rev-parse HEAD来完成。所以,我
们的build.rs可以这样实现:
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn main() {
let out_dir = env::var(“OUT_DIR”).unwrap();
let dest_path = Path::new(&out_dir).join(“commit_id.rs”);
let mut f = File::create(&dest_path).unwrap();
let commit = Command::new(“git”)
.arg(“rev-parse”)
.arg(“HEAD”)
.output()
.expect(“Failed to execute git command”);
let commit = String::from_utf8(commit.stdout).expect(“Invalid utf8 string”);
let output = format!(r#“pub const CURRENT_COMMIT_ID : &'static str = “{}”;” #, commit);
f.write_all(output.as_bytes()).unwrap();
}
输出路径是通过读取OUT_DIR环境变量获得的。利用标准库里面的Command
类型,我们可以调用外部的进程,并获得它的标准输出结果。最后再构造出我们想
要的源码字符串,写入到目标文件中。
生成了这份代码之后,我们怎么使用呢?在main.rs里面,可以通过宏直接把这
部分源码包含到项目中来:
include!(concat!(env!(“OUT_DIR”), “/commit_id.rs”));
fn main() {
println!(“Current commit id is: {}”, CURRENT_COMMIT_ID);
}
这个include!宏可以直接把目标文件中的内容在编译阶段复制到当前位置。这
样main函数就可以访问CURRENT_COMMIT_ID这个常量了。大家要记得在当前项
目使用git命令新建几个commit。然后编译,执行,可见在可执行程序中包含最新
commit id这个任务就完全自动化起来了。