use std::{path::Path, process::Command};
fn main() {
for path in ["build.rs", "Cargo.toml", "Cargo.lock", "src"] {
println!("cargo:rerun-if-changed={path}");
}
let build = from_git().or_else(from_package).unwrap_or_default();
println!("cargo:rustc-env=TEAMS_BUILD={build}");
let version = std::env::var("CARGO_PKG_VERSION").unwrap_or_default();
let long = match build.as_str() {
"" => version,
build => format!("{version} ({build})"),
};
println!("cargo:rustc-env=TEAMS_LONG_VERSION={long}");
}
fn git(args: &[&str]) -> Option<Vec<u8>> {
let output = Command::new("git").args(args).output().ok()?;
output.status.success().then_some(output.stdout)
}
fn text(bytes: Vec<u8>) -> Option<String> {
let text = String::from_utf8(bytes).ok()?.trim().to_owned();
(!text.is_empty()).then_some(text)
}
fn from_git() -> Option<String> {
let top = text(git(&["rev-parse", "--show-toplevel"])?)?;
let manifest = std::env::var("CARGO_MANIFEST_DIR").ok()?;
if Path::new(&top).canonicalize().ok()? != Path::new(&manifest).canonicalize().ok()? {
return None;
}
let reference = git(&["symbolic-ref", "-q", "HEAD"]).and_then(text);
for name in ["HEAD", "index", "packed-refs"]
.into_iter()
.chain(reference.as_deref())
{
if let Some(path) = git(&["rev-parse", "--git-path", name]).and_then(text)
&& Path::new(&path).exists()
{
println!("cargo:rerun-if-changed={path}");
}
}
let commit = text(git(&["rev-parse", "--short=12", "HEAD"])?)?;
let changes = git(&["diff", "HEAD", "--no-ext-diff", "--binary"])?;
Some(if changes.is_empty() {
commit
} else {
format!("{commit}-dirty-{:08x}", fnv1a(&changes) as u32)
})
}
fn from_package() -> Option<String> {
let info = std::fs::read_to_string(".cargo_vcs_info.json").ok()?;
let sha1 = info.split("\"sha1\"").nth(1)?.split('"').nth(1)?;
let commit = sha1
.get(..12)
.filter(|c| c.bytes().all(|b| b.is_ascii_hexdigit()))?;
let dirty = info.contains("\"dirty\": true") || info.contains("\"dirty\":true");
Some(if dirty {
format!("{commit}-dirty")
} else {
commit.to_owned()
})
}
fn fnv1a(bytes: &[u8]) -> u64 {
bytes.iter().fold(0xcbf2_9ce4_8422_2325, |hash, &byte| {
(hash ^ u64::from(byte)).wrapping_mul(0x0100_0000_01b3)
})
}