#![forbid(unsafe_code)]
use chrono::TimeZone;
use std::convert::TryFrom;
use std::ffi::OsStr;
pub fn exec(cmd: impl AsRef<OsStr>, args: &[&str]) -> Result<String, String> {
let output = std::process::Command::new(cmd.as_ref())
.args(args)
.output()
.map_err(|e| {
format!(
"error executing '{} {}': {}",
cmd.as_ref().to_string_lossy(),
args.join(" "),
e
)
})?;
if !output.status.success() {
return Err(format!(
"command '{} {}' failed: status={} stdout='{}' stderr='{}'",
cmd.as_ref().to_string_lossy(),
args.join(" "),
output.status,
escape_ascii(output.stdout),
escape_ascii(output.stderr)
));
}
Ok(escape_ascii(std::str::from_utf8(&output.stdout).unwrap().trim()).replace('"', "\\"))
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn escape_ascii(input: impl AsRef<[u8]>) -> String {
let mut result = String::new();
for byte in input.as_ref() {
for ascii_byte in core::ascii::escape_default(*byte) {
result.push_str(core::str::from_utf8(&[ascii_byte]).unwrap());
}
}
result
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn get_seconds_since_epoch() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs()
}
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn format_iso8601(seconds_since_epoch: u64) -> String {
chrono::Utc
.timestamp(i64::try_from(seconds_since_epoch).unwrap(), 0)
.to_rfc3339()
}
pub fn get_hostname() -> Result<String, String> {
exec("hostname", &[])
}
pub fn get_git_commit() -> Result<String, String> {
exec("git", &["rev-parse", "HEAD"])
}
pub fn get_git_branch() -> Result<String, String> {
exec("git", &["rev-parse", "--abbrev-ref=loose", "HEAD"])
}
pub fn is_git_dirty() -> Result<bool, String> {
Ok(!exec("git", &["status", "-s"])?.is_empty())
}
pub fn get_rustc_version() -> Result<String, String> {
let rustc_var = std::env::var_os("RUSTC")
.filter(|s| !s.is_empty())
.ok_or_else(|| String::from("RUSTC env var is not set"))?;
exec(rustc_var, &["--version"])
}
pub fn write(path: &std::path::Path) -> Result<(), std::io::Error> {
let mut contents = String::new();
match get_git_branch() {
Ok(value) => contents.push_str(&format!("GIT_BRANCH:{}\n", value)),
Err(e) => eprint!(
"WARNING build-data-writer: failed getting git branch: {}",
e
),
}
match get_git_commit() {
Ok(value) => contents.push_str(&format!("GIT_COMMIT:{}\n", value)),
Err(e) => eprint!(
"WARNING build-data-writer: failed getting git commit: {}",
e
),
}
match is_git_dirty() {
Ok(value) => contents.push_str(&format!("GIT_DIRTY:{}\n", value)),
Err(e) => eprint!(
"WARNING build-data-writer: failed getting git dirtiness: {}",
e
),
}
match get_hostname() {
Ok(value) => contents.push_str(&format!("HOSTNAME:{}\n", value)),
Err(e) => eprint!("WARNING build-data-writer: failed getting hostname: {}", e),
}
match get_rustc_version() {
Ok(value) => contents.push_str(&format!("RUSTC_VERSION:{}\n", value)),
Err(e) => eprint!("WARNING build-data-writer: failed rustc version: {}", e),
}
let seconds_since_epoch = get_seconds_since_epoch();
contents.push_str(&format!("TIME:{}\n", format_iso8601(seconds_since_epoch)));
contents.push_str(&format!("TIME_SECONDS:{}\n", seconds_since_epoch));
std::fs::write(&path, contents)
}
pub fn no_debug_rebuilds() {
if std::env::var_os("PROFILE")
.filter(|s| s == "debug")
.is_some()
{
println!("cargo:rerun-if-env-changed=PROFILE");
}
}