簡體   English   中英

將 git commit hash 作為字符串包含到 Rust 程序中

[英]Include git commit hash as string into Rust program

我在 git 存儲庫中托管了一個 Rust 項目,我想讓它在某些命令上打印版本。 如何將版本包含在程序中? 我認為構建腳本可以設置可以在編譯項目本身時使用的環境變量,但它不起作用:

構建.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 1.19 (cargo 0.20.0) 開始,感謝https://github.com/rust-lang/cargo/pull/3929 ,您現在可以為rustc定義一個編譯時環境變量( env!(…) )和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 或更低版本,則仍然無法使用它。

已經有一個現有的 crate vergen可以計算構建腳本中的 git commit。 正如vergen的回答所述,構建腳本無法在 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
}

我只能考慮將數據寫入某個文件,但我認為這對於這種情況來說太過分了。

這是不幸的,因為這唯一的方法。 環境變量無法工作,因為對環境的更改不能“泄漏”到其他非子進程中。

對於更簡單的事情,您可以指示 Cargo 定義條件編譯標志,但這些標志不足以傳達字符串 [1]。

Cargo 文檔代碼生成部分詳細介紹了從構建腳本生成代碼的詳細信息。


[1]:我的意思是,除非你想將散列分成 160 個配置標志,然后在正在編譯的源代碼中重新組裝它們,但這更加矯枉過正。

呃。 (我不建議在生產或測試或公共代碼甚至私有代碼中使用它,但我的意思是,它有點工作?)

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% 未經測試的。)

有一種簡單的方法可以做到這一點,而無需任何build.rs邏輯或自定義板條箱。 您只需將當前的 git hash 作為環境變量直接傳遞給構建命令,並在您的程序中使用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 配置中,您可以在貨物命令前加上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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM