Cargo 使用 build.rs 动态生成版本号
2019-08-03 10:08 rust
最近有个需求, 要在生成的可执行文件中包含一个版本号字段, 除了像 0.1.0
这样的
在 Cargo.toml
中定义的版本号之外, 最好还包含一下当前的 git commit id.
这个功能可以用 build.rs 脚本来实现. 大致做法也很简单:
- 在 build.rs 中读取 Cargo.toml 里定义好的版本号, 以及当前的 git 历史, 并写入到编译目录里的一个文本文件
- 在
src/
目录的源代码里, 定义一个字符串常量, 它的值在编译期指定的, 就是这个文件文件的内容
编写 build.rs
以下是 build.rs
的内容:
use std::env;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::process::Command;
fn get_git_version() -> String {
let version = env::var("CARGO_PKG_VERSION").unwrap().to_string();
let child = Command::new("git")
.args(&["describe", "--always"])
.output();
match child {
Ok(child) => {
let buf = String::from_utf8(child.stdout).expect("failed to read stdout");
return version + "-" + &buf;
},
Err(err) => {
eprintln!("`git describe` err: {}", err);
return version;
}
}
}
fn main() {
let version = get_git_version();
let mut f = File::create(
Path::new(&env::var("OUT_DIR").unwrap())
.join("VERSION")).unwrap();
f.write_all(version.trim().as_bytes()).unwrap();
}
其中:
- 环境变量
CARGO_PKG_VERSION
就是Cargo.toml
中定义好的版本号 - 调用
git describe --always
命令读取当前的 commit id - 将拿到的版本号字符串写入到
$OUT_DIR/VERSION
文件中,$OUT_DIR
这个环境变量在使用cargo build
命令编译时, 它的值是:/home/shaohua/build-demo/target/debug/build/build-demo-1796000c31d7b82a/out"
在源码中读取版本号
之后我们在 main.rs
中测试一下:
pub const VERSION: &'static str =
include_str!(concat!(env!("OUT_DIR"), "/VERSION"));
fn main() {
println!("Hello, world!");
println!("version: {}", VERSION);
}
这里的 VERSION
常量字符串, 就包含了 $OUT_DIR/VERSION
文件的内容.
读取时用到了几个宏:
include_str!()
宏用于读取 UTF-8 编码的文本文件, 默认路径相对于当前源文件concat!()
宏用于合并字符串env!()
宏用于展开编译时的环境变量
来测试一下:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/build-demo`
Hello, world!
version: 0.1.0-28dfe0f
参考
- https://www.reddit.com/r/rust/comments/6b40pb/how_to_write_buildrs_scripts_properly
- https://www.youtube.com/watch?v=zAXbPnfTJg4