1use std::path::PathBuf;
2use std::process::Command;
3use std::str;
4
5/// This macro creates the version string during compilation from the
6/// current environment
7#[macro_export]
8macro_rules! get_version_info {
9 () => {{
10 let major = std::env!("CARGO_PKG_VERSION_MAJOR").parse::<u8>().unwrap();
11 let minor = std::env!("CARGO_PKG_VERSION_MINOR").parse::<u8>().unwrap();
12 let patch = std::env!("CARGO_PKG_VERSION_PATCH").parse::<u16>().unwrap();
13 let crate_name = String::from(std::env!("CARGO_PKG_NAME"));
14
15 let host_compiler = std::option_env!("RUSTC_RELEASE_CHANNEL").map(str::to_string);
16 let commit_hash = std::option_env!("GIT_HASH").map(str::to_string);
17 let commit_date = std::option_env!("COMMIT_DATE").map(str::to_string);
18
19 $crate::VersionInfo {
20 major,
21 minor,
22 patch,
23 host_compiler,
24 commit_hash,
25 commit_date,
26 crate_name,
27 }
28 }};
29}
30
31/// This macro can be used in `build.rs` to automatically set the needed
32/// environment values, namely `GIT_HASH`, `COMMIT_DATE` and
33/// `RUSTC_RELEASE_CHANNEL`
34#[macro_export]
35macro_rules! setup_version_info {
36 () => {{
37 let _ = $crate::rerun_if_git_changes();
38 println!(
39 "cargo:rustc-env=GIT_HASH={}",
40 $crate::get_commit_hash().unwrap_or_default()
41 );
42 println!(
43 "cargo:rustc-env=COMMIT_DATE={}",
44 $crate::get_commit_date().unwrap_or_default()
45 );
46 println!("cargo:rustc-env=RUSTC_RELEASE_CHANNEL={}", $crate::get_channel());
47 }};
48}
49
50// some code taken and adapted from RLS and cargo
51pub struct VersionInfo {
52 pub major: u8,
53 pub minor: u8,
54 pub patch: u16,
55 pub host_compiler: Option<String>,
56 pub commit_hash: Option<String>,
57 pub commit_date: Option<String>,
58 pub crate_name: String,
59}
60
61impl std::fmt::Display for VersionInfo {
62 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
63 let hash: String = self.commit_hash.clone().unwrap_or_default();
64 let hash_trimmed: &str = hash.trim();
65
66 let date: String = self.commit_date.clone().unwrap_or_default();
67 let date_trimmed: &str = date.trim();
68
69 if (hash_trimmed.len() + date_trimmed.len()) > 0 {
70 write!(
71 f,
72 "{} {}.{}.{} ({hash_trimmed} {date_trimmed})",
73 self.crate_name, self.major, self.minor, self.patch,
74 )?;
75 } else {
76 write!(f, "{} {}.{}.{}", self.crate_name, self.major, self.minor, self.patch)?;
77 }
78
79 Ok(())
80 }
81}
82
83impl std::fmt::Debug for VersionInfo {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 write!(
86 f,
87 "VersionInfo {{ crate_name: \"{}\", major: {}, minor: {}, patch: {}",
88 self.crate_name, self.major, self.minor, self.patch,
89 )?;
90 if self.commit_hash.is_some() {
91 write!(
92 f,
93 ", commit_hash: \"{}\", commit_date: \"{}\" }}",
94 self.commit_hash.clone().unwrap_or_default().trim(),
95 self.commit_date.clone().unwrap_or_default().trim()
96 )?;
97 } else {
98 write!(f, " }}")?;
99 }
100
101 Ok(())
102 }
103}
104
105#[must_use]
106fn get_output(cmd: &str, args: &[&str]) -> Option<String> {
107 let output: Output = Command::new(program:cmd).args(args).output().ok()?;
108 let mut stdout: Vec = output.status.success().then_some(output.stdout)?;
109 // Remove trailing newlines.
110 while stdout.last().copied() == Some(b'\n') {
111 stdout.pop();
112 }
113 String::from_utf8(vec:stdout).ok()
114}
115
116#[must_use]
117pub fn rerun_if_git_changes() -> Option<()> {
118 // Make sure we get rerun when the git commit changes.
119 // We want to watch two files: HEAD, which tracks which branch we are on,
120 // and the file for that branch that tracks which commit is is on.
121
122 // First, find the `HEAD` file. This should work even with worktrees.
123 let git_head_file: PathBuf = PathBuf::from(get_output(cmd:"git", &["rev-parse", "--git-path", "HEAD"])?);
124 if git_head_file.exists() {
125 println!("cargo::rerun-if-changed={}", git_head_file.display());
126 }
127
128 // Determine the name of the current ref.
129 // This will quit if HEAD is detached.
130 let git_head_ref: String = get_output(cmd:"git", &["symbolic-ref", "-q", "HEAD"])?;
131 // Ask git where this ref is stored.
132 let git_head_ref_file: PathBuf = PathBuf::from(get_output(cmd:"git", &["rev-parse", "--git-path", &git_head_ref])?);
133 // If this ref is packed, the file does not exist. However, the checked-out branch is never (?)
134 // packed, so we should always be able to find this file.
135 if git_head_ref_file.exists() {
136 println!("cargo::rerun-if-changed={}", git_head_ref_file.display());
137 }
138
139 Some(())
140}
141
142#[must_use]
143pub fn get_commit_hash() -> Option<String> {
144 let mut stdout: String = get_output(cmd:"git", &["rev-parse", "HEAD"])?;
145 stdout.truncate(new_len:10);
146 Some(stdout)
147}
148
149#[must_use]
150pub fn get_commit_date() -> Option<String> {
151 get_output(cmd:"git", &["log", "-1", "--date=short", "--pretty=format:%cd"])
152}
153
154#[must_use]
155pub fn get_channel() -> String {
156 if let Ok(channel: String) = std::env::var(key:"CFG_RELEASE_CHANNEL") {
157 return channel;
158 }
159
160 // if that failed, try to ask rustc -V, do some parsing and find out
161 if let Some(rustc_output: String) = get_output(cmd:"rustc", &["-V"]) {
162 if rustc_output.contains("beta") {
163 return String::from("beta");
164 } else if rustc_output.contains("stable") {
165 return String::from("stable");
166 }
167 }
168
169 // default to nightly
170 String::from("nightly")
171}
172
173#[cfg(test)]
174mod test {
175 use super::*;
176
177 #[test]
178 fn test_struct_local() {
179 let vi = get_version_info!();
180 assert_eq!(vi.major, 0);
181 assert_eq!(vi.minor, 4);
182 assert_eq!(vi.patch, 0);
183 assert_eq!(vi.crate_name, "rustc_tools_util");
184 // hard to make positive tests for these since they will always change
185 assert!(vi.commit_hash.is_none());
186 assert!(vi.commit_date.is_none());
187 }
188
189 #[test]
190 fn test_display_local() {
191 let vi = get_version_info!();
192 assert_eq!(vi.to_string(), "rustc_tools_util 0.4.0");
193 }
194
195 #[test]
196 fn test_debug_local() {
197 let vi = get_version_info!();
198 let s = format!("{vi:?}");
199 assert_eq!(
200 s,
201 "VersionInfo { crate_name: \"rustc_tools_util\", major: 0, minor: 4, patch: 0 }"
202 );
203 }
204}
205

Provided by KDAB

Privacy Policy
Learn Rust with the experts
Find out more