1use eyre::{bail, eyre, Result, WrapErr as _};
2use std::{
3 env::{var, VarError},
4 fs,
5 path::Path,
6 process::Command,
7};
8use time::{format_description::well_known::Rfc3339, OffsetDateTime, UtcOffset};
9
10#[allow(clippy::module_name_repetitions)]
21pub fn build_rs() -> Result<()> {
22 let commit = rerun_if_git_changes().unwrap_or_else(|e| {
23 eprintln!("Warning: {}", e);
24 None
25 });
26
27 println!(
28 "cargo:rustc-env=COMMIT_SHA={}",
29 env_or_cmd("COMMIT_SHA", &["git", "rev-parse", "HEAD"]).unwrap_or_else(|e| {
30 eprintln!("Warning: {}", e);
31 commit.unwrap_or_else(|| "0000000000000000000000000000000000000000".to_string())
32 })
33 );
34 let build_date = OffsetDateTime::now_utc();
35 let commit_date = env_or_cmd("COMMIT_DATE", &[
36 "git",
37 "log",
38 "-n1",
39 "--pretty=format:'%aI'",
40 ])
41 .and_then(|str| Ok(OffsetDateTime::parse(str.trim_matches('\''), &Rfc3339)?))
42 .unwrap_or_else(|e| {
43 eprintln!("Warning: {}", e);
44 OffsetDateTime::UNIX_EPOCH
45 });
46 println!(
47 "cargo:rustc-env=COMMIT_DATE={}",
48 commit_date.to_offset(UtcOffset::UTC).date()
49 );
50 println!(
51 "cargo:rustc-env=BUILD_DATE={}",
52 build_date.to_offset(UtcOffset::UTC).date()
53 );
54 println!(
55 "cargo:rustc-env=TARGET={}",
56 var("TARGET").wrap_err("Fetching environment variable TARGET")?
57 );
58
59 Ok(())
60}
61
62fn env_or_cmd(env: &str, cmd: &[&str]) -> Result<String> {
63 match var(env) {
65 Ok(s) => return Ok(s),
66 Err(VarError::NotPresent) => (),
67 Err(e) => bail!(e),
68 };
69
70 let err = || {
72 format!(
73 "Variable {} is unset and command \"{}\" failed",
74 env,
75 cmd.join(" ")
76 )
77 };
78 let output = Command::new(cmd[0])
79 .args(&cmd[1..])
80 .output()
81 .with_context(err)?;
82 if output.status.success() {
83 Ok(String::from_utf8(output.stdout)?.trim().to_string())
84 } else {
85 bail!(err())
86 }
87}
88
89fn rerun_if_git_changes() -> Result<Option<String>> {
90 let git_head = Path::new(".git/HEAD");
91
92 if !git_head.exists() {
94 eprintln!("No .git/HEAD found, not rerunning on git change");
95 return Ok(None);
96 }
97
98 println!("cargo:rerun-if-changed=.git/HEAD");
100
101 let contents = fs::read_to_string(git_head).wrap_err("Error reading .git/HEAD")?;
103 let head_ref = contents.split(": ").collect::<Vec<_>>();
104 let commit = if head_ref.len() == 2 && head_ref[0] == "ref" {
105 let ref_path = Path::new(".git").join(head_ref[1].trim());
106 let ref_path_str = ref_path
107 .to_str()
108 .ok_or_else(|| eyre!("Could not convert ref path {:?} to string", ref_path))?;
109 println!("cargo:rerun-if-changed={}", ref_path_str);
110 fs::read_to_string(&ref_path).with_context(|| format!("Error reading {}", ref_path_str))?
111 } else {
112 contents
113 };
114 Ok(Some(commit))
115}