build_info_build/build_script_options/
mod.rs1use core::sync::atomic::{AtomicBool, Ordering};
2use std::path::{Path, PathBuf};
3
4use build_info_common::{OptimizationLevel, VersionedString};
5use chrono::{DateTime, Utc};
6
7pub use self::crate_info::DependencyDepth;
8use super::BuildInfo;
9
10mod compiler;
11mod crate_info;
12mod target;
13mod timestamp;
14mod version_control;
15
16pub fn cargo_toml() -> &'static Path {
17 static CARGO_TOML: std::sync::OnceLock<PathBuf> = std::sync::OnceLock::new();
18 CARGO_TOML.get_or_init(|| Path::new(&std::env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("Cargo.toml"))
19}
20
21pub struct BuildScriptOptions {
23 consumed: bool,
25
26 timestamp: Option<DateTime<Utc>>,
28
29 collect_runtime_dependencies: DependencyDepth,
31
32 collect_build_dependencies: DependencyDepth,
34
35 collect_dev_dependencies: DependencyDepth,
37}
38static BUILD_SCRIPT_RAN: AtomicBool = AtomicBool::new(false);
39
40impl BuildScriptOptions {
41 fn drop_to_build_info(&mut self) -> BuildInfo {
43 assert!(!self.consumed);
44 self.consumed = true;
45
46 let profile = std::env::var("PROFILE").unwrap_or_else(|_| "UNKNOWN".to_string());
47 let optimization_level = match std::env::var("OPT_LEVEL")
48 .expect("Expected environment variable `OPT_LEVEL` to be set by cargo")
49 .as_str()
50 {
51 "0" => OptimizationLevel::O0,
52 "1" => OptimizationLevel::O1,
53 "2" => OptimizationLevel::O2,
54 "3" => OptimizationLevel::O3,
55 "s" => OptimizationLevel::Os,
56 "z" => OptimizationLevel::Oz,
57 level => panic!("Unknown optimization level {level:?}"),
58 };
59
60 let compiler = compiler::get_info();
61 let target = target::get_info();
62 let crate_info::Manifest {
63 crate_info,
64 workspace_root,
65 } = crate_info::read_manifest(
66 &target.triple,
67 self.collect_runtime_dependencies,
68 self.collect_build_dependencies,
69 self.collect_dev_dependencies,
70 );
71 let version_control = version_control::get_info();
72
73 let timestamp = self.timestamp.unwrap_or_else(timestamp::get_timestamp);
74 let build_info = BuildInfo {
75 timestamp,
76 profile,
77 optimization_level,
78 crate_info,
79 compiler,
80 target,
81 version_control,
82 };
83
84 let mut bytes = Vec::new();
85 let mut compressed = zstd::Encoder::new(&mut bytes, 22).expect("Could not create ZSTD encoder");
86 ciborium::into_writer(&build_info, &mut compressed).unwrap();
87 compressed.finish().unwrap();
88
89 let string = z85::encode(&bytes);
90 let versioned = VersionedString::build_info_common_versioned(string);
91 let serialized = serde_json::to_string(&versioned).unwrap();
92
93 println!("cargo:rustc-env=BUILD_INFO={serialized}");
94
95 rebuild_if_project_changes(&workspace_root);
99
100 build_info
101 }
102
103 pub fn build(mut self) -> BuildInfo {
106 self.drop_to_build_info()
107 }
108}
109
110impl From<BuildScriptOptions> for BuildInfo {
111 fn from(opts: BuildScriptOptions) -> BuildInfo {
112 opts.build()
113 }
114}
115
116impl Default for BuildScriptOptions {
117 fn default() -> Self {
118 let build_script_ran = BUILD_SCRIPT_RAN.swap(true, Ordering::SeqCst);
119 assert!(!build_script_ran, "The build script may only be run once.");
120
121 Self {
122 consumed: false,
123 timestamp: None,
124 collect_runtime_dependencies: DependencyDepth::None,
125 collect_build_dependencies: DependencyDepth::None,
126 collect_dev_dependencies: DependencyDepth::None,
127 }
128 }
129}
130
131impl Drop for BuildScriptOptions {
132 fn drop(&mut self) {
133 if !self.consumed {
134 let _build_info = self.drop_to_build_info();
135 }
136 }
137}
138
139fn rebuild_if_project_changes(workspace_root: &str) {
145 println!("cargo:rerun-if-changed={}", cargo_toml().to_str().unwrap());
146 println!(
147 "cargo:rerun-if-changed={}",
148 Path::new(workspace_root).join("Cargo.lock").to_str().unwrap()
149 );
150
151 for source in glob::glob_with(
152 "**/*.rs",
153 glob::MatchOptions {
154 case_sensitive: false,
155 require_literal_separator: false,
156 require_literal_leading_dot: false,
157 },
158 )
159 .unwrap()
160 .map(|source| source.unwrap())
161 {
162 println!("cargo:rerun-if-changed={}", source.to_str().unwrap());
163 }
164}