120 lines
3.6 KiB
Rust
120 lines
3.6 KiB
Rust
use std::env;
|
|
use std::fs;
|
|
use std::path::PathBuf;
|
|
use std::time::Duration;
|
|
|
|
fn main() {
|
|
let version = resolve_app_version();
|
|
println!("cargo:rustc-env=APP_VERSION={version}");
|
|
|
|
embed_windows_icon();
|
|
|
|
slint_build::compile("ui/app.slint").expect("Slint build failed");
|
|
}
|
|
|
|
/// Shell icon for `rcalc.exe` in Explorer (needs `assets/icon.ico` + windres/rc).
|
|
fn embed_windows_icon() {
|
|
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
|
if target_os != "windows" {
|
|
return;
|
|
}
|
|
|
|
println!("cargo:rerun-if-changed=assets/icon.ico");
|
|
|
|
let mut res = winresource::WindowsResource::new();
|
|
res.set_icon("assets/icon.ico");
|
|
|
|
// Cross-compile from Linux: mingw windres is prefixed.
|
|
if cfg!(not(windows)) {
|
|
let target = env::var("TARGET").unwrap_or_default();
|
|
if target.contains("windows-gnu") {
|
|
res.set_windres_path("x86_64-w64-mingw32-windres");
|
|
}
|
|
}
|
|
|
|
res.compile()
|
|
.expect("failed to embed Windows icon (is windres/rc available?)");
|
|
}
|
|
|
|
/// Window / package version shown as `Rcalc-{version}`.
|
|
///
|
|
/// On each `release` build the patch in `Cargo.toml` is incremented once
|
|
/// (`0.1.0` → `0.1.1` → …). If Cargo restarts the build script right after we
|
|
/// rewrite `Cargo.toml`, a short mtime guard prevents a double bump.
|
|
fn resolve_app_version() -> String {
|
|
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
|
let pkg = env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "0.0.0".into());
|
|
let profile = env::var("PROFILE").unwrap_or_default();
|
|
|
|
if profile != "release" {
|
|
return pkg;
|
|
}
|
|
|
|
let cargo_toml = manifest_dir.join("Cargo.toml");
|
|
let guard_path = manifest_dir.join(".rcalc_last_bump");
|
|
let already = fs::read_to_string(&guard_path)
|
|
.map(|s| s.trim().to_string())
|
|
.unwrap_or_default();
|
|
|
|
// Cargo re-invoked us because we just rewrote Cargo.toml — keep version.
|
|
if already == pkg {
|
|
if let Ok(meta) = fs::metadata(&cargo_toml) {
|
|
if let Ok(modified) = meta.modified() {
|
|
if modified.elapsed().unwrap_or(Duration::MAX) < Duration::from_secs(15) {
|
|
return pkg;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let Some(next) = bump_patch(&pkg) else {
|
|
return pkg;
|
|
};
|
|
|
|
if let Err(e) = set_cargo_toml_version(&cargo_toml, &next) {
|
|
println!("cargo:warning=could not bump Cargo.toml version: {e}");
|
|
return pkg;
|
|
}
|
|
let _ = fs::write(&guard_path, &next);
|
|
next
|
|
}
|
|
|
|
fn bump_patch(version: &str) -> Option<String> {
|
|
let mut parts: Vec<&str> = version.split('.').collect();
|
|
if parts.is_empty() {
|
|
return None;
|
|
}
|
|
while parts.len() < 3 {
|
|
parts.push("0");
|
|
}
|
|
let patch: u64 = parts[2]
|
|
.split(['-', '+'])
|
|
.next()?
|
|
.parse()
|
|
.ok()?;
|
|
Some(format!("{}.{}.{}", parts[0], parts[1], patch + 1))
|
|
}
|
|
|
|
fn set_cargo_toml_version(path: &PathBuf, version: &str) -> std::io::Result<()> {
|
|
let raw = fs::read_to_string(path)?;
|
|
let mut out = String::with_capacity(raw.len() + 8);
|
|
let mut replaced = false;
|
|
for line in raw.lines() {
|
|
if !replaced && line.starts_with("version") && line.contains('=') {
|
|
out.push_str(&format!("version = \"{version}\""));
|
|
out.push('\n');
|
|
replaced = true;
|
|
} else {
|
|
out.push_str(line);
|
|
out.push('\n');
|
|
}
|
|
}
|
|
if !replaced {
|
|
return Err(std::io::Error::new(
|
|
std::io::ErrorKind::InvalidData,
|
|
"version field not found in Cargo.toml",
|
|
));
|
|
}
|
|
fs::write(path, out)
|
|
}
|