//! Build script for the `openhuman` core crate. //! //! Its sole job today is to generate the module list for the aggregated //! `raw_coverage_all` integration test target. The `tests/raw_coverage/` //! directory holds ~76 auto-generated `*_raw_coverage_e2e.rs` coverage suites //! that were previously ~76 separate `tests/*.rs` integration targets. Each //! separate target statically relinks the entire (very large) `openhuman` //! rlib, so building the test suite paid ~76 full-crate link steps. Folding //! them into a single target ([`tests/raw_coverage_all.rs`]) reduces that to //! one link. //! //! We glob the directory at build time (rather than hand-maintaining a `mod` //! list) so that any newly added `tests/raw_coverage/*.rs` file is picked up //! automatically and cannot be silently skipped. use std::env; use std::fs; use std::path::Path; fn main() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set"); let tests_dir = Path::new(&manifest_dir).join("tests"); let raw_dir = tests_dir.join("raw_coverage"); // Re-run whenever a file is added to / removed from the directory. println!("cargo:rerun-if-changed={}", raw_dir.display()); let mut entries: Vec<(String, String)> = Vec::new(); let read_dir = match fs::read_dir(&raw_dir) { Ok(rd) => Some(rd), // Source-only builds (e.g. the Docker image copies `src/` but not // `tests/`) never compile the integration targets, so there is nothing // to aggregate — emit an empty module list rather than breaking the // build. But if `tests/` IS present and only `raw_coverage/` is missing, // that's an accidental deletion: fail loudly so the suite can't be // silently dropped. Err(_) if !tests_dir.exists() => None, Err(e) => panic!("failed to read {}: {e}", raw_dir.display()), }; for entry in read_dir.into_iter().flatten().flatten() { let path = entry.path(); if path.extension().and_then(|e| e.to_str()) != Some("rs") { continue; } let stem = match path.file_stem().and_then(|s| s.to_str()) { Some(s) => s.to_string(), None => continue, }; // Re-run if any individual test file's contents change. println!("cargo:rerun-if-changed={}", path.display()); // Rust `#[path]` accepts forward slashes on every platform; the // absolute path keeps resolution independent of where the generated // file is `include!`d from. let abs = path.display().to_string().replace('\\', "/"); entries.push((stem, abs)); } // Deterministic order keeps the generated file stable across builds. entries.sort(); let mut generated = String::from("// @generated by build.rs — do not edit.\n"); for (stem, abs) in &entries { generated.push_str(&format!("#[path = \"{abs}\"]\nmod {stem};\n")); } let out_dir = env::var("OUT_DIR").expect("OUT_DIR must be set"); let out_path = Path::new(&out_dir).join("raw_coverage_mods.rs"); fs::write(&out_path, generated).expect("failed to write raw_coverage_mods.rs"); }