diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..0429d44 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[alias] +runtest = "run -p testsuite --bin runtest" \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 20062b2..86319a0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,9 +13,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.65" +version = "1.0.70" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98161a4e3e2184da77bb14f02184cdd111e83bbbcc9979dfee3c44b9a85f5602" +checksum = "7de8ce5e0f9f8d88245311066a578d72b7af3e7088f32783804676302df237e4" [[package]] name = "atty" @@ -47,15 +47,11 @@ dependencies = [ "anyhow", "clap", "ctrlc", - "fs_extra", "libc", - "once_cell", "owo-colors", "prettyplease", "proc-macro2", "quote", - "rayon", - "regex", "rustfix", "serde", "serde_json", @@ -186,6 +182,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "errno" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "errno-dragonfly" version = "0.1.2" @@ -262,7 +269,7 @@ checksum = "927609f78c2913a6f6ac3c27a4fe87f43e2a35367c0c4b0f8265e8f49a104330" dependencies = [ "hermit-abi 0.2.6", "io-lifetimes", - "rustix", + "rustix 0.36.5", "windows-sys 0.42.0", ] @@ -290,6 +297,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" +[[package]] +name = "linux-raw-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" + [[package]] name = "log" version = "0.4.17" @@ -459,9 +472,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" dependencies = [ "bitflags", ] @@ -492,15 +505,6 @@ version = "0.6.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "rustfix" version = "0.6.1" @@ -520,13 +524,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3807b5d10909833d3e9acd1eb5fb988f79376ff10fce42937de71a449c4c588" dependencies = [ "bitflags", - "errno", + "errno 0.2.8", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.1.4", "windows-sys 0.42.0", ] +[[package]] +name = "rustix" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" +dependencies = [ + "bitflags", + "errno 0.3.1", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.1", + "windows-sys 0.45.0", +] + [[package]] name = "ryu" version = "1.0.11" @@ -619,16 +637,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" dependencies = [ "cfg-if", "fastrand", - "libc", "redox_syscall", - "remove_dir_all", - "winapi", + "rustix 0.37.3", + "windows-sys 0.45.0", ] [[package]] @@ -640,6 +657,19 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "testsuite" +version = "0.1.0" +dependencies = [ + "anyhow", + "fs_extra", + "once_cell", + "rayon", + "regex", + "tempfile", + "walkdir", +] + [[package]] name = "thread_local" version = "1.1.4" @@ -744,12 +774,11 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "walkdir" -version = "2.3.2" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" dependencies = [ "same-file", - "winapi", "winapi-util", ] @@ -790,13 +819,13 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", ] [[package]] @@ -805,7 +834,16 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" dependencies = [ - "windows-targets", + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.0", ] [[package]] @@ -814,13 +852,28 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +dependencies = [ + "windows_aarch64_gnullvm 0.48.0", + "windows_aarch64_msvc 0.48.0", + "windows_i686_gnu 0.48.0", + "windows_i686_msvc 0.48.0", + "windows_x86_64_gnu 0.48.0", + "windows_x86_64_gnullvm 0.48.0", + "windows_x86_64_msvc 0.48.0", ] [[package]] @@ -829,38 +882,80 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + [[package]] name = "windows_i686_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + [[package]] name = "windows_i686_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml index 2dd935a..15b5b15 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["./", "./prettyplease-forked"] +members = ["./", "./prettyplease-forked", "./testsuite"] exclude = ["test-cases/*", "full-tests/*"] [package] @@ -30,9 +30,3 @@ walkdir = "2.3.2" [target."cfg(unix)".dependencies] libc = "0.2.138" - -[dev-dependencies] -fs_extra = "1.3.0" -once_cell = "1.17.1" -rayon = "1.7.0" -regex = "1.7.3" diff --git a/tests/full_tests.rs b/tests/full_tests.rs index ccb5c93..46aca22 100644 --- a/tests/full_tests.rs +++ b/tests/full_tests.rs @@ -1,21 +1,5 @@ -use anyhow::{ensure, Context, Result}; -use once_cell::sync::Lazy; -use rayon::prelude::{IntoParallelIterator, ParallelIterator}; -use regex::Regex; -use std::collections::hash_map::RandomState; -use std::collections::HashSet; -use std::fs::Permissions; -use std::io::BufWriter; -#[cfg(unix)] -use std::os::unix::prelude::PermissionsExt; -use std::path::PathBuf; +use anyhow::{ensure, Result}; use std::process::Command; -use std::{ - fs, - io::{self, Write}, - path::Path, -}; -use tempfile::TempDir; #[test] #[ignore = "FIXME: Make this not cursed."] @@ -27,225 +11,7 @@ fn full_tests() -> Result<()> { #[test] #[cfg(unix)] fn full_tests() -> Result<()> { - let exit = Command::new("cargo") - .arg("build") - .spawn() - .context("spawn: cargo build")? - .wait() - .context("wait: cargo build")?; - - ensure!(exit.success(), "cargo build failed"); - - let path = Path::new(file!()) - .canonicalize()? - .parent() - .unwrap() - .parent() - .unwrap() - .join("full-tests"); - - let children = fs::read_dir(path)?; - - let children = children - .map(|e| e.map_err(Into::into)) - .collect::>>()?; - - if std::env::var("PARALLEL").as_deref() != Ok("0") { - children - .into_par_iter() - .map(|child| { - let path = child.path(); - - build(&path).with_context(|| format!("building {:?}", path.file_name().unwrap())) - }) - .collect::>>()?; - } else { - for child in children { - let path = child.path(); - - build(&path).with_context(|| format!("building {:?}", path.file_name().unwrap()))?; - } - } - + let status = Command::new("cargo").arg("runtest").spawn()?.wait()?; + ensure!(status.success(), "runtest failed"); Ok(()) } - -fn setup_dir(path: &Path) -> Result<(TempDir, PathBuf)> { - let tempdir = tempfile::tempdir()?; - - let proj_name = path.file_name().unwrap().to_str().unwrap(); - let proj_name = if let Some(proj_name) = proj_name.strip_suffix(".rs") { - let out = Command::new("cargo") - .arg("new") - .arg(proj_name) - .current_dir(tempdir.path()) - .output() - .context("spawning cargo new")?; - - ensure!(out.status.success(), "Failed to run cargo new"); - - fs::copy( - path, - tempdir.path().join(proj_name).join("src").join("main.rs"), - ) - .context("copying to main.rs")?; - proj_name - } else { - proj_name - }; - - writeln!(io::stdout(), ".... Testing {}", proj_name)?; - - fs_extra::copy_items(&[path], &tempdir, &fs_extra::dir::CopyOptions::new())?; - - let proj_dir = tempdir.path().join(proj_name).canonicalize()?; - - Ok((tempdir, proj_dir)) -} - -fn setup_scripts(start_roots: &[String], proj_dir: &Path) -> Result<()> { - // FIXME: Do this in a good way. - // What the fuck is this. - { - let file = fs::File::create(proj_dir.join("check.sh"))?; - - let expected_roots = start_roots - .iter() - .map(|root| format!("'{}'", root)) - .collect::>() - .join(", "); - - dbg!(&expected_roots); - - write!( - BufWriter::new(&file), - r#"#!/usr/bin/env bash -if ! cargo check ; then - >&2 echo "Cargo check failed" - exit 1 -fi - -OUT=$(grep -ro "~MINIMIZE-ROOT [a-zA-Z_\-]*" --no-filename src) - -python3 -c " -# Get the data from bash by just substituting it in. It works! -out = '''$OUT''' - -lines = out.split('\n') - -found = set() - -for line in lines: - name = line.removeprefix('~MINIMIZE-ROOT').strip() - found.add(name) - -# Pass in the data _from Rust directly_. Beautiful. -expected_roots = {{{expected_roots}}} - -for root in expected_roots: - if root in found: - print(f'Found {{root}} in output') - else: - print(f'Did not find {{root}} in output!') - exit(1) -" - "# - )?; - - file.set_permissions(Permissions::from_mode(0o777))?; - } - { - let file = fs::File::create(proj_dir.join("lint.sh"))?; - - write!( - BufWriter::new(&file), - r#"#!/usr/bin/env bash -cargo check - "# - )?; - - #[cfg(unix)] - file.set_permissions(Permissions::from_mode(0o777))?; - } - Ok(()) -} - -fn build(path: &Path) -> Result<()> { - let (_tempdir, proj_dir) = setup_dir(path).context("setting up tempdir")?; - let cargo_minimize = Path::new("target/debug/cargo-minimize") - .canonicalize() - .context("canonicalizing target/debug/cargo-minimize")?; - - let start_roots = get_roots(&proj_dir).context("getting initial MINIMIZE-ROOTs")?; - eprintln!("Roots: {:?}", start_roots); - - setup_scripts(&start_roots, &proj_dir).context("setting up scripts")?; - - let mut cmd = Command::new(cargo_minimize); - cmd.current_dir(&proj_dir); - - cmd.arg("minimize"); - cmd.arg("--script-path=./check.sh"); - cmd.arg("--script-path-lints=./lint.sh"); - - let out = cmd.output().context("spawning cargo-minimize")?; - let stderr = String::from_utf8(out.stderr).unwrap(); - let stdout = String::from_utf8(out.stdout).unwrap(); - - ensure!( - out.status.success(), - "Command failed:\n--- stderr:\n{stderr}\n--- stdout:\n{stdout}" - ); - - let required_deleted = get_required_deleted(&proj_dir).context("get REQUIRED-DELETED")?; - - ensure!( - required_deleted.is_empty(), - "Some REQUIRE-DELETED have not been deleted: {required_deleted:?}" - ); - - let end_roots = HashSet::<_, RandomState>::from_iter( - get_roots(&proj_dir).context("getting final MINIMIZE-ROOTs")?, - ); - for root in &start_roots { - ensure!( - end_roots.contains(root), - "{root} was not found after minimization" - ); - } - - Ok(()) -} - -fn get_roots(path: &Path) -> Result> { - static REGEX: Lazy = Lazy::new(|| Regex::new(r"~MINIMIZE-ROOT ([\w\-_]+)").unwrap()); - - grep(path, ®EX) -} - -fn get_required_deleted(path: &Path) -> Result> { - static REGEX: Lazy = Lazy::new(|| Regex::new(r"~REQUIRE-DELETED ([\w\-_]+)").unwrap()); - - grep(path, ®EX) -} - -fn grep(path: &Path, regex: &Regex) -> Result> { - let path = path.join("src"); - let mut results = Vec::new(); - let walk = walkdir::WalkDir::new(path); - - for entry in walk { - let entry = entry?; - if !entry.metadata()?.is_file() { - continue; - } - let src = fs::read_to_string(entry.path()).context("reading file")?; - let captures = regex.captures_iter(&src); - for cap in captures { - let root_name = cap.get(1).unwrap(); - results.push(root_name.as_str().to_owned()); - } - } - - Ok(results) -} diff --git a/testsuite/Cargo.toml b/testsuite/Cargo.toml new file mode 100644 index 0000000..cfb506b --- /dev/null +++ b/testsuite/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "testsuite" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +anyhow = "1.0.70" +fs_extra = "1.3.0" +once_cell = "1.17.1" +rayon = "1.7.0" +regex = "1.7.3" +tempfile = "3.5.0" +walkdir = "2.3.3" diff --git a/testsuite/src/bin/regression_checker.rs b/testsuite/src/bin/regression_checker.rs new file mode 100644 index 0000000..e71fdf5 --- /dev/null +++ b/testsuite/src/bin/regression_checker.rs @@ -0,0 +1 @@ +fn main() {} \ No newline at end of file diff --git a/testsuite/src/bin/runtest.rs b/testsuite/src/bin/runtest.rs new file mode 100644 index 0000000..96b7c71 --- /dev/null +++ b/testsuite/src/bin/runtest.rs @@ -0,0 +1,6 @@ +#[cfg(not(unix))] +compile_error!("FIXME: This does not support windows yet. I am so sorry."); + +fn main() -> anyhow::Result<()> { + testsuite::full_tests() +} diff --git a/testsuite/src/lib.rs b/testsuite/src/lib.rs new file mode 100644 index 0000000..2c32711 --- /dev/null +++ b/testsuite/src/lib.rs @@ -0,0 +1,250 @@ +use anyhow::{ensure, Context, Result}; +use once_cell::sync::Lazy; +use rayon::prelude::{IntoParallelIterator, ParallelIterator}; +use regex::Regex; +use std::collections::hash_map::RandomState; +use std::collections::HashSet; +use std::fs::Permissions; +use std::io::BufWriter; +#[cfg(unix)] +use std::os::unix::prelude::PermissionsExt; +use std::path::PathBuf; +use std::process::Command; +use std::{ + fs, + io::{self, Write}, + path::Path, +}; +use tempfile::TempDir; + +#[cfg(not(unix))] +pub fn full_tests() -> Result<()> { + todo!("FIXME: Make this not cursed.") +} + +#[cfg(unix)] +pub fn full_tests() -> Result<()> { + let exit = Command::new("cargo") + .arg("build") + .spawn() + .context("spawn: cargo build")? + .wait() + .context("wait: cargo build")?; + + ensure!(exit.success(), "cargo build failed"); + + let path = Path::new(file!()) + .canonicalize()? + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .join("full-tests"); + + let children = fs::read_dir(&path).with_context(|| format!("reading {}", path.display()))?; + + let children = children + .map(|e| e.map_err(Into::into)) + .collect::>>()?; + + if std::env::var("PARALLEL").as_deref() != Ok("0") { + children + .into_par_iter() + .map(|child| { + let path = child.path(); + + build(&path).with_context(|| format!("building {:?}", path.file_name().unwrap())) + }) + .collect::>>()?; + } else { + for child in children { + let path = child.path(); + + build(&path).with_context(|| format!("building {:?}", path.file_name().unwrap()))?; + } + } + + Ok(()) +} + +fn setup_dir(path: &Path) -> Result<(TempDir, PathBuf)> { + let tempdir = tempfile::tempdir()?; + + let proj_name = path.file_name().unwrap().to_str().unwrap(); + let proj_name = if let Some(proj_name) = proj_name.strip_suffix(".rs") { + let out = Command::new("cargo") + .arg("new") + .arg(proj_name) + .current_dir(tempdir.path()) + .output() + .context("spawning cargo new")?; + + ensure!(out.status.success(), "Failed to run cargo new"); + + fs::copy( + path, + tempdir.path().join(proj_name).join("src").join("main.rs"), + ) + .context("copying to main.rs")?; + proj_name + } else { + proj_name + }; + + writeln!(io::stdout(), ".... Testing {}", proj_name)?; + + fs_extra::copy_items(&[path], &tempdir, &fs_extra::dir::CopyOptions::new())?; + + let proj_dir = tempdir.path().join(proj_name).canonicalize()?; + + Ok((tempdir, proj_dir)) +} + +fn setup_scripts(start_roots: &[String], proj_dir: &Path) -> Result<()> { + // FIXME: Do this in a good way. + // What the fuck is this. + { + let file = fs::File::create(proj_dir.join("check.sh"))?; + + let expected_roots = start_roots + .iter() + .map(|root| format!("'{}'", root)) + .collect::>() + .join(", "); + + dbg!(&expected_roots); + + write!( + BufWriter::new(&file), + r#"#!/usr/bin/env bash +if ! cargo check ; then + >&2 echo "Cargo check failed" + exit 1 +fi + +OUT=$(grep -ro "~MINIMIZE-ROOT [a-zA-Z_\-]*" --no-filename src) + +python3 -c " +# Get the data from bash by just substituting it in. It works! +out = '''$OUT''' + +lines = out.split('\n') + +found = set() + +for line in lines: + name = line.removeprefix('~MINIMIZE-ROOT').strip() + found.add(name) + +# Pass in the data _from Rust directly_. Beautiful. +expected_roots = {{{expected_roots}}} + +for root in expected_roots: + if root in found: + print(f'Found {{root}} in output') + else: + print(f'Did not find {{root}} in output!') + exit(1) +" + "# + )?; + + file.set_permissions(Permissions::from_mode(0o777))?; + } + { + let file = fs::File::create(proj_dir.join("lint.sh"))?; + + write!( + BufWriter::new(&file), + r#"#!/usr/bin/env bash +cargo check + "# + )?; + + #[cfg(unix)] + file.set_permissions(Permissions::from_mode(0o777))?; + } + Ok(()) +} + +fn build(path: &Path) -> Result<()> { + let (_tempdir, proj_dir) = setup_dir(path).context("setting up tempdir")?; + let cargo_minimize = Path::new("target/debug/cargo-minimize") + .canonicalize() + .context("canonicalizing target/debug/cargo-minimize")?; + + let start_roots = get_roots(&proj_dir).context("getting initial MINIMIZE-ROOTs")?; + eprintln!("Roots: {:?}", start_roots); + + setup_scripts(&start_roots, &proj_dir).context("setting up scripts")?; + + let mut cmd = Command::new(cargo_minimize); + cmd.current_dir(&proj_dir); + + cmd.arg("minimize"); + cmd.arg("--script-path=./check.sh"); + cmd.arg("--script-path-lints=./lint.sh"); + + let out = cmd.output().context("spawning cargo-minimize")?; + let stderr = String::from_utf8(out.stderr).unwrap(); + let stdout = String::from_utf8(out.stdout).unwrap(); + + ensure!( + out.status.success(), + "Command failed:\n--- stderr:\n{stderr}\n--- stdout:\n{stdout}" + ); + + let required_deleted = get_required_deleted(&proj_dir).context("get REQUIRED-DELETED")?; + + ensure!( + required_deleted.is_empty(), + "Some REQUIRE-DELETED have not been deleted: {required_deleted:?}" + ); + + let end_roots = HashSet::<_, RandomState>::from_iter( + get_roots(&proj_dir).context("getting final MINIMIZE-ROOTs")?, + ); + for root in &start_roots { + ensure!( + end_roots.contains(root), + "{root} was not found after minimization" + ); + } + + Ok(()) +} + +fn get_roots(path: &Path) -> Result> { + static REGEX: Lazy = Lazy::new(|| Regex::new(r"~MINIMIZE-ROOT ([\w\-_]+)").unwrap()); + + grep(path, ®EX) +} + +fn get_required_deleted(path: &Path) -> Result> { + static REGEX: Lazy = Lazy::new(|| Regex::new(r"~REQUIRE-DELETED ([\w\-_]+)").unwrap()); + + grep(path, ®EX) +} + +fn grep(path: &Path, regex: &Regex) -> Result> { + let path = path.join("src"); + let mut results = Vec::new(); + let walk = walkdir::WalkDir::new(path); + + for entry in walk { + let entry = entry?; + if !entry.metadata()?.is_file() { + continue; + } + let src = fs::read_to_string(entry.path()).context("reading file")?; + let captures = regex.captures_iter(&src); + for cap in captures { + let root_name = cap.get(1).unwrap(); + results.push(root_name.as_str().to_owned()); + } + } + + Ok(results) +}