将 git commit hash 作为字符串包含到 Rust 程序中

问题描述 投票:0回答:6

我在 git 存储库中托管一个 Rust 项目,我想让它在某些命令上打印版本。如何将版本包含到程序中?我认为构建脚本可以设置可以在编译项目本身时使用的环境变量,但它不起作用:

build.rs

use std::env;

fn get_git_hash() -> Option<String> {
    use std::process::Command;

    let branch = Command::new("git")
                         .arg("rev-parse")
                         .arg("--abbrev-ref")
                         .arg("HEAD")
                         .output();
    if let Ok(branch_output) = branch {
        let branch_string = String::from_utf8_lossy(&branch_output.stdout);
        let commit = Command::new("git")
                             .arg("rev-parse")
                             .arg("--verify")
                             .arg("HEAD")
                             .output();
        if let Ok(commit_output) = commit {
            let commit_string = String::from_utf8_lossy(&commit_output.stdout);

            return Some(format!("{}, {}",
                        branch_string.lines().next().unwrap_or(""),
                        commit_string.lines().next().unwrap_or("")))
        } else {
            panic!("Can not get git commit: {}", commit_output.unwrap_err());
        }
    } else {
        panic!("Can not get git branch: {}", branch.unwrap_err());
    }
    None
}

fn main() {
    if let Some(git) = get_git_hash() {
        env::set_var("GIT_HASH", git);
    }
}

src/main.rs:

pub const GIT_HASH: &'static str = env!("GIT_HASH");

fm main() {
    println!("Git hash: {}", GIT_HASH);
}

错误信息:

error: environment variable `GIT_HASH` not defined
  --> src/main.rs:10:25
   |
10 | pub const GIT_HASH: &'static str = env!("GIT_HASH");
   |   
                                        ^^^^^^^^^^^^^^^^

有没有办法在编译时传递这些数据?如果不使用环境变量,如何在构建脚本和源代码之间进行通信?我只能考虑将数据写入某个文件,但我认为这对于这种情况来说太过分了。

rust rust-cargo
6个回答
57
投票

自 Rust 1.19 (cargo 0.20.0) 起,感谢 https://github.com/rust-lang/cargo/pull/3929,您现在可以为

 定义编译时环境变量 (
env!(…)
) rustc
rustdoc
通过:

println!("cargo:rustc-env=KEY=value");

所以OP的程序可以写为:

// build.rs
use std::process::Command;
fn main() {
    // note: add error checking yourself.
    let output = Command::new("git").args(&["rev-parse", "HEAD"]).output().unwrap();
    let git_hash = String::from_utf8(output.stdout).unwrap();
    println!("cargo:rustc-env=GIT_HASH={}", git_hash);
}
// main.rs
fn main() {
    println!("{}", env!("GIT_HASH"));
    // output something like:
    // 7480b50f3c75eeed88323ec6a718d7baac76290d
}

请注意,如果您仍想支持 1.18 或更低版本,您仍然无法使用此功能。


23
投票

已经有一个现有的箱子

vergen
可以计算构建脚本中的git提交。正如@DK的回答所描述的,在Rust 1.19之前,构建脚本无法修改环境变量,因此
vergen
仍然可以通过将结果写入OUT_DIR来工作(即
vergen
仍然无法解决OP的问题,但应该更容易使用)。


用途:

# Cargo.toml
...
[build-dependencies]
vergen = "0.1"
// build.rs
extern crate vergen;
use vergen::*;
fn main() {
    vergen(SHORT_SHA | COMMIT_DATE).unwrap();
}
mod version {
    include!(concat!(env!("OUT_DIR"), "/version.rs"));
}
fn main() {
    println!("commit: {} {}", version::commit_date(), version::short_sha());
    // output something like:
    //        commit: 2017-05-03 a29c7e5
}

9
投票

有一种简单的方法可以做到这一点,无需任何 build.rs 逻辑或自定义包。您只需将当前的 git 哈希作为环境变量直接传递给构建命令,然后使用

option_env!("PROJECT_VERSION")
在程序中读取它,并使用
env!("CARGO_PKG_VERSION")
后备。这些宏在构建时间期间读取环境变量。

下面的示例构建了这个最小的src/main.rs

fn main() {
    let version = option_env!("PROJECT_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
    println!("This binary was built from {}", version);
}

当您构建程序并想要准确的 git 哈希值时,例如在 CI/CD 配置中,您可以在 Cargo 命令前添加

PROJECT_VERSION=$(git rev-parse --short HEAD)
。像这样的
cargo run
(但也适用于
cargo build
和其他):

% PROJECT_VERSION=$(git rev-parse --short HEAD) cargo run
This binary was built from 6ca63b2

我个人更喜欢

$(git describe)
而不是
$(git rev-parse)
,因为前者更具描述性(现在使用
cargo build
作为例子只是为了变化):

% PROJECT_VERSION=$(git describe) cargo build 
% ./target/debug/your-program
This binary was built from v0.3.0-15-g6ca63b2    # or just 'v0.3.0' if current commit is tagged with that

由于您有

CARGO_PKG_VERSION
后备,您的 IDE 仍然可以为您即时构建文件。同样,对于开发,您可以跳过通过
PROJECT_VERSION
。在这种情况下,将使用您的
Cargo.toml
中的版本:

% cargo run
This binary was built from 0.3.0

6
投票

我只能考虑将数据写入某个文件,但我认为这对于这种情况来说太过分了。

这很不幸,因为这是唯一的方法。环境变量不起作用,因为对环境的更改不能“泄漏”到其他非子进程中。

对于更简单的事情,您

可以指示 Cargo 定义条件编译标志,但这些标志的功能不足以传达字符串 [1]。

Cargo 文档

的代码生成部分详细介绍了从构建脚本生成代码的详细信息。

[1]:我的意思是,除非您想将哈希分解为 160 个配置标志,然后在正在编译的源代码中重新组装它们,但这甚至是
more

过度杀伤力。


6
投票

const REF: &str = include_str!("../.git/HEAD"); const REF_MASTER: &str = include_str!("../.git/refs/heads/master"); // (elsewhere) if REF == "ref: refs/heads/master" { REF_MASTER } else { REF }

(除非您正在制作某种代码高尔夫,否则不要使用它。请注意,这是 100% 未经测试的。)


0
投票
cargo install --locked <crate>

获得提交哈希,您可以查看一下

https://github.com/pitoniak32/axolotl_git/blob/458b143a8dec2d45abe2e8af7b454ecbbbeafa14/build.rs#L1
有一个使用提交哈希写入的文件,它不适用于

--allow-dirty

标志。但这是我从 crates.io 获取安装中的提交哈希的解决方案。

use std::{fs, path::PathBuf, process::Command};

use serde::Deserialize;

#[derive(Debug, Deserialize)]
struct CargoVcsInfo {
    git: GitVcsInfo,
}

#[derive(Debug, Deserialize)]
struct GitVcsInfo {
    sha1: String,
}

const GIT_SHA_SHORT_MIN: usize = 7;

fn main() {
    println!("cargo::rerun-if-changed=.git/HEAD");
    let cargo_vcs_info = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(".cargo_vcs_info.json");
    let git_sha_long = if cargo_vcs_info.exists() {
        // not compatible with `cargo package` or `cargo publish` using `--allow-dirty` flag.
        // the `.cargo_vcs_info.json` file is not written
        serde_json::from_str::<CargoVcsInfo>(
            &fs::read_to_string(cargo_vcs_info)
                .expect("should be able to read cargo_vcs_info.json"),
        )
        .expect("cargo_vcs_info.json should contain expected info")
        .git
        .sha1
        .trim()
        .to_string()
    } else {
        String::from_utf8(
            Command::new("git")
                .args(["rev-parse", "HEAD"])
                .output()
                .expect("should be able to get commit hash")
                .stdout,
        )
        .expect("commit hash should be valid utf8")
        .trim()
        .to_string()
    };

    println!("cargo:rustc-env=GIT_SHA_LONG={}", &git_sha_long,);
    println!(
        "cargo:rustc-env=GIT_SHA_SHORT={}",
        &git_sha_long[..GIT_SHA_SHORT_MIN],
    );
}

希望这有帮助!

© www.soinside.com 2019 - 2024. All rights reserved.