diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f2b594ec..5c92157a8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [unreleased] +### Added +- Honor `SOURCE_DATE_EPOCH` in `BUILT_TIME_UTC` + ## [0.7.3] - 2024-05-21 ### Added - Search for lockfile in manifest's parent directory (crates in workspaces) diff --git a/src/krono.rs b/src/krono.rs index bfba03ae3..f595aa0b1 100644 --- a/src/krono.rs +++ b/src/krono.rs @@ -23,10 +23,32 @@ pub fn strptime(s: &str) -> chrono::DateTime { .with_timezone(&chrono::offset::Utc) } +fn get_source_date_epoch_from_env() -> Option> { + match std::env::var("SOURCE_DATE_EPOCH") { + Ok(val) => { + let ts = match val.parse::() { + Ok(ts) => ts, + Err(_) => { + eprintln!("SOURCE_DATE_EPOCH defined, but not a i64"); + return None; + } + }; + match chrono::DateTime::from_timestamp(ts, 0) { + Some(now) => Some(now), + None => { + eprintln!("SOURCE_DATE_EPOCH can't be represented as a UTC-time"); + None + } + } + } + Err(_) => None, + } +} + pub fn write_time(mut w: &fs::File) -> io::Result<()> { use io::Write; - let now = chrono::offset::Utc::now(); + let now = get_source_date_epoch_from_env().unwrap_or_else(chrono::offset::Utc::now); write_str_variable!( w, "BUILT_TIME_UTC", diff --git a/src/lib.rs b/src/lib.rs index ed933a3fd..b6e4dec7a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -286,6 +286,11 @@ //! The build-time is recorded as `BUILT_TIME_UTC`. If `built` is included as a runtime-dependency, //! it can parse the string-representation into a `time:Tm` with the help of //! `built::util::strptime()`. +//! +//! `built` honors the environment variable `SOURCE_DATE_EPOCH`. If the variable is defined and +//! parses to a valid UTC timestamp, that build-time is used instead of the current local time. +//! The variable is silently ignored if defined but but does not parse to a valid UTC timestamp. +//! //! ``` //! /// The built-time in RFC2822, UTC //! pub const BUILT_TIME_UTC: &str = "Wed, 27 May 2020 18:12:39 +0000"; diff --git a/tests/testbox_tests.rs b/tests/testbox_tests.rs index 888a1005c..f4b2a2908 100644 --- a/tests/testbox_tests.rs +++ b/tests/testbox_tests.rs @@ -468,6 +468,60 @@ fn main() { p.create_and_run(&[]); } +#[test] +fn source_date_epoch() { + let mut p = Project::new(); + let built_root = get_built_root(); + + p.add_file( + "Cargo.toml", + format!( + r#" +[package] +name = "testbox" +version = "1.2.3-rc1" +authors = ["Joe", "Bob", "Harry:Potter"] +build = "build.rs" +description = "xobtset" + +[dependencies] +built = {{ path = {:?}, features=["chrono"] }} + +[build-dependencies] +built = {{ path = {:?}, features=["chrono"] }}"#, + &built_root, &built_root + ), + ); + + p.add_file( + "build.rs", + r#" +use std::env; + +fn main() { + // Set timestamp + env::set_var("SOURCE_DATE_EPOCH", "1716639359"); + + built::write_built_file().unwrap(); +}"#, + ); + + p.add_file( + "src/main.rs", + r#" +mod built_info { + include!(concat!(env!("OUT_DIR"), "/built.rs")); +} + +fn main() { + assert_eq!(built::util::strptime(built_info::BUILT_TIME_UTC).to_rfc2822(), + "Sat, 25 May 2024 12:15:59 +0000"); + println!("builttestsuccess"); +}"#, + ); + p.create_and_run(&[]); +} + #[test] #[cfg(feature = "git2")] fn git_no_git() {