Include git commit hash as string into Rust program

后端 未结 6 2054
小蘑菇
小蘑菇 2021-02-12 23:33

I host a Rust project in git repository and I want to make it print the version on some command. How can I include the version into the program? I thought that the build script

相关标签:
6条回答
  • 2021-02-12 23:42

    There is an easy way to do this without the need for any build.rs logic or custom crates. You simply pass the current git hash directly to the build command as an environment variable, and read it in your program with option_env!("PROJECT_VERSION"), with a env!("CARGO_PKG_VERSION") fallback. These macros read environment variables during build time.

    Examples follow that builds this minimal src/main.rs:

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

    When you build the program and want an accurate git hash, e.g. in your CI/CD configuration, you prefix the cargo command with PROJECT_VERSION=$(git rev-parse --short HEAD). Like this for cargo run (but also works for cargo build and others):

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

    Personally I prefer $(git describe) over $(git rev-parse) since the former is more descriptive (using cargo build as example now just for variation):

    % 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
    

    Since you have a CARGO_PKG_VERSION fallback, your IDE can still build the files on-the-fly for you. Likewise, for development, you can skip passing PROJECT_VERSION. In that case, the version from your Cargo.toml will be used:

    % cargo run
    This binary was built from 0.3.0
    
    0 讨论(0)
  • 2021-02-12 23:44

    I can only think about writing data to some file, but I think this is overkill for this case.

    That's unfortunate, because that is the only way of doing it. Environment variables can't work because changes to the environment can't "leak" into other, non-child processes.

    For simpler things, you can instruct Cargo to define conditional compilation flags, but those aren't powerful enough to communicate a string [1].

    The details of generating code from a build script is detailed in the code generation section of the Cargo documentation.


    [1]: I mean, unless you feel like breaking the hash into 160 config flags and then re-assembling them in the source being compiled, but that's even more overkill.

    0 讨论(0)
  • 2021-02-12 23:51

    Since Rust 1.19 (cargo 0.20.0), thanks to https://github.com/rust-lang/cargo/pull/3929, you can now define a compile-time environment variable (env!(…)) for rustc and rustdoc via:

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

    So OP's program can be written as:

    // 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
    }
    

    Note that you still cannot use this if you still want to support 1.18 or below.

    0 讨论(0)
  • 2021-02-12 23:55

    There is already an existing crate vergen that can calculate the git commit in the build script. As @DK's answer described, the build script cannot modify environment variable before Rust 1.19, so vergen still works by writing the result into OUT_DIR (i.e. vergen still won't solve OP's question, but it should be easier to use).


    Usage:

    # 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
    }
    
    0 讨论(0)
  • 2021-02-13 00:05

    shadow-rs:: build-time information stored in your rust project.(binary,lib,cdylib,dylib)

    https://github.com/baoyachi/shadow-rs

    shadow-rs allows you to recall properties of the build process and environment at runtime, including:

    • Cargo.toml project version
    • Dependency information
    • The Git commit that produced the build artifact (binary)
    • What version of the rust toolchain was used in compilation
    • The build variant, e.g. debug or release
    • (And more)

    You can use this tool to check in production exactly where a binary came from and how it was built.

    Full Examples

    • Check out the shadow_example for a simple demonstration of how shadow-rs might be used to provide build-time information at run-time.
    • built in method:examples.
    0 讨论(0)
  • 2021-02-13 00:07

    Uh. (I do not recommend this in production or in testing or in public code or even in private code but I mean, it kinda does the job?)

    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 }
    

    (do not use this unless you're making some sort of codegolf. note that this is 100% untested.)

    0 讨论(0)
提交回复
热议问题