diff --git a/stacks-common/build.rs b/stacks-common/build.rs index 00db6c24db..218cb20cb6 100644 --- a/stacks-common/build.rs +++ b/stacks-common/build.rs @@ -1,8 +1,53 @@ use std::path::Path; +use std::process::Command; use std::{env, fs}; use toml::Value; +/// Given a [Command], run it and return the output as a string, +/// returning `None` if the command fails. +fn run_git_command(command: &mut Command) -> Option { + command + .output() + .map(|output| String::from_utf8(output.stdout).ok()) + .unwrap_or(None) + .map(|s| s.trim().to_string()) +} + +fn current_git_hash() -> Option { + option_env!("GIT_COMMIT").map(String::from).or_else(|| { + run_git_command( + Command::new("git") + .arg("log") + .arg("-1") + .arg("--pretty=format:%h") + .current_dir(env!("CARGO_MANIFEST_DIR")), + ) + }) +} + +fn current_git_branch() -> Option { + option_env!("GIT_BRANCH").map(String::from).or_else(|| { + run_git_command( + Command::new("git") + .arg("rev-parse") + .arg("--abbrev-ref") + .arg("HEAD"), + ) + }) +} + +fn is_working_tree_clean() -> bool { + Command::new("git") + .arg("diff") + .arg("--quiet") + .arg("--exit-code") + .current_dir(env!("CARGO_MANIFEST_DIR")) + .status() + .map(|status| status.code() == Some(0)) + .unwrap_or(true) +} + fn main() { let toml_content = fs::read_to_string("../versions.toml").expect("Failed to read versions.toml"); @@ -34,6 +79,29 @@ fn main() { } } + let git_commit = current_git_hash(); + rust_code.push_str(&format!( + "pub const GIT_COMMIT: Option<&'static str> = {git_commit:?};\n", + )); + if let Some(git_commit) = git_commit { + println!("cargo:rustc-env=GIT_COMMIT={}", git_commit); + } + + let git_branch = current_git_branch(); + rust_code.push_str(&format!( + "pub const GIT_BRANCH: Option<&'static str> = {git_branch:?};\n", + )); + if let Some(git_branch) = git_branch { + println!("cargo:rustc-env=GIT_BRANCH={}", git_branch); + } + + let is_clean = if is_working_tree_clean() { "" } else { "+" }; + rust_code.push_str(&format!( + "pub const GIT_TREE_CLEAN: Option<&'static str> = Some(\"{}\");\n", + is_clean + )); + println!("cargo:rustc-env=GIT_TREE_CLEAN={}", is_clean); + let out_dir = env::var_os("OUT_DIR").unwrap(); let dest_path = Path::new(&out_dir).join("versions.rs"); fs::write(&dest_path, rust_code).expect("Failed to write generated code"); diff --git a/stackslib/src/lib.rs b/stackslib/src/lib.rs index a9eb1b2aa5..66f09ac843 100644 --- a/stackslib/src/lib.rs +++ b/stackslib/src/lib.rs @@ -45,7 +45,7 @@ extern crate stacks_common; #[macro_use] pub extern crate clarity; -use stacks_common::versions::STACKS_NODE_VERSION; +use stacks_common::versions::{GIT_BRANCH, GIT_COMMIT, GIT_TREE_CLEAN, STACKS_NODE_VERSION}; pub use stacks_common::{address, codec, types, util}; #[macro_use] @@ -71,9 +71,9 @@ pub mod deps; pub mod monitoring; // set via _compile-time_ envars -const GIT_BRANCH: Option<&'static str> = option_env!("GIT_BRANCH"); -const GIT_COMMIT: Option<&'static str> = option_env!("GIT_COMMIT"); -const GIT_TREE_CLEAN: Option<&'static str> = option_env!("GIT_TREE_CLEAN"); +const GIT_BRANCH_ENV: Option<&'static str> = option_env!("GIT_BRANCH"); +const GIT_COMMIT_ENV: Option<&'static str> = option_env!("GIT_COMMIT"); +const GIT_TREE_CLEAN_ENV: Option<&'static str> = option_env!("GIT_TREE_CLEAN"); #[cfg(debug_assertions)] const BUILD_TYPE: &'static str = "debug"; @@ -82,11 +82,9 @@ const BUILD_TYPE: &'static str = "release"; pub fn version_string(pkg_name: &str, pkg_version: Option<&str>) -> String { let pkg_version = pkg_version.unwrap_or(STACKS_NODE_VERSION); - let git_branch = GIT_BRANCH - .map(|x| format!("{}", x)) - .unwrap_or("".to_string()); - let git_commit = GIT_COMMIT.unwrap_or(""); - let git_tree_clean = GIT_TREE_CLEAN.unwrap_or(""); + let git_branch = GIT_BRANCH_ENV.unwrap_or_else(|| GIT_BRANCH.unwrap_or("")); + let git_commit = GIT_COMMIT_ENV.unwrap_or_else(|| GIT_COMMIT.unwrap_or("")); + let git_tree_clean = GIT_TREE_CLEAN_ENV.unwrap_or_else(|| GIT_TREE_CLEAN.unwrap_or("")); format!( "{} {} ({}:{}{}, {} build, {} [{}])",