-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathbuild.rs
More file actions
106 lines (87 loc) · 3.43 KB
/
build.rs
File metadata and controls
106 lines (87 loc) · 3.43 KB
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
// build.rs
use regex::Regex;
use sha2::{Digest, Sha256};
use std::env;
use std::fs;
use std::process::Command;
fn main() {
// Generate checksum of this build.rs file
let build_rs_checksum = generate_build_rs_checksum().unwrap_or_else(|e| {
println!("cargo:warning=Failed to generate build.rs checksum: {}", e);
"unknown".to_string()
});
// Generate build time
let build_time = chrono::Utc::now().format("%Y-%m-%d").to_string();
// Try multiple sources for version in order of preference
let version = env::var("APP_VERSION")
.or_else(|_| get_latest_semantic_git_tag())
.or_else(|_| get_git_describe())
.unwrap_or_else(|_| env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string()));
println!("cargo:rustc-env=APP_VERSION={}", version);
println!("cargo:rustc-env=BUILD_RS_CHECKSUM={}", build_rs_checksum);
println!("cargo:rustc-env=BUILD_TIME={}", build_time);
println!("cargo:rerun-if-env-changed=APP_VERSION");
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=.git/HEAD");
println!("cargo:rerun-if-changed=.git/refs/tags");
}
fn generate_build_rs_checksum() -> Result<String, Box<dyn std::error::Error>> {
// Read the contents of build.rs
let build_rs_path = "build.rs";
let contents = fs::read_to_string(build_rs_path)?;
// Create a SHA-256 hasher
let mut hasher = Sha256::new();
hasher.update(contents.as_bytes());
let result = hasher.finalize();
// Convert to hex string
Ok(format!("{:x}", result))
}
fn get_latest_semantic_git_tag() -> Result<String, Box<dyn std::error::Error>> {
// Get all tags
let output = Command::new("git")
.args(&["tag", "--list", "--sort=-version:refname"])
.output()?;
if !output.status.success() {
return Err("Git tag command failed".into());
}
let tags_output = String::from_utf8(output.stdout)?;
// Regex pattern for semantic versioning: v followed by major.minor.patch (no pre-release suffixes)
let semver_pattern = Regex::new(r"^v(\d+)\.(\d+)\.(\d+)$")?;
// Collect and parse valid semantic version tags
let mut valid_tags: Vec<(String, (u32, u32, u32))> = Vec::new();
for line in tags_output.lines() {
let tag = line.trim();
if let Some(caps) = semver_pattern.captures(tag) {
let major: u32 = caps[1].parse()?;
let minor: u32 = caps[2].parse()?;
let patch: u32 = caps[3].parse()?;
valid_tags.push((tag.to_string(), (major, minor, patch)));
}
}
if valid_tags.is_empty() {
return Err(
"No semantic versioning tags found matching pattern v[0-9]+.[0-9]+.[0-9]+".into(),
);
}
// Sort by semantic version (major, minor, patch) in descending order
valid_tags.sort_by(|a, b| {
let (maj_a, min_a, patch_a) = a.1;
let (maj_b, min_b, patch_b) = b.1;
maj_b
.cmp(&maj_a)
.then_with(|| min_b.cmp(&min_a))
.then_with(|| patch_b.cmp(&patch_a))
});
// Return the latest version
Ok(valid_tags[0].0.clone())
}
fn get_git_describe() -> Result<String, Box<dyn std::error::Error>> {
let output = Command::new("git")
.args(&["describe", "--always", "--dirty"])
.output()?;
if output.status.success() {
Ok(String::from_utf8(output.stdout)?.trim().to_string())
} else {
Err("Git command failed".into())
}
}