use std::{env, path::Path, process::Command};
use vergen_gix::{Build, Cargo, Emitter, Rustc};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let build = Build::builder().build_timestamp(true).build();
let cargo = Cargo::builder().target_triple(true).build();
let rustc = Rustc::builder().semver(true).build();
let mut emitter = Emitter::default();
emitter
.add_instructions(&build)?
.add_instructions(&cargo)?
.add_instructions(&rustc)?;
emitter.emit()?;
emit_git_metadata();
emit_engine_version();
Ok(())
}
fn emit_git_metadata() {
if git_output(&["rev-parse", "--is-inside-work-tree"]).as_deref() == Some("true")
&& let Some(sha) = git_output(&["rev-parse", "HEAD"])
{
emit_git_rerun_triggers();
emit_env("VERGEN_GIT_SHA", &sha);
if let Some(branch) = git_output(&["rev-parse", "--abbrev-ref", "HEAD"]) {
emit_env("VERGEN_GIT_BRANCH", &branch);
}
if let Some(timestamp) = git_output(&["log", "-1", "--format=%cI"]) {
emit_env("VERGEN_GIT_COMMIT_TIMESTAMP", ×tamp);
}
if let Some(status) = git_output(&["status", "--porcelain"]) {
emit_env(
"VERGEN_GIT_DIRTY",
if status.is_empty() { "false" } else { "true" },
);
}
return;
}
if let Some(sha) = first_env(&[
"VERGEN_GIT_SHA",
"RCH_SOURCE_COMMIT",
"RCH_GIT_SHA",
"RCH_GIT_COMMIT",
"GIT_COMMIT",
"GITHUB_SHA",
"CI_COMMIT_SHA",
"BUILDKITE_COMMIT",
"DRONE_COMMIT_SHA",
"VERCEL_GIT_COMMIT_SHA",
]) {
emit_env("VERGEN_GIT_SHA", &sha);
}
if let Some(branch) = first_env(&["VERGEN_GIT_BRANCH", "GITHUB_REF_NAME", "CI_COMMIT_REF_NAME"])
{
emit_env("VERGEN_GIT_BRANCH", &branch);
}
}
fn emit_git_rerun_triggers() {
for name in ["HEAD", "packed-refs", "index", "reftable"] {
watch_git_path(name);
}
if let Some(head_ref) = git_output(&["symbolic-ref", "-q", "HEAD"])
&& let Some(path) = git_output(&["rev-parse", "--git-path", &head_ref])
{
let path = Path::new(&path);
if path.exists() {
println!("cargo:rerun-if-changed={}", path.display());
} else if let Some(parent) = path.parent().filter(|parent| parent.is_dir()) {
println!("cargo:rerun-if-changed={}", parent.display());
}
}
}
fn watch_git_path(name: &str) {
if let Some(path) = git_output(&["rev-parse", "--git-path", name])
&& Path::new(&path).exists()
{
println!("cargo:rerun-if-changed={path}");
}
}
fn emit_env(key: &str, value: &str) {
println!("cargo:rustc-env={key}={value}");
}
fn emit_engine_version() {
println!("cargo:rerun-if-changed=Cargo.lock");
let Ok(lock) = std::fs::read_to_string("Cargo.lock") else {
return;
};
let mut lines = lock.lines();
while let Some(line) = lines.next() {
if line.trim() == "name = \"fsqlite\""
&& let Some(version_line) = lines.next()
&& let Some(version) = version_line
.trim()
.strip_prefix("version = \"")
.and_then(|rest| rest.strip_suffix('"'))
{
emit_env("BR_FSQLITE_VERSION", version);
return;
}
}
}
fn git_output(args: &[&str]) -> Option<String> {
let output = Command::new("git").args(args).output().ok()?;
if !output.status.success() {
return None;
}
let value = String::from_utf8(output.stdout).ok()?;
let trimmed = value.trim();
Some(trimmed.to_string())
}
fn first_env(names: &[&str]) -> Option<String> {
for name in names {
println!("cargo:rerun-if-env-changed={name}");
if let Ok(value) = env::var(name) {
let trimmed = value.trim();
if !trimmed.is_empty() {
return Some(trimmed.to_string());
}
}
}
None
}