//! Build script: stage a clean copy of the repo `render/` package into `OUT_DIR` //! so `src/embedded.rs` can `include_dir!` it without tripping over the dev-only //! self-referential symlink at `render/vendor/local-packages/`. //! //! Why this exists: `include_dir!` walks a directory at compile time and follows //! symlinks. The repo keeps `render/vendor/local-packages/local/cph-render/0.1.0` //! as a symlink pointing back at `render/` (a gitignored dev convenience for //! typst's local-package resolution). Embedding `render/` directly would make //! `include_dir!` recurse into that link forever. The runtime never needs that //! link (the engine resolves `@local/cph-render` from the render dir directly, //! and `@preview/*` from `vendor/typst-packages/`), so we copy everything EXCEPT //! `vendor/local-packages/` into `OUT_DIR/render` and embed that staged tree. use std::path::{Path, PathBuf}; fn main() { let manifest_dir = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); let render_src = manifest_dir.join("..").join("..").join("render"); let out_dir = PathBuf::from(std::env::var("OUT_DIR").unwrap()); let staged = out_dir.join("render"); // Rebuild the staged tree from scratch each run (cheap — ~140 KiB). if staged.exists() { let _ = std::fs::remove_dir_all(&staged); } copy_tree(&render_src, &staged).expect("stage render/ for embedding"); // Re-run when the render package changes. println!("cargo:rerun-if-changed={}", render_src.display()); // Expose the staged path to the crate via an env var include_dir! can read. println!("cargo:rustc-env=CPH_STAGED_RENDER_DIR={}", staged.display()); } /// Recursively copy `src` → `dst`, skipping the dev-only `vendor/local-packages` /// subtree and never following symlinks into it. fn copy_tree(src: &Path, dst: &Path) -> std::io::Result<()> { std::fs::create_dir_all(dst)?; for entry in std::fs::read_dir(src)? { let entry = entry?; let name = entry.file_name(); let from = entry.path(); let to = dst.join(&name); // Skip the self-referential dev symlink tree. if from.ends_with("vendor/local-packages") || name == *"local-packages" { continue; } // Resolve symlinks/metadata without following into a cycle: use // `symlink_metadata` so a symlinked dir is treated by its link, not its // (recursive) target. We only copy real files and real directories. let meta = std::fs::symlink_metadata(&from)?; let ft = meta.file_type(); if ft.is_symlink() { // Skip any symlink (the only one in the tree is the dev convenience). continue; } else if ft.is_dir() { copy_tree(&from, &to)?; } else if ft.is_file() { std::fs::copy(&from, &to)?; } } Ok(()) }