// embeds assets/Icon.svg into the windows .exe via rsvg-convert, magick, llvm-windres, and rustc-link-arg-bins. fn main() { let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); if target_os != "windows" { return; } let svg = "assets/Icon.svg"; let out_dir = std::env::var("OUT_DIR").expect("cargo OUT_DIR"); let tmp = format!("{out_dir}/icon_tmp"); let ico = format!("{out_dir}/icon.ico"); let rc = format!("{out_dir}/icon.rc"); let res = format!("{out_dir}/icon.res"); println!("cargo:rerun-if-changed={svg}"); println!("cargo:rerun-if-changed=build.rs"); if !std::path::Path::new(svg).exists() { println!("cargo:warning=no {svg} — skipping windows icon embed"); return; } let _ = std::fs::create_dir_all(&tmp); let sizes = [16, 24, 32, 48, 64, 128, 256]; let mut pngs: Vec = Vec::new(); for size in sizes { let out = format!("{tmp}/icon_{size}.png"); let s = size.to_string(); if !run(&["rsvg-convert", "--width", &s, "--height", &s, svg, "-o", &out]) { println!("cargo:warning=rsvg-convert failed — windows icon not embedded (brew install librsvg)"); let _ = std::fs::remove_dir_all(&tmp); return; } pngs.push(out); } let mut magick_args: Vec<&str> = pngs.iter().map(|s| s.as_str()).collect(); magick_args.push(&ico); if !run_vec("magick", &magick_args) { println!("cargo:warning=magick failed — windows icon not embedded (brew install imagemagick)"); let _ = std::fs::remove_dir_all(&tmp); return; } let _ = std::fs::remove_dir_all(&tmp); if std::fs::write(&rc, "1 ICON \"icon.ico\"\r\n").is_err() { println!("cargo:warning=could not write {rc} — windows icon not embedded"); return; } let Some(windres) = locate_llvm_windres() else { println!("cargo:warning=llvm-windres not found — windows icon not embedded (brew install llvm)"); return; }; if !run(&[&windres, "-I", &out_dir, &rc, "-o", &res]) { println!("cargo:warning=llvm-windres failed — windows icon not embedded"); return; } println!("cargo:rustc-link-arg-bins={res}"); } /// resolves llvm-windres from $LLVM_WINDRES, Homebrew's keg-only llvm prefix on Apple Silicon and Intel, or $PATH. fn locate_llvm_windres() -> Option { if let Ok(p) = std::env::var("LLVM_WINDRES") { if std::path::Path::new(&p).exists() { return Some(p); } } let candidates = [ "/opt/homebrew/opt/llvm/bin/llvm-windres", "/usr/local/opt/llvm/bin/llvm-windres", ]; for c in candidates { if std::path::Path::new(c).exists() { return Some(c.to_string()); } } if run(&["sh", "-c", "command -v llvm-windres"]) { return Some("llvm-windres".to_string()); } None } fn run(args: &[&str]) -> bool { std::process::Command::new(args[0]) .args(&args[1..]) .status() .map(|s| s.success()) .unwrap_or(false) } fn run_vec(cmd: &str, args: &[&str]) -> bool { std::process::Command::new(cmd) .args(args) .status() .map(|s| s.success()) .unwrap_or(false) }