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
use base64::write::EncoderWriter as Base64Encoder;
use pretty_assertions::assert_eq;
use xz2::write::XzEncoder;
use core::sync::atomic::{AtomicBool, Ordering};
use std::path::Path;
use build_info_common::VersionedString;
use super::chrono::{DateTime, Utc};
use super::BuildInfo;
mod compiler;
mod crate_info;
mod timestamp;
mod version_control;
pub struct BuildScriptOptions {
consumed: bool,
timestamp: Option<DateTime<Utc>>,
}
static BUILD_SCRIPT_RAN: AtomicBool = AtomicBool::new(false);
impl BuildScriptOptions {
fn to_build_info(&mut self) -> BuildInfo {
assert_eq!(self.consumed, false);
self.consumed = true;
rebuild_if_project_changes();
let profile = std::env::var("PROFILE").unwrap_or_else(|_| "UNKNOWN".to_string());
let optimization_level = std::env::var("OPT_LEVEL")
.expect("Expected environment variable `OPT_LEVEL` to be set by cargo")
.parse()
.expect("Expected environment variable `OPT_LEVEL` to be set to a number by cargo");
let compiler = compiler::get_info();
let crate_info = crate_info::read_manifest(&compiler.target_triple);
let version_control = version_control::get_info();
let timestamp = self.timestamp.unwrap_or_else(timestamp::get_timestamp);
let build_info = BuildInfo {
timestamp,
profile,
optimization_level,
crate_info,
compiler,
version_control,
};
let mut bytes = Vec::new();
let string_safe = Base64Encoder::new(&mut bytes, base64::STANDARD_NO_PAD);
let mut compressed = XzEncoder::new(string_safe, 9);
bincode::serialize_into(&mut compressed, &build_info).unwrap();
compressed.finish().unwrap().finish().unwrap();
let string = String::from_utf8(bytes).unwrap();
let versioned = VersionedString::build_info_common_versioned(string);
let serialized = serde_json::to_string(&versioned).unwrap();
println!("cargo:rustc-env=BUILD_INFO={}", serialized);
build_info
}
pub fn build(mut self) -> BuildInfo {
self.to_build_info()
}
}
impl From<BuildScriptOptions> for BuildInfo {
fn from(opts: BuildScriptOptions) -> BuildInfo {
opts.build()
}
}
impl Default for BuildScriptOptions {
fn default() -> Self {
let build_script_ran = BUILD_SCRIPT_RAN.compare_and_swap(false, true, Ordering::Relaxed);
assert_eq!(build_script_ran, false, "The build script may only be run once.");
Self {
consumed: false,
timestamp: None,
}
}
}
impl Drop for BuildScriptOptions {
fn drop(&mut self) {
if !self.consumed {
let _build_info = self.to_build_info();
}
}
}
fn rebuild_if_project_changes() {
println!("cargo:rerun-if-changed=Cargo.toml");
if Path::new("Cargo.lock").is_file() {
println!("cargo:rerun-if-changed=Cargo.lock");
} else if Path::new("../Cargo.lock").is_file() {
println!("cargo:rerun-if-changed=../Cargo.lock");
}
for source in glob::glob_with(
"**/*.rs",
glob::MatchOptions {
case_sensitive: false,
require_literal_separator: false,
require_literal_leading_dot: false,
},
)
.unwrap()
.map(|source| source.unwrap())
{
println!("cargo:rerun-if-changed={}", source.to_string_lossy());
}
}