diff --git a/crates/engine/build.rs b/crates/engine/build.rs index 6665cf44..7350c6d8 100644 --- a/crates/engine/build.rs +++ b/crates/engine/build.rs @@ -7,16 +7,20 @@ //! `.wasm` within the web size budget (Cloudflare serves at most 25 MiB per file); the export //! watermark falls back to it there. Native builds embed every font. use std::fmt::Write as _; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; fn main() { println!("cargo::rerun-if-changed=build.rs"); println!("cargo::rerun-if-env-changed=CRAFT_FONTS_DIR"); println!("cargo::rerun-if-env-changed=CRAFT_FONTS_REQUIRED"); let mut src = String::from("pub static CRAFT_FONTS: &[CraftFont] = &[\n"); - if let Some(dir) = std::env::var_os("CRAFT_FONTS_DIR").map(PathBuf::from) { + let mut embedded = 0; + if let Some(dir) = std::env::var_os("CRAFT_FONTS_DIR").map(PathBuf::from).map(resolve) { match craft_fonts(&dir) { - Ok(entries) => src.push_str(&entries), + Ok(entries) => { + embedded = entries.lines().count(); + src.push_str(&entries); + } Err(e) if std::env::var_os("CRAFT_FONTS_REQUIRED").is_some() => { println!("cargo::error=CRAFT_FONTS_DIR={}: {e}", dir.display()); } @@ -24,14 +28,37 @@ fn main() { } } src.push_str("];\n"); + // Without them the app still runs and every language still selectable, so Chinese and Japanese + // just paint boxes — the one failure a user cannot diagnose from what they see. + if embedded == 0 { + println!( + "cargo::warning=no craft-fonts embedded: Chinese and Japanese text will render as empty boxes. \ + Clone https://github.com/storytold/craft-fonts next to this repo and rebuild with \ + CRAFT_FONTS_DIR=../craft-fonts" + ); + } let out = PathBuf::from(std::env::var_os("OUT_DIR").unwrap_or_default()).join("craft_fonts.rs"); if let Err(e) = std::fs::write(&out, src) { println!("cargo::error=writing {}: {e}", out.display()); } } +/// Resolve a relative `CRAFT_FONTS_DIR` against both roots a caller might mean: a build script's +/// cwd is the *package* root (`crates/engine`), while the `../craft-fonts` the README and +/// craft-fonts' docs use is written relative to the *workspace* root and would otherwise resolve +/// to `crates/craft-fonts`. +fn resolve(dir: PathBuf) -> PathBuf { + if dir.is_absolute() { + return dir; + } + let package = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap_or_default()); + let workspace = package.parent().and_then(Path::parent).unwrap_or(package.as_path()); + let candidates = [workspace.join(&dir), package.join(&dir)]; + candidates.iter().find(|candidate| candidate.is_dir()).cloned().unwrap_or_else(|| candidates[0].clone()) +} + /// One `CraftFont { .. }` initialiser per manifest line. -fn craft_fonts(dir: &std::path::Path) -> Result { +fn craft_fonts(dir: &Path) -> Result { let manifest = dir.join("fonts/manifest.txt"); println!("cargo::rerun-if-changed={}", manifest.display()); let text = std::fs::read_to_string(&manifest).map_err(|e| format!("{}: {e}", manifest.display()))?;