/
build.rs
162 lines (146 loc) Β· 5.22 KB
/
build.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#![warn(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::restriction)]
#![allow(clippy::missing_panics_doc)]
use std::env;
use std::ffi::OsString;
use std::fmt;
use std::process::Command;
use std::str;
use tz::UtcDateTime;
#[derive(Default, Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
struct Date {
pub year: i32,
pub month: u32,
pub day: u32,
}
impl From<UtcDateTime> for Date {
fn from(date: UtcDateTime) -> Self {
Self {
year: date.year(),
month: date.month().into(),
day: date.month_day().into(),
}
}
}
impl fmt::Display for Date {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
}
}
pub fn build_release_metadata() {
let version = env::var_os("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION was not set in build.rs");
let version = version
.to_str()
.expect("CARGO_PKG_VERSION was not a valid UTF-8 String");
let birth_date = birthdate();
let build_date = build_date();
let release_date = build_date;
let revision_count = revision_count();
let platform = platform();
let copyright = copyright(birth_date, build_date);
let description = description(version, release_date, revision_count, platform.as_str());
emit("RUBY_RELEASE_DATE", release_date);
emit("RUBY_RELEASE_YEAR", build_date.year);
emit("RUBY_RELEASE_MONTH", build_date.month);
emit("RUBY_RELEASE_DAY", build_date.day);
emit("RUBY_REVISION", revision_count.unwrap_or(0));
emit("RUBY_PLATFORM", platform);
emit("RUBY_COPYRIGHT", copyright);
emit("RUBY_DESCRIPTION", description);
emit("ARTICHOKE_COMPILER_VERSION", compiler_version().unwrap_or_default());
}
fn emit<T>(env: &str, value: T)
where
T: fmt::Display,
{
println!("cargo:rustc-env={env}={value}");
}
fn birthdate() -> Date {
// ```console
// $ git rev-list --format=%B --max-parents=0 trunk
// commit db318759dad41686be679c87c349fcb5ff0a396c
// Initial commit
// $ git show -s --format="%ct" db318759dad41686be679c87c349fcb5ff0a396c
// 1554600621
// $ git show -s --format="%ci" db318759dad41686be679c87c349fcb5ff0a396c
// 2019-04-06 18:30:21 -0700
// ```
let time = 1_554_600_621;
UtcDateTime::from_timespec(time, 0)
.expect("Could not construct datetime from birthdate")
.into()
}
fn build_date() -> Date {
// Enable reproducibility for `RUBY_RELEASE_DATE` and friends by respecting
// the `SOURCE_DATE_EPOCH` env variable.
//
// https://reproducible-builds.org/docs/source-date-epoch/
println!("cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH");
let datetime = if let Some(timestamp) = env::var_os("SOURCE_DATE_EPOCH") {
let seconds_since_epoch = timestamp
.into_string()
.expect("SOURCE_DATE_EPOCH was not valid UTF-8")
.parse::<i64>()
.expect("SOURCE_DATE_EPOCH was not a valid integer");
UtcDateTime::from_timespec(seconds_since_epoch, 0)
.expect("Could not construct datetime from SOURCE_DATE_EPOCH")
} else {
UtcDateTime::now().expect("Could not retreive current timestamp")
};
datetime.into()
}
fn revision_count() -> Option<usize> {
let revision_count = Command::new("git")
.arg("rev-list")
.arg("--count")
.arg("HEAD")
.output()
.ok()?;
let output = String::from_utf8(revision_count.stdout).ok()?;
output.trim().parse::<usize>().ok()
}
fn platform() -> String {
env::var_os("TARGET")
.expect("cargo-provided TARGET env var not set")
.to_str()
.expect("cargo-provided TARGET env var was not valid UTF-8")
.to_owned()
}
fn copyright(birth_date: Date, build_date: Date) -> String {
match build_date.year {
build_date_year if build_date_year == birth_date.year => format!(
"artichoke - Copyright (c) {} Ryan Lopopolo <rjl@hyperbo.la>",
build_date.year
),
build_date_year if build_date_year > birth_date.year => format!(
"artichoke - Copyright (c) {}-{} Ryan Lopopolo <rjl@hyperbo.la>",
birth_date.year, build_date.year
),
_ => format!(
"artichoke - Copyright (c) {} Ryan Lopopolo <rjl@hyperbo.la>",
birth_date.year
),
}
}
fn description(version: &str, release_date: Date, revision_count: Option<usize>, platform: &str) -> String {
if let Some(revision_count) = revision_count {
format!("artichoke {version} ({release_date} revision {revision_count}) [{platform}]",)
} else {
format!("artichoke {version} ({release_date}) [{platform}]")
}
}
fn compiler_version() -> Option<String> {
let cmd = env::var_os("RUSTC").unwrap_or_else(|| OsString::from("rustc"));
let compiler_version = Command::new(cmd).arg("-V").output().ok()?;
let compiler_version = String::from_utf8(compiler_version.stdout).ok()?;
let mut compiler_version = compiler_version.trim().to_owned();
if let Ok(compiler_host) = env::var("HOST") {
compiler_version.push_str(" on ");
compiler_version.push_str(&compiler_host);
}
Some(compiler_version)
}
fn main() {
build_release_metadata();
}