背景
不管是什么语言的服务,编译出来的二进制可能都有一个最简单的诉求,那就是可以快速看到这个二进制的一些详细信息。例如:
👉 ./app-bin -v
# 从左到右为: 二进制名, commit, OS/ARCH, rustc版本, cargo版本, 编译时间
👉 app-bin a78a4d3, linux/x86_64, compiled by rustc 1.74.1 (a28077b28 2023-12-04) and cargo 1.74.1 (ecb9851af 2023-10-18), built on 2024.01.26-07:47:09
这个可以帮我们快速掌握这个二进制的信息。
实现
我们可以通过rust自带的特殊方案,构建build.rs
,在编译时将我们需要的信息设置成cargo env
传递进去。
目录结构
build.rs
需要在根目录上:
.
├── build.rs
├── Cargo.toml
├── Makefile
├── src
├── main.rs
编译配置
在Cargo.toml
中需要指定build的文件:
[package]
name = "app-bin"
version = "0.1.0"
edition = "2021"
build = "build.rs"
构造环境变量
// build.rs
use std::process::Command;
// 执行命令行得到对应的结果
fn execute_cmd(cmd: &str, args: &[&str], omit_error: bool) -> String {
let output = match Command::new(cmd).args(args).output() {
Ok(output) => output,
Err(err) => {
if omit_error {
return "".to_string();
} else {
panic!("fail to execute command [{cmd}] because of [{err}]");
}
}
};
if !output.status.success() {
if omit_error {
return "".to_string();
}
panic!(
"fail to execute command [{cmd}] because of [{}]",
String::from_utf8_lossy(&output.stderr)
);
}
String::from_utf8_lossy(&output.stdout).to_string()
}
// 设置cargo env 在程序中可以被使用到
fn set_cargo_env(name: &str, value: String) {
// set cargo env only when it is not defined
if std::env::var(name).is_err() {
println!("cargo:rustc-env={name}={value}");
}
// rebuild is needed if this env is changed compared with last build
println!("cargo:rerun-if-env-changed={name}");
}
fn main() {
let build_date = execute_cmd("date", &["+%Y.%m.%d-%H:%M:%S"], false);
// we should omit stderr in case git command is not installed
let build_commit = execute_cmd("git", &["describe", "--always", "--long", "--dirty"], true);
// we should omit stderr in case this repository has no any tags
let build_tag = execute_cmd("git", &["describe", "--tags", "--abbrev=0"], true);
let rustc_build_version = execute_cmd("rustc", &["--version"], false);
let cargo_build_version = execute_cmd("cargo", &["--version"], false);
set_cargo_env("BUILD_DATE", build_date);
set_cargo_env("BUILD_COMMIT", build_commit);
set_cargo_env("BUILD_TAG", build_tag);
set_cargo_env("RUSTC_BUILD_VERSION", rustc_build_version);
set_cargo_env("CARGO_BUILD_VERSION", cargo_build_version);
}
主函数
use std::{env, process};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 && args[1].as_str() == "-v" {
show_version();
}
}
fn show_version() {
// cargo编译时自带的环境变量
let name = env!("CARGO_PKG_NAME");
// 我们自己添加进来的运行时环境变量
let build_date = env!("BUILD_DATE");
let git_tag = env!("BUILD_TAG");
let git_commit = env!("BUILD_COMMIT");
let rustc_build_version = env!("RUSTC_BUILD_VERSION");
let cargo_build_version = env!("CARGO_BUILD_VERSION");
let git_info = if git_tag.is_empty() {
format!("{git_commit}")
} else {
format!("{git_tag}+{git_commit}")
};
let os = env::consts::OS;
let arch = env::consts::ARCH;
println!("{name} {git_info}, {os}/{arch}, compiled by {rustc_build_version} and {cargo_build_version}, built on {build_date}");
process::exit(0);
}
PREVIOUSgolang中进行类型转换时避免GC产生
NEXTrust导入未发布的库