Newer
Older
TelosDB / src / backend / build.rs
@楽曲作りまくりおじさん 楽曲作りまくりおじさん 15 hours ago 8 KB build: improve Windows resource compile logging and error messages
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()
}