Skip to main content

dolang_private_build/
lib.rs

1#![deny(warnings)]
2
3//! Build script helpers for Do language.
4//!
5//! This crate provides utilities for build scripts to generate test code.
6//! It is intended to be used as a build-dependency.
7
8use std::{fs, io::Write, path::Path};
9
10fn munge(s: String) -> String {
11    s.replace(|c: char| !c.is_alphanumeric(), "_")
12}
13
14/// Check if a file should skip Miri by looking for `# skip: miri` in first 10 lines.
15fn should_skip_miri(path: &Path) -> bool {
16    let content = match fs::read_to_string(path) {
17        Ok(c) => c,
18        Err(_) => return false,
19    };
20
21    for line in content.lines().take(10) {
22        if line.contains("# skip: miri") {
23            return true;
24        }
25    }
26    false
27}
28
29/// Process a test directory and generate test code.
30///
31/// This recursively scans the directory for `.dol` files and generates
32/// test functions for each one. It also handles subdirectories as modules.
33///
34/// Tests are skipped under Miri if:
35/// - The test file contains `# skip: miri` in the first 10 lines, or
36/// - A parent directory contains a `.directive` file with `# skip: miri` in the first 10 lines
37pub fn generate_tests(out: &mut dyn Write, dir: &Path) {
38    println!("cargo::rerun-if-changed={}", dir.display());
39    process(out, dir, false);
40}
41
42fn process(out: &mut dyn Write, dir: &Path, parent_skip_miri: bool) {
43    // Check if this directory has a .directive file that sets skip_miri
44    let directive_path = dir.join(".directive");
45    let skip_miri = parent_skip_miri || should_skip_miri(&directive_path);
46
47    for entry in fs::read_dir(dir).unwrap() {
48        let entry = entry.unwrap();
49        let path = entry.path();
50        let path_str = path.display().to_string().replace('\\', "/");
51        if path.is_dir() {
52            let mod_name = munge(path.file_name().unwrap().display().to_string());
53            writeln!(out, "mod r#{mod_name} {{").unwrap();
54            writeln!(out, "#[allow(unused_imports)]").unwrap();
55            writeln!(out, "use super::run;").unwrap();
56            process(out, &path, skip_miri);
57            writeln!(out, "}}").unwrap();
58        } else if path.extension() == Some("dol".as_ref()) {
59            let test_name = munge(path.file_stem().unwrap().display().to_string());
60            let file_skip_miri = skip_miri || should_skip_miri(&path);
61
62            if file_skip_miri {
63                writeln!(out, "#[cfg_attr(miri, ignore)]").unwrap();
64            }
65            writeln!(out, "#[test]").unwrap();
66            writeln!(out, "fn r#{}() {{", test_name).unwrap();
67            writeln!(out, "    run(std::path::Path::new(\"{path_str}\"));").unwrap();
68            writeln!(out, "}}").unwrap();
69            println!("cargo::rerun-if-changed={path_str}");
70        }
71    }
72}