//! Compiles the blueprint UI files with `blueprint-compiler` and bundles them //! (plus CSS and icons) into a GResource, mirroring //! ZIG/src/apprt/gtk/build/{blueprint,gresource}.zig. #![allow(clippy::unwrap_used)] use std::{ fmt::Write as _, path::{Path, PathBuf}, process::Command, }; const PREFIX: &str = "/com/mitchellh/ghostty"; const APP_ID: &str = "com.mitchellh.ghostty"; const ICON_SIZES: &[u32] = &[16, 32, 128, 256, 512, 1024]; const CSS: &[&str] = &[ "style.css", "style-dark.css", "style-hc.css", "style-hc-dark.css", ]; // (major, minor, name). const BLUEPRINTS: &[(u32, u32, &str)] = &[ (1, 0, "clipboard-confirmation-dialog"), (1, 4, "clipboard-confirmation-dialog"), (1, 2, "close-confirmation-dialog"), (1, 2, "config-errors-dialog"), (1, 2, "debug-warning"), (1, 3, "debug-warning"), (1, 2, "resize-overlay"), (1, 2, "search-overlay"), (1, 2, "key-state-overlay"), (1, 5, "split-tree"), (1, 5, "split-tree-split"), (1, 2, "surface"), (1, 5, "surface-scrolled-window"), (1, 3, "surface-child-exited"), (1, 5, "tab"), (1, 5, "title-dialog"), (1, 5, "window"), (1, 5, "command-palette"), (1, 5, "imgui-widget"), (1, 5, "inspector-widget"), (1, 5, "inspector-window"), ]; /// Emits `GHOSTTY__VERSION` with the version of a library as reported /// by pkg-config (the headers we compile against; Zig reads the same values /// from the C headers). Falls back to the feature level we require. fn emit_pkg_version(env_name: &str, pkg: &str, fallback: &str) { println!("cargo:rerun-if-env-changed=PKG_CONFIG_PATH"); let version = Command::new("pkg-config") .args(["--modversion", pkg]) .output() .ok() .filter(|o| o.status.success()) .and_then(|o| String::from_utf8(o.stdout).ok()) .map(|s| s.trim().to_string()) .filter(|s| !s.is_empty()) .unwrap_or_else(|| fallback.to_string()); println!("cargo:rustc-env={env_name}={version}"); } fn main() { emit_pkg_version("GHOSTTY_GTK_VERSION", "gtk4", "4.14.0"); emit_pkg_version("GHOSTTY_ADW_VERSION", "libadwaita-1", "1.5.0"); let manifest = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let out = PathBuf::from(std::env::var("OUT_DIR").unwrap()); let res = manifest.join("resources"); println!("cargo:rerun-if-changed=resources"); println!("cargo:rerun-if-changed=build.rs"); // Source tarballs ship the prebuilt GResource bundle so packagers need // neither blueprint-compiler nor glib-compile-resources (Zig: // `GhosttyDist.Resource` for `ghostty_resources.{c,h}`). As in Zig, it is // never used in a git checkout. let prebuilt = manifest.join("dist/ghostty.gresource"); if prebuilt.is_file() && !manifest.join("../../.git").exists() { // Watch the bundle only when it is an input. In a checkout it is // normally absent, and watching a missing file forces every build // to recompile the resources and all dependent crates. println!("cargo:rerun-if-changed=dist/ghostty.gresource"); std::fs::copy(&prebuilt, out.join("ghostty.gresource")).unwrap(); return; } let mut xml = String::new(); xml.push_str("\n\n"); let _ = writeln!(xml, " "); for name in CSS { let _ = writeln!( xml, " css/{name}" ); } xml.push_str(" \n"); let _ = writeln!(xml, " "); for &s in ICON_SIZES { let _ = writeln!( xml, " icons/{s}.png" ); let _ = writeln!( xml, " icons/{}.png", s * 2 ); } xml.push_str(" \n"); let _ = writeln!(xml, " "); for &(major, minor, name) in BLUEPRINTS { let rel = format!("{major}.{minor}/{name}"); let input = res.join(format!("ui/{rel}.blp")); let output = out.join(format!("ui/{rel}.ui")); compile_blueprint(&input, &output); let _ = writeln!(xml, " ui/{rel}.ui"); } xml.push_str(" \n\n"); let xml_path = out.join("ghostty.gresource.xml"); std::fs::write(&xml_path, xml).unwrap(); // glib-build-tools also watches the generated XML and UI files. Their // timestamps change during this script, invalidating the next build. // All actual inputs are already covered by the `resources` watch above. let compiled = Command::new("glib-compile-resources") .arg("--sourcedir") .arg(&out) .arg("--sourcedir") .arg(&res) .arg("--target") .arg(out.join("ghostty.gresource")) .arg(&xml_path) .output() .expect("`glib-compile-resources` is required to build ghostty-apprt-gtk"); assert!( compiled.status.success(), "glib-compile-resources failed with {}:\n{}", compiled.status, String::from_utf8_lossy(&compiled.stderr) ); } fn compile_blueprint(input: &Path, output: &Path) { std::fs::create_dir_all(output.parent().unwrap()).unwrap(); let st = Command::new("blueprint-compiler") .arg("compile") .arg("--output") .arg(output) .arg(input) .output() .unwrap_or_else(|e| { panic!("`blueprint-compiler` (>= 0.16) is required to build ghostty-apprt-gtk: {e}") }); assert!( st.status.success(), "blueprint-compiler failed for {}:\n{}", input.display(), String::from_utf8_lossy(&st.stderr) ); }