1mod gitversion;
2
3use anyhow::Result;
4use gitversion::GitVersion;
5use quote::quote;
6use std::env;
7use std::fmt::Debug;
8use std::fs::File;
9use std::io::{BufWriter, Read, Write};
10use std::path::Path;
11use std::process::Command;
12use thiserror::Error;
13
14#[derive(Error, Debug)]
15pub enum Error {
16 #[error("I/O error")]
17 Io(#[from] std::io::Error),
18
19 #[error("environment variable is missing")]
20 MissingEnvVar,
21}
22
23fn same_content_as(path: &Path, content: &str) -> Result<bool> {
24 let mut f = File::open(path)?;
25 let mut current = String::new();
26 f.read_to_string(&mut current)?;
27
28 Ok(current == content)
29}
30
31#[allow(unused)]
32macro_rules! include_gitversion_from_path {
33 ($path: tt) => {
34 include!($path);
35 };
36}
37
38pub fn build() -> Result<GitVersion> {
41 let path = env::var_os("OUT_DIR").ok_or(Error::MissingEnvVar)?;
42 let path: &Path = path.as_ref();
43 let path = path.join("gitversion.rs");
44 write_version_file(path.as_path())
45}
46
47fn dotnet_gitversion() -> Option<String> {
48 Command::new("dotnet-gitversion")
49 .args(&["/nofetch"])
50 .output()
51 .ok()
52 .and_then(|out| {
53 std::str::from_utf8(&out.stdout[..])
54 .map(str::trim)
55 .map(str::to_owned)
56 .ok()
57 })
58}
59
60#[allow(deprecated)]
62fn write_version_file(path: &Path) -> Result<GitVersion> {
63 let content = if let Some(json) = dotnet_gitversion() {
64 json.to_owned()
65 } else {
66 "{}".to_owned()
67 };
68
69 let gv: GitVersion = serde_json::from_str(content.as_str())?;
70
71 let major = gv.major;
72 let minor = gv.minor;
73 let patch = gv.patch;
74 println!("cargo:rustc-env=GITVERSION_MAJOR={}", major.to_string());
75 println!("cargo:rustc-env=GITVERSION_MINOR={}", minor.to_string());
76 println!("cargo:rustc-env=GITVERSION_PATCH={}", patch.to_string());
77
78 let pre_release_tag = gv.pre_release_tag.clone();
79 let pre_release_tag_with_dash = gv.pre_release_tag_with_dash.clone();
80 println!(
81 "cargo:rustc-env=GITVERSION_PRE_RELEASE_TAG={}",
82 pre_release_tag.clone()
83 );
84 println!(
85 "cargo:rustc-env=GITVERSION_PRE_RELEASE_TAG_WITH_DASH={}",
86 pre_release_tag_with_dash.clone()
87 );
88
89 let pre_release_label = gv.pre_release_label.clone();
90 let pre_release_label_with_dash = gv.pre_release_label_with_dash.clone();
91 println!(
92 "cargo:rustc-env=GITVERSION_PRE_RELEASE_LABEL={}",
93 pre_release_label.clone()
94 );
95 println!(
96 "cargo:rustc-env=GITVERSION_PRE_RELEASE_LABEL_WITH_DASH={}",
97 pre_release_label_with_dash.clone()
98 );
99
100 let has_pre_release_number = gv.pre_release_number != None;
101 let pre_release_number = gv.pre_release_number.unwrap_or(0);
102 if let Some(number) = gv.pre_release_number {
103 println!(
104 "cargo:rustc-env=GITVERSION_PRE_RELEASE_NUMBER={}",
105 number.to_string()
106 );
107 }
108
109 let weighted_pre_release_number = gv.weighted_pre_release_number;
110 println!(
111 "cargo:rustc-env=GITVERSION_WEIGHTED_PRE_RELEASE_NUMBER={}",
112 weighted_pre_release_number.to_string()
113 );
114
115 let has_build_meta_data = gv.build_meta_data != None;
116 let build_meta_data = gv.build_meta_data.unwrap_or(0);
117 if let Some(number) = gv.build_meta_data {
118 println!(
119 "cargo:rustc-env=GITVERSION_BUILD_META_DATA={}",
120 number.to_string()
121 );
122 }
123
124 let build_meta_data_padded = gv.build_meta_data_padded.clone();
125 println!(
126 "cargo:rustc-env=GITVERSION_BUILD_META_DATA_PADDED={}",
127 build_meta_data_padded.clone()
128 );
129
130 let full_build_meta_data = gv.full_build_meta_data.clone();
131 println!(
132 "cargo:rustc-env=GITVERSION_FULL_BUILD_META_DATA={}",
133 full_build_meta_data.clone()
134 );
135
136 let major_minor_patch = gv.major_minor_patch.clone();
137 println!(
138 "cargo:rustc-env=GITVERSION_MAJOR_MINOR_PATCH={}",
139 major_minor_patch.clone()
140 );
141
142 let semver = gv.semver.clone();
143 println!("cargo:rustc-env=GITVERSION_SEMVER={}", semver.clone());
144
145 let legacy_semver = gv.legacy_semver.clone();
146 let legacy_semver_padded = gv.legacy_semver_padded.clone();
147 println!(
148 "cargo:rustc-env=GITVERSION_LEGACY_SEMVER={}",
149 legacy_semver.clone()
150 );
151 println!(
152 "cargo:rustc-env=GITVERSION_LEGACY_SEMVER_PADDED={}",
153 legacy_semver_padded.clone()
154 );
155
156 let assembly_semver = gv.assembly_semver.clone();
157 println!(
158 "cargo:rustc-env=GITVERSION_ASSEMBLY_SEMVER={}",
159 assembly_semver.clone()
160 );
161
162 let assembly_sem_file_version = gv.assembly_sem_file_version.clone();
163 println!(
164 "cargo:rustc-env=GITVERSION_ASSEMBLY_SEM_FILE_VERSION={}",
165 assembly_sem_file_version.clone()
166 );
167
168 let informational_version = gv.informational_version.clone();
169 println!(
170 "cargo:rustc-env=GITVERSION_INFORMATIONAL_VERSION={}",
171 informational_version.clone()
172 );
173
174 let full_semver = gv.full_semver.clone();
175 println!(
176 "cargo:rustc-env=GITVERSION_FULL_SEMVER={}",
177 full_semver.clone()
178 );
179
180 let branch_name = gv.branch_name.clone();
181 println!(
182 "cargo:rustc-env=GITVERSION_BRANCH_NAME={}",
183 branch_name.clone()
184 );
185
186 let escaped_branch_name = gv.escaped_branch_name.clone();
187 println!(
188 "cargo:rustc-env=GITVERSION_ESCAPED_BRANCH_NAME={}",
189 escaped_branch_name.clone()
190 );
191
192 let sha = gv.sha.clone();
193 println!("cargo:rustc-env=GITVERSION_SHA={}", sha.clone());
194
195 let short_sha = gv.short_sha.clone();
196 println!("cargo:rustc-env=GITVERSION_SHORT_SHA={}", short_sha.clone());
197
198 let nuget_version_v2 = gv.nuget_version_v2.clone();
199 println!(
200 "cargo:rustc-env=GITVERSION_NUGET_VERSION_V2={}",
201 nuget_version_v2.clone()
202 );
203
204 let nuget_version = gv.nuget_version.clone();
205 println!(
206 "cargo:rustc-env=GITVERSION_NUGET_VERSION={}",
207 nuget_version.clone()
208 );
209
210 let nuget_prerelease_tag_v2 = gv.nuget_prerelease_tag_v2.clone();
211 println!(
212 "cargo:rustc-env=GITVERSION_NUGET_PRERELEASE_TAG_V2={}",
213 nuget_prerelease_tag_v2.clone()
214 );
215
216 let nuget_prerelease_tag = gv.nuget_prerelease_tag.clone();
217 println!(
218 "cargo:rustc-env=GITVERSION_NUGET_PRERELEASE_TAG={}",
219 nuget_prerelease_tag.clone()
220 );
221
222 let version_source_sha = gv.version_source_sha.clone();
223 println!(
224 "cargo:rustc-env=GITVERSION_VERSION_SOURCE_SHA={}",
225 version_source_sha.clone()
226 );
227
228 let commits_since_version_source = gv.commits_since_version_source;
229 println!(
230 "cargo:rustc-env=GITVERSION_COMMITS_SINCE_VERSION_SOURCE={}",
231 commits_since_version_source.clone()
232 );
233
234 let commits_since_version_source_padded = gv.commits_since_version_source_padded.clone();
235 println!(
236 "cargo:rustc-env=GITVERSION_COMMITS_SINCE_VERSION_SOURCE_PADDED={}",
237 commits_since_version_source_padded.clone()
238 );
239
240 let uncommitted_changes = gv.uncommitted_changes;
241 println!(
242 "cargo:rustc-env=GITVERSION_UNCOMMITTED_CHANGES={}",
243 uncommitted_changes.to_string()
244 );
245
246 let commit_date = gv.commit_date.clone();
247 println!(
248 "cargo:rustc-env=GITVERSION_COMMIT_DATE={}",
249 commit_date.clone()
250 );
251
252 let tokens = quote! {
253 #[allow(dead_code)]
254 pub struct GitVersion {
255 pub major: u32,
257 pub minor: u32,
259 pub patch: u32,
261 pub pre_release_tag: &'static str,
263 pub pre_release_tag_with_dash: &'static str,
265 pub pre_release_label: &'static str,
267 pub pre_release_label_with_dash: &'static str,
269 pub pre_release_number: Option<u32>,
271 pub weighted_pre_release_number: u32,
274 pub build_meta_data: Option<u32>,
276 pub build_meta_data_padded: &'static str,
278 pub full_build_meta_data: &'static str,
280 pub major_minor_patch: &'static str,
282 pub semver: &'static str,
284 #[deprecated]
286 pub legacy_semver: &'static str,
287 #[deprecated]
289 pub legacy_semver_padded: &'static str,
290 #[deprecated]
294 pub assembly_semver: &'static str,
295 #[deprecated]
298 pub assembly_sem_file_version: &'static str,
299 pub informational_version: &'static str,
302 pub full_semver: &'static str,
304 pub branch_name: &'static str,
306 pub escaped_branch_name: &'static str,
308 pub sha: &'static str,
310 pub short_sha: &'static str,
312 #[deprecated]
314 pub nuget_version_v2: &'static str,
315 #[deprecated]
317 pub nuget_version: &'static str,
318 #[deprecated]
320 pub nuget_prerelease_tag_v2: &'static str,
321 #[deprecated]
323 pub nuget_prerelease_tag: &'static str,
324 pub version_source_sha: &'static str,
326 pub commits_since_version_source: u32,
328 pub commits_since_version_source_padded: &'static str,
330 pub uncommitted_changes: u32,
332 pub commit_date: &'static str,
334 }
335
336 #[allow(dead_code)]
337 impl GitVersion {
338 #[allow(deprecated)]
340 pub const fn new() -> GitVersion {
341 GitVersion {
342 major: #major,
343 minor: #minor,
344 patch: #patch,
345 pre_release_tag: #pre_release_tag,
346 pre_release_tag_with_dash: #pre_release_tag_with_dash,
347 pre_release_label: #pre_release_label,
348 pre_release_label_with_dash: #pre_release_label_with_dash,
349 pre_release_number: if #has_pre_release_number { Some( #pre_release_number ) } else { None },
350 weighted_pre_release_number: #weighted_pre_release_number,
351 build_meta_data: if #has_build_meta_data { Some( #build_meta_data ) } else { None },
352 build_meta_data_padded: #build_meta_data_padded,
353 full_build_meta_data: #full_build_meta_data,
354 major_minor_patch: #major_minor_patch,
355 semver: #semver,
356 legacy_semver: #legacy_semver,
357 legacy_semver_padded: #legacy_semver_padded,
358 assembly_semver: #assembly_semver,
359 assembly_sem_file_version: #assembly_sem_file_version,
360 informational_version: #informational_version,
361 full_semver: #full_semver,
362 branch_name: #branch_name,
363 escaped_branch_name: #escaped_branch_name,
364 sha: #sha,
365 short_sha: #short_sha,
366 nuget_version_v2: #nuget_version_v2,
367 nuget_version: #nuget_version,
368 nuget_prerelease_tag_v2: #nuget_prerelease_tag_v2,
369 nuget_prerelease_tag: #nuget_prerelease_tag,
370 version_source_sha: #version_source_sha,
371 commits_since_version_source: #commits_since_version_source,
372 commits_since_version_source_padded: #commits_since_version_source_padded,
373 uncommitted_changes: #uncommitted_changes,
374 commit_date: #commit_date
375 }
376 }
377 }
378
379 #[allow(dead_code)]
380 impl Default for GitVersion {
381 fn default() -> Self {
382 GitVersion::new()
383 }
384 }
385
386 #[allow(dead_code)]
387 pub const GIT_VERSION: GitVersion = GitVersion::new();
388
389 impl std::fmt::Display for GitVersion {
390 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
391 write!(f, "{}", self.semver)
392 }
393 }
394
395 impl std::fmt::Debug for GitVersion {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 write!(f, "{}", self.informational_version)
398 }
399 }
400 };
401
402 let code = tokens.to_string();
403 let is_fresh = if path.exists() {
404 same_content_as(&path, &code)?
405 } else {
406 false
407 };
408
409 if !is_fresh {
410 let mut file = BufWriter::new(File::create(&path)?);
411 write!(file, "{}", code)?;
412 }
413 Ok(gv)
414}
415
416#[cfg(test)]
417mod test {
418 use super::*;
419 use tempfile::NamedTempFile;
420
421 #[test]
422 pub fn json_is_created() {
423 let result = dotnet_gitversion().expect("dotnet_gitversion");
424 assert!(result.len() > 8);
425 }
426
427 #[test]
428 pub fn write_file() -> Result<()> {
429 let mut file = NamedTempFile::new()?;
430 write_version_file(file.path());
431 Ok(())
432 }
433}