//! Build-Hook: baut geänderte Assets automatisch beim Cargo-Build. //! //! Reines „Wann"-Stück — das eigentliche Bauen machen die Skripte unter //! `tools/` (und bleiben von Hand aufrufbar). Hier melden wir Cargo nur, wann //! es uns erneut ausführen soll, und stoßen die Skripte an: //! //! * `build-dialogues.sh` — `.ink` → `.ink.json` (inklecate) //! * `build-models.sh` — `assets/maps/**/*.blend` → `.glb` (Blender) //! //! Cargo führt build.rs ausschließlich dann erneut aus, wenn eine der unten //! via `rerun-if-changed` gemeldeten Dateien sich ändert. Unveränderte //! Rebuilds kosten also nichts; nur nach einer Quell-Änderung läuft das //! jeweilige Werkzeug (beide Skripte bauen intern inkrementell). use std::path::{Path, PathBuf}; use std::process::Command; fn main() { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); // --- Ink-Dialoge ------------------------------------------------------ println!("cargo:rerun-if-changed=tools/build-dialogues.sh"); println!("cargo:rerun-if-changed=tools/inklecate"); // Verzeichnis-mtime fängt neu hinzugekommene/gelöschte .ink-Dateien. println!("cargo:rerun-if-changed=assets/interactions"); watch_ext(&root.join("assets/interactions"), "ink"); run_script(&root, "tools/build-dialogues.sh", "Dialog"); // --- Blender-Welten ------------------------------------------------- println!("cargo:rerun-if-changed=tools/build-models.sh"); // Ordner-mtime fängt neue/gelöschte Maps; die .blend selbst einzeln. // Die erzeugten .glb werden NICHT beobachtet (das Skript schreibt sie). watch_dirs_and_ext(&root.join("assets/maps"), "blend"); run_script(&root, "tools/build-models.sh", "Modell"); } /// Jede Datei mit Endung `ext` direkt in `dir` einzeln bei Cargo anmelden. fn watch_ext(dir: &Path, ext: &str) { let Ok(entries) = std::fs::read_dir(dir) else { return }; for entry in entries.flatten() { let p = entry.path(); if p.extension().and_then(|s| s.to_str()) == Some(ext) { println!("cargo:rerun-if-changed={}", p.display()); } } } /// Wie [`watch_ext`], aber rekursiv — und jedes Unterverzeichnis wird selbst /// beobachtet (fängt neu angelegte/entfernte Maps). fn watch_dirs_and_ext(dir: &Path, ext: &str) { let Ok(entries) = std::fs::read_dir(dir) else { return }; println!("cargo:rerun-if-changed={}", dir.display()); for entry in entries.flatten() { let p = entry.path(); if p.is_dir() { watch_dirs_and_ext(&p, ext); } else if p.extension().and_then(|s| s.to_str()) == Some(ext) { println!("cargo:rerun-if-changed={}", p.display()); } } } /// Ein `tools/`-Skript laufen lassen. Ausgabe einsammeln (Cargo blendet /// Build-Script-stdout sonst aus); Fehlschlag ist fatal, ein nicht startbares /// bash nur eine Warnung — die committeten Artefakte reichen dann. fn run_script(root: &Path, rel: &str, label: &str) { let script = root.join(rel); match Command::new("bash").arg(&script).output() { Ok(out) if out.status.success() => { /* still bei Erfolg — Detail via Hand-Aufruf */ } Ok(out) => { let stdout = String::from_utf8_lossy(&out.stdout); let stderr = String::from_utf8_lossy(&out.stderr); for line in stdout.lines().chain(stderr.lines()) { println!("cargo:warning={line}"); } panic!("{label}-Build fehlgeschlagen — siehe Warnungen oben. Einzeln debuggen: ./{rel}"); } Err(e) => { println!("cargo:warning={label}-Build übersprungen ({e}). Bei Bedarf von Hand: ./{rel}"); } } }