#![forbid(unsafe_code)]
use chrono::{DateTime, Utc};
use std::env;
use std::fs::File;
use std::io::{self, Write};
use std::path::PathBuf;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
fn main() -> io::Result<()> {
println!("cargo:rerun-if-changed=build.rs");
let out_dir = PathBuf::from(env::var("OUT_DIR").expect("Expected OUT_DIR to be set"));
let build_dir = out_dir
.parent()
.and_then(|p| p.parent())
.and_then(|p| p.parent())
.expect("Could not find build directory");
let env_file_name = "build.env";
let env_file_path = build_dir.join(env_file_name);
let mut env_file = File::create(&env_file_path)?;
add_source_date_epoch(&mut env_file)?;
add_tz(&mut env_file)?;
add_profile(&mut env_file)?;
Ok(())
}
fn add_source_date_epoch(file: &mut File) -> io::Result<()> {
const SOURCE_DATE_EPOCH: &str = "SOURCE_DATE_EPOCH";
let timestamp = env::var(SOURCE_DATE_EPOCH)
.ok()
.and_then(|value| {
if value.is_empty() {
None
} else {
value
.parse::<u64>()
.map_err(|err| panic!("Invalid {SOURCE_DATE_EPOCH} '{value}': {err}"))
.ok()
.map(Duration::from_secs)
}
})
.unwrap_or_else(|| {
SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
});
let source_date_epoch = timestamp.as_secs();
let dt = DateTime::<Utc>::from_timestamp(source_date_epoch as i64, 0)
.expect("Invalid timestamp for DateTime conversion");
let date_iso = dt.format("%Y-%m-%d").to_string();
let datetime_iso = dt.format("%Y-%m-%dT%H:%M:%SZ").to_string();
writeln!(file, "{SOURCE_DATE_EPOCH}={source_date_epoch}")?;
println!("cargo:rerun-if-changed=Cargo.toml");
println!("cargo:rerun-if-changed=src");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-env-changed={SOURCE_DATE_EPOCH}");
println!("cargo:rustc-env={SOURCE_DATE_EPOCH}={source_date_epoch}");
println!("cargo:rustc-env=BUILD_TIMESTAMP={source_date_epoch}");
println!("cargo:rustc-env=BUILD_DATE_ISO={date_iso}");
println!("cargo:rustc-env=BUILD_DATETIME_ISO={datetime_iso}");
Ok(())
}
fn add_tz(_file: &mut File) -> io::Result<()> {
const TZ: &str = "TZ";
const UTC: &str = "UTC";
println!("cargo:rustc-env={TZ}={UTC}");
Ok(())
}
fn add_profile(file: &mut File) -> io::Result<()> {
const PROFILE: &str = "PROFILE";
const EXPECT_PROFILE: &str = "EXPECT_PROFILE";
const RELEASE: &str = "release";
const DEBUG: &str = "debug";
let profile = env::var(PROFILE).expect("Expected PROFILE to be set");
let is_release = profile == RELEASE;
let mode = if is_release { RELEASE } else { DEBUG };
if let Ok(expected) = env::var(EXPECT_PROFILE) {
if expected != mode {
panic!("Expected profile '{expected}' but building with profile '{mode}'");
}
}
writeln!(file, "{EXPECT_PROFILE}={mode}")?;
println!("cargo:rerun-if-env-changed={PROFILE}");
println!("cargo:rerun-if-env-changed={EXPECT_PROFILE}");
println!("cargo:rustc-env={EXPECT_PROFILE}={mode}");
Ok(())
}