use std::{env, fs, io, process::Command};
use serde::{Deserialize, Serialize};
fn main() {
define_version_with_git();
define_revision();
define_features();
define_target();
define_profile();
define_built_by();
}
fn define_version_with_git() {
let cargo_version = env!("CARGO_PKG_VERSION");
let version = version_with_features(cargo_version);
let version = version_with_revision(&version);
println!("cargo:rustc-env=VERSION_WITH_GIT={version}");
}
fn version_with_features(cargo_version: &str) -> String {
let features = features().join("+");
if features.is_empty() {
String::from(cargo_version)
} else {
format!("{cargo_version}+{features}")
}
}
fn features() -> Vec<&'static str> {
if env::var("CARGO_FEATURE_UNSTABLE_PRE_COMMIT").is_ok() {
vec!["unstable-pre-commit"]
} else {
vec![]
}
}
fn version_with_revision(cargo_version: &str) -> String {
if let Some(revision) = maybe_revision(cargo_version) {
format!("{cargo_version}+{revision}")
} else {
String::from(cargo_version)
}
}
fn maybe_revision(cargo_version: &str) -> Option<String> {
maybe_revision_from_git(cargo_version)
.ok()
.flatten()
.or_else(|| maybe_revision_from_flake(cargo_version))
.or_else(|| maybe_revision_from_cargo_vcs_info(cargo_version))
}
fn maybe_revision_from_git(cargo_version: &str) -> io::Result<Option<String>> {
if git_describe()?.is_some_and(|s| s == format!("v{cargo_version}"))
|| is_cargo_checkout()? && !is_dev_version(cargo_version)
{
Ok(None)
} else {
Ok(git_revision_and_state()?)
}
}
#[expect(
clippy::missing_panics_doc,
reason = "the unwrap in the function cannot actually panic on modern systems"
)]
fn git_describe() -> io::Result<Option<String>> {
let output = Command::new("git")
.args(["describe", "--tags", "--always", "--dirty=-modified"])
.output()?;
#[expect(clippy::unwrap_used, reason = "non-UTF-8 outputs are obsolete")]
Ok(output
.status
.success()
.then(|| String::from_utf8(output.stdout).unwrap().trim().to_owned()))
}
fn git_revision_and_state() -> io::Result<Option<String>> {
git_revision()?
.map(|revision| {
if git_is_dirty()? && !is_cargo_checkout()? {
Ok(format!("{revision}-modified"))
} else {
Ok(revision)
}
})
.transpose()
}
#[expect(
clippy::missing_panics_doc,
reason = "the unwrap in the function cannot actually panic on modern systems"
)]
fn git_revision() -> io::Result<Option<String>> {
let output = Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()?;
#[expect(clippy::unwrap_used, reason = "non-UTF-8 outputs are obsolete")]
Ok(output
.status
.success()
.then(|| String::from_utf8(output.stdout).unwrap().trim().to_owned()))
}
fn git_is_dirty() -> io::Result<bool> {
let output = Command::new("git")
.args(["status", "--porcelain"])
.output()?;
Ok(output.status.success() && !output.stdout.is_empty())
}
fn is_cargo_checkout() -> io::Result<bool> {
let output = Command::new("git")
.args(["status", "--porcelain"])
.output()?;
Ok(output.status.success() && output.stdout == b"?? .cargo-ok\n")
}
fn maybe_revision_from_flake(cargo_version: &str) -> Option<String> {
if is_dev_version(cargo_version) {
env::var("FLAKE_REVISION").ok()
} else {
None
}
}
fn maybe_revision_from_cargo_vcs_info(cargo_version: &str) -> Option<String> {
if is_dev_version(cargo_version) {
revision_from_cargo_vcs_info().ok()
} else {
None
}
}
fn revision_from_cargo_vcs_info() -> io::Result<String> {
#[derive(Serialize, Deserialize)]
struct CargoVcsInfo {
git: GitInfo,
}
#[derive(Serialize, Deserialize)]
struct GitInfo {
sha1: String,
dirty: Option<bool>,
}
let vcs_info: CargoVcsInfo =
serde_json::from_str(&fs::read_to_string(".cargo_vcs_info.json")?)?;
let revision = vcs_info.git.sha1.chars().take(8).collect();
let revision = if vcs_info.git.dirty == Some(true) {
format!("{revision}-modified")
} else {
revision
};
Ok(revision)
}
fn is_dev_version(cargo_version: &str) -> bool {
cargo_version.contains("-dev")
}
fn define_revision() {
let revision = revision();
println!("cargo:rustc-env=REVISION={revision}");
}
fn revision() -> String {
git_revision_and_state()
.ok()
.flatten()
.or_else(|| env::var("FLAKE_REVISION").ok())
.or_else(|| revision_from_cargo_vcs_info().ok())
.unwrap_or_default()
}
fn define_features() {
let features = features().join(", ");
println!("cargo:rustc-env=FEATURES={features}");
}
#[expect(
clippy::missing_panics_doc,
reason = "the unwrap in the function cannot actually panic"
)]
fn define_target() {
#[expect(clippy::unwrap_used, reason = "TARGET is defined by cargo")]
let target = env::var("TARGET").unwrap();
println!("cargo:rustc-env=TARGET={target}");
}
#[expect(
clippy::missing_panics_doc,
reason = "the unwrap in the function cannot actually panic"
)]
fn define_profile() {
#[expect(clippy::unwrap_used, reason = "PROFILE is defined by cargo")]
let profile = env::var("PROFILE").unwrap();
println!("cargo:rustc-env=PROFILE={profile}");
}
fn define_built_by() {
let built_by = built_by();
println!("cargo:rustc-env=BUILT_BY={built_by}");
}
fn built_by() -> &'static str {
if env::var("FLAKE_REVISION").is_ok() {
"nix"
} else {
"cargo"
}
}