fn main() {
// Compile Windows resources (version info) when building on Windows
#[cfg(windows)]
{
// Robust resource compile: invoke rc.exe -> cvtres.exe -> lib.exe with proper SDK include paths.
use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
fn latest_subdir(parent: &Path) -> Option<PathBuf> {
let mut versions = std::fs::read_dir(parent)
.ok()?
.filter_map(|e| e.ok())
.filter(|e| e.path().is_dir())
.map(|e| e.path())
.collect::<Vec<_>>();
versions.sort();
versions.pop()
}
let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".into());
let manifest = PathBuf::from(&manifest_dir);
let rc_input = manifest.join("resources").join("windows.rc");
if !rc_input.exists() {
println!("cargo:warning=resources/windows.rc not found, skipping resource compile");
} else {
// Find Windows Kits include/version
let kits_base1 = Path::new("C:/Program Files (x86)/Windows Kits/10");
let kits_base2 = Path::new("C:/Program Files/Windows Kits/10");
let kits = if kits_base1.exists() { kits_base1 } else { kits_base2 };
let include_root = if kits.exists() {
latest_subdir(&kits.join("Include")).map(|p| p).map(|p| p)
} else {
None
};
// assemble include paths
let mut include_um = PathBuf::new();
let mut include_shared = PathBuf::new();
let mut include_ucrt = PathBuf::new();
if let Some(inc_ver) = include_root {
include_um = kits.join("Include").join(inc_ver.file_name().unwrap()).join("um");
include_shared = kits.join("Include").join(inc_ver.file_name().unwrap()).join("shared");
include_ucrt = kits.join("Include").join(inc_ver.file_name().unwrap()).join("ucrt");
}
// Find rc.exe under Kits bin (scan subdirs)
let mut rc_exe: Option<PathBuf> = None;
if kits.exists() {
if let Ok(entries) = std::fs::read_dir(kits.join("bin")) {
for e in entries.filter_map(|x| x.ok()) {
let p = e.path();
let candidate = p.join("x64").join("rc.exe");
if candidate.exists() {
rc_exe = Some(candidate);
break;
}
}
}
}
// Find MSVC tools (cvtres, lib)
let msvc_root = Path::new("C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC");
let mut cvtres_exe: Option<PathBuf> = None;
let mut lib_exe: Option<PathBuf> = None;
if msvc_root.exists() {
if let Some(msvc_ver) = latest_subdir(msvc_root) {
let bin = msvc_root.join(msvc_ver.file_name().unwrap()).join("bin").join("Hostx64").join("x64");
let c = bin.join("cvtres.exe");
let l = bin.join("lib.exe");
if c.exists() { cvtres_exe = Some(c); }
if l.exists() { lib_exe = Some(l); }
}
}
// Fallback: try to find in PATH
if rc_exe.is_none() {
if let Ok(p) = which::which("rc.exe") { rc_exe = Some(p); }
}
if cvtres_exe.is_none() {
if let Ok(p) = which::which("cvtres.exe") { cvtres_exe = Some(p); }
}
if lib_exe.is_none() {
if let Ok(p) = which::which("lib.exe") { lib_exe = Some(p); }
}
// Prepare output paths
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
let res_out = out_dir.join("windows.res");
let obj_out = out_dir.join("windows.res.obj");
let lib_out = out_dir.join("resource.lib");
// Run rc.exe
if let Some(rc) = rc_exe {
println!("cargo:warning=Using rc.exe at: {}", rc.display());
let mut cmd = Command::new(&rc);
if include_um.exists() { cmd.arg("-I").arg(&include_um); }
if include_shared.exists() { cmd.arg("-I").arg(&include_shared); }
if include_ucrt.exists() { cmd.arg("-I").arg(&include_ucrt); }
cmd.arg("-fo").arg(&res_out).arg(&rc_input);
match cmd.output() {
Ok(out) => {
if !out.status.success() {
let stderr = String::from_utf8_lossy(&out.stderr);
println!("cargo:warning=rc.exe failed (status={})", out.status);
for line in stderr.lines() { println!("cargo:warning=rc: {}", line); }
} else {
let stdout = String::from_utf8_lossy(&out.stdout);
for line in stdout.lines() { println!("cargo:warning=rc: {}", line); }
}
}
Err(e) => {
println!("cargo:warning=failed to spawn rc.exe: {}", e);
}
}
} else {
println!("cargo:warning=rc.exe not found; skipping resource compile");
}
// Run cvtres.exe to convert to object
if let Some(cvt) = cvtres_exe {
println!("cargo:warning=Using cvtres.exe at: {}", cvt.display());
match Command::new(&cvt)
.arg("/machine:x64")
.arg(format!("/OUT:{}", obj_out.display()))
.arg(&res_out)
.output()
{
Ok(out) => {
if !out.status.success() {
let stderr = String::from_utf8_lossy(&out.stderr);
println!("cargo:warning=cvtres.exe failed (status={})", out.status);
for line in stderr.lines() { println!("cargo:warning=cvtres: {}", line); }
} else {
let stdout = String::from_utf8_lossy(&out.stdout);
for line in stdout.lines() { println!("cargo:warning=cvtres: {}", line); }
}
}
Err(e) => {
println!("cargo:warning=failed to spawn cvtres.exe: {}", e);
}
}
} else {
println!("cargo:warning=cvtres.exe not found; skipping resource conversion");
}
// Run lib.exe to create .lib
if let Some(libtool) = lib_exe {
println!("cargo:warning=Using lib.exe at: {}", libtool.display());
match Command::new(&libtool)
.arg("/NOLOGO")
.arg(format!("/OUT:{}", lib_out.display()))
.arg(&obj_out)
.output()
{
Ok(out) => {
if !out.status.success() {
let stderr = String::from_utf8_lossy(&out.stderr);
println!("cargo:warning=lib.exe failed (status={})", out.status);
for line in stderr.lines() { println!("cargo:warning=lib: {}", line); }
} else {
let stdout = String::from_utf8_lossy(&out.stdout);
for line in stdout.lines() { println!("cargo:warning=lib: {}", line); }
}
}
Err(e) => {
println!("cargo:warning=failed to spawn lib.exe: {}", e);
}
}
} else {
println!("cargo:warning=lib.exe not found; skipping final lib creation");
}
println!("cargo:rerun-if-changed=resources/windows.rc");
}
}
tauri_build::build()
}