use std::io::Write;
use std::path::{absolute, Path, PathBuf};
fn file_contents<P: AsRef<Path>>(path: P) -> String {
let path =
absolute(path.as_ref()).expect("Unable to make the path absolute");
let mut stdout = std::io::stdout();
stdout
.write_all(b"cargo::rerun-if-changed=")
.expect("Unable to write stdout");
stdout
.write_all(path.as_os_str().as_encoded_bytes())
.expect("Unable to write path to stdout");
stdout
.write_all(b"\n")
.expect("Unable to write newline to stdout");
std::fs::read_to_string(path).expect("Unable to read file")
}
fn git_rel<P: AsRef<Path>>(path: P) -> PathBuf {
let output = std::process::Command::new("git")
.arg("rev-parse")
.arg("--show-toplevel")
.output()
.expect("Error launching git rev-parse");
if !output.status.success() {
let stderr = std::str::from_utf8(&output.stderr)
.expect("git failed and stderr was not utf8");
eprintln!("`git rev-parse --show-toplevel` failed, stderr: {stderr}");
panic!("`git rev-parse --show-toplevel` failed");
}
let mut root = PathBuf::from(
std::str::from_utf8(&output.stdout)
.expect("Output was not utf-8")
.trim(),
);
root.push(path);
root
}
fn emit_git_commit() {
let head = file_contents(git_rel(".git/HEAD"));
let rev = if let Some(r) = head.strip_prefix("ref: ") {
let mut ref_path = git_rel(".git/");
ref_path.push(r.trim());
file_contents(ref_path)
} else {
head
};
let output = std::process::Command::new("git")
.arg("rev-parse")
.arg("--short")
.arg(rev.trim())
.output()
.expect("could not spawn `git` to get the hash");
if !output.status.success() {
let stderr = std::str::from_utf8(&output.stderr)
.expect("git failed and stderr was not utf8");
eprintln!("`git rev-parse --short HEAD` failed, stderr: {stderr}");
panic!("`git rev-parse --short HEAD` failed");
}
let rev = std::str::from_utf8(&output.stdout)
.expect("git did not output utf8")
.trim();
println!("cargo::rustc-env=REPO_REV={rev}");
}
fn emit_git_dirty() {
let output = std::process::Command::new("git")
.arg("status")
.arg("-z")
.arg("--porcelain=v1")
.output()
.expect("could not spawn `git` to get repository status");
if !output.status.success() {
let stderr = std::str::from_utf8(&output.stderr)
.expect("git failed and stderr was not utf8");
eprintln!("`git status` failed, stderr: {stderr}");
panic!("`git status` failed");
}
let output =
std::str::from_utf8(&output.stdout).expect("git did not output utf8");
let dirty = if output.trim().is_empty() {
""
} else {
" *dirty*"
};
println!("cargo::rustc-env=REPO_DIRTY={dirty}");
let output = std::process::Command::new("git")
.arg("ls-files")
.arg("-z")
.arg("--cached")
.arg("--deleted")
.arg("--modified")
.arg("--others")
.arg("--exclude-standard")
.output()
.expect("could not spawn `git` to get repository status");
if !output.status.success() {
let stderr = std::str::from_utf8(&output.stderr)
.expect("git failed and stderr was not utf-8");
eprintln!("`git ls-files` failed, stderr: {stderr}");
panic!("`git ls-files` failed");
}
let output =
std::str::from_utf8(&output.stdout).expect("git did not output utf8");
for fname in output.split_terminator("\0") {
println!("cargo::rerun-if-changed={fname}");
}
}
fn main() {
emit_git_commit();
emit_git_dirty();
}