From 9d9978ab1cd91e4ec018ddc721612bf485dd3c3d Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Tue, 9 Jun 2026 17:43:11 +0000 Subject: [PATCH 1/3] Add stdarch-gen-common --- Cargo.lock | 62 +++++ crates/stdarch-gen-common/Cargo.toml | 7 + crates/stdarch-gen-common/src/lib.rs | 309 +++++++++++++++++++++++++ crates/stdarch-gen-hexagon/Cargo.toml | 1 + crates/stdarch-gen-hexagon/src/main.rs | 33 +-- 5 files changed, 399 insertions(+), 13 deletions(-) create mode 100644 crates/stdarch-gen-common/Cargo.toml create mode 100644 crates/stdarch-gen-common/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index a1c31fa9f0..d4a4a279ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -268,6 +268,22 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + [[package]] name = "find-msvc-tools" version = "0.1.9" @@ -445,6 +461,12 @@ version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + [[package]] name = "log" version = "0.4.29" @@ -457,6 +479,12 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + [[package]] name = "once_cell_polyfill" version = "1.70.2" @@ -648,6 +676,19 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + [[package]] name = "ryu" version = "1.0.23" @@ -787,11 +828,19 @@ dependencies = [ "walkdir", ] +[[package]] +name = "stdarch-gen-common" +version = "0.1.0" +dependencies = [ + "tempfile", +] + [[package]] name = "stdarch-gen-hexagon" version = "0.1.0" dependencies = [ "regex", + "stdarch-gen-common", ] [[package]] @@ -864,6 +913,19 @@ version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + [[package]] name = "termcolor" version = "1.4.1" diff --git a/crates/stdarch-gen-common/Cargo.toml b/crates/stdarch-gen-common/Cargo.toml new file mode 100644 index 0000000000..691be14971 --- /dev/null +++ b/crates/stdarch-gen-common/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "stdarch-gen-common" +version = "0.1.0" +edition = "2024" + +[dependencies] +tempfile = "3" \ No newline at end of file diff --git a/crates/stdarch-gen-common/src/lib.rs b/crates/stdarch-gen-common/src/lib.rs new file mode 100644 index 0000000000..8125282fb1 --- /dev/null +++ b/crates/stdarch-gen-common/src/lib.rs @@ -0,0 +1,309 @@ +//! Shared check/bless harness for stdarch generators. + +use std::error::Error as StdError; +use std::fmt; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Mode { + Write, + Check, + Bless, +} + +impl Mode { + pub fn from_env() -> Self { + match std::env::var("STDARCH_GEN_MODE").as_deref() { + Ok("check") => Mode::Check, + Ok("bless") => Mode::Bless, + _ => Mode::Write, + } + } +} + +#[derive(Debug)] +pub enum Error { + Io(io::Error), + Mismatch { path: PathBuf, kind: MismatchKind }, + Generator(Box), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MismatchKind { + MissingInCommitted, + ExtraInCommitted, + ContentsDiffer, + MissingFromGenerated, +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Error::Io(e) => write!(f, "I/O error: {e}"), + Error::Mismatch { path, kind } => match kind { + MismatchKind::MissingInCommitted => { + write!(f, "{}: generated but not committed", path.display()) + } + MismatchKind::ExtraInCommitted => { + write!(f, "{}: committed but no longer generated", path.display()) + } + MismatchKind::ContentsDiffer => write!(f, "{}: contents differ", path.display()), + MismatchKind::MissingFromGenerated => write!( + f, + "{}: declared owned but generator did not produce it", + path.display() + ), + }, + Error::Generator(e) => write!(f, "generator failed: {e}"), + } + } +} + +impl StdError for Error { + fn source(&self) -> Option<&(dyn StdError + 'static)> { + match self { + Error::Io(e) => Some(e), + Error::Generator(e) => Some(&**e), + _ => None, + } + } +} + +impl From for Error { + fn from(e: io::Error) -> Self { + Error::Io(e) + } +} + +pub type Result = std::result::Result; + +// owned is the list of relative paths the generator manages. Files in +// committed not listed here are left untouched by Bless and ignored by Check. +pub fn run(committed: &Path, owned: &[&str], mode: Mode, generate: F) -> Result<()> +where + F: FnOnce(&Path) -> std::result::Result<(), E>, + E: Into>, +{ + match mode { + Mode::Write => { + fs::create_dir_all(committed)?; + generate(committed).map_err(|e| Error::Generator(e.into())) + } + Mode::Check => { + let scratch = tempfile::tempdir()?; + generate(scratch.path()).map_err(|e| Error::Generator(e.into()))?; + compare(scratch.path(), committed, owned) + } + Mode::Bless => { + let scratch = tempfile::tempdir()?; + generate(scratch.path()).map_err(|e| Error::Generator(e.into()))?; + apply_bless(scratch.path(), committed, owned) + } + } +} + +fn compare(generated: &Path, committed: &Path, owned: &[&str]) -> Result<()> { + for rel in owned { + let rel_path = PathBuf::from(rel); + let gen_path = generated.join(&rel_path); + let comm_path = committed.join(&rel_path); + match (gen_path.exists(), comm_path.exists()) { + (true, false) => { + return Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::MissingInCommitted, + }); + } + (false, true) => { + return Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ExtraInCommitted, + }); + } + (false, false) => continue, + (true, true) => { + let g = fs::read(&gen_path)?; + let c = fs::read(&comm_path)?; + if g != c { + return Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ContentsDiffer, + }); + } + } + } + } + Ok(()) +} + +fn apply_bless(scratch: &Path, committed: &Path, owned: &[&str]) -> Result<()> { + fs::create_dir_all(committed)?; + for rel in owned { + let rel_path = PathBuf::from(rel); + let from = scratch.join(&rel_path); + if !from.exists() { + return Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::MissingFromGenerated, + }); + } + let to = committed.join(&rel_path); + if let Some(parent) = to.parent() { + fs::create_dir_all(parent)?; + } + fs::copy(&from, &to)?; + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn write(p: &Path, b: &[u8]) { + if let Some(d) = p.parent() { + fs::create_dir_all(d).unwrap(); + } + fs::write(p, b).unwrap(); + } + + #[test] + fn write_mode_creates_committed() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + run( + &committed, + &["a.txt"], + Mode::Write, + |out| -> std::result::Result<(), io::Error> { + write(&out.join("a.txt"), b"hi"); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(fs::read(committed.join("a.txt")).unwrap(), b"hi"); + } + + #[test] + fn check_passes_when_identical() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write(&committed.join("a.txt"), b"hi"); + run( + &committed, + &["a.txt"], + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write(&out.join("a.txt"), b"hi"); + Ok(()) + }, + ) + .unwrap(); + } + + #[test] + fn check_fails_on_byte_diff() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write(&committed.join("a.txt"), b"hi"); + let e = run( + &committed, + &["a.txt"], + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write(&out.join("a.txt"), b"HI"); + Ok(()) + }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ContentsDiffer, + .. + } + )); + } + + #[test] + fn check_ignores_unowned_committed_files() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write(&committed.join("mod.rs"), b"hand-written"); + write(&committed.join("a.txt"), b"hi"); + run( + &committed, + &["a.txt"], + Mode::Check, + |out| -> std::result::Result<(), io::Error> { + write(&out.join("a.txt"), b"hi"); + Ok(()) + }, + ) + .unwrap(); + } + + #[test] + fn check_fails_when_owned_file_missing_from_generated() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write(&committed.join("a.txt"), b"hi"); + let e = run( + &committed, + &["a.txt"], + Mode::Check, + |_| -> std::result::Result<(), io::Error> { Ok(()) }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::ExtraInCommitted, + .. + } + )); + } + + #[test] + fn bless_preserves_unowned_files() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + write(&committed.join("mod.rs"), b"hand-written"); + write(&committed.join("old.txt"), b"old"); + run( + &committed, + &["new.txt"], + Mode::Bless, + |out| -> std::result::Result<(), io::Error> { + write(&out.join("new.txt"), b"new"); + Ok(()) + }, + ) + .unwrap(); + assert_eq!(fs::read(committed.join("mod.rs")).unwrap(), b"hand-written"); + assert_eq!(fs::read(committed.join("old.txt")).unwrap(), b"old"); + assert_eq!(fs::read(committed.join("new.txt")).unwrap(), b"new"); + } + + #[test] + fn bless_fails_when_generator_drops_owned_file() { + let tmp = tempfile::tempdir().unwrap(); + let committed = tmp.path().join("c"); + let e = run( + &committed, + &["a.txt"], + Mode::Bless, + |_| -> std::result::Result<(), io::Error> { Ok(()) }, + ) + .unwrap_err(); + assert!(matches!( + e, + Error::Mismatch { + kind: MismatchKind::MissingFromGenerated, + .. + } + )); + } +} diff --git a/crates/stdarch-gen-hexagon/Cargo.toml b/crates/stdarch-gen-hexagon/Cargo.toml index 397c7816f8..c7dfce2c0f 100644 --- a/crates/stdarch-gen-hexagon/Cargo.toml +++ b/crates/stdarch-gen-hexagon/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" [dependencies] regex = "1.10" +stdarch-gen-common = { path = "../stdarch-gen-common" } diff --git a/crates/stdarch-gen-hexagon/src/main.rs b/crates/stdarch-gen-hexagon/src/main.rs index 7a1c3030c0..a40e934482 100644 --- a/crates/stdarch-gen-hexagon/src/main.rs +++ b/crates/stdarch-gen-hexagon/src/main.rs @@ -21,6 +21,7 @@ use std::collections::{HashMap, HashSet}; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::{run, Mode}; /// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. /// These intrinsics have equivalent semantics and can be lowered to the generic form. @@ -1695,19 +1696,25 @@ fn main() -> Result<(), String> { .nth(1) .map(std::path::PathBuf::from) .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - - // Generate v64.rs (64-byte vector mode) - let v64_path = hexagon_dir.join("v64.rs"); - println!("\nStep 3: Generating v64.rs (64-byte mode)..."); - generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?; - println!(" Output: {}", v64_path.display()); - - // Generate v128.rs (128-byte vector mode) - let v128_path = hexagon_dir.join("v128.rs"); - println!("\nStep 4: Generating v128.rs (128-byte mode)..."); - generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?; - println!(" Output: {}", v128_path.display()); + + let mode = Mode::from_env(); + println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run( + &hexagon_dir, + &["v64.rs", "v128.rs"], + mode, + |out_dir| -> Result<(), String> { + let v64_path = out_dir.join("v64.rs"); + generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?; + println!(" Output: {}", v64_path.display()); + + let v128_path = out_dir.join("v128.rs"); + generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?; + println!(" Output: {}", v128_path.display()); + Ok(()) + }, + ) + .map_err(|e| e.to_string())?; println!("\n=== Results ==="); println!( From 9aca67e8d3cd933b0e8cded351bf607a6b873551 Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Fri, 12 Jun 2026 05:56:56 +0000 Subject: [PATCH 2/3] add: doc comments --- crates/stdarch-gen-common/src/lib.rs | 55 +++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/crates/stdarch-gen-common/src/lib.rs b/crates/stdarch-gen-common/src/lib.rs index 8125282fb1..993ac69aa0 100644 --- a/crates/stdarch-gen-common/src/lib.rs +++ b/crates/stdarch-gen-common/src/lib.rs @@ -6,14 +6,36 @@ use std::fs; use std::io; use std::path::{Path, PathBuf}; +// Controls what `run` does with the generator's output. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Mode { + // Write the generator's output directly into the target directory. + // + // This is the default for standalone runs. The owned list is not consulted + // whatever the generator emits lands on disk as-is. Write, + // Verify that the committed tree matches the generator's output. + // + // Runs the generator into a temp directory, then compares each path + // in the owned list. Returns an error on the first mismatch. Check, + // Update the committed tree to match the generator's output. + // + // Runs the generator into a temp directory and copies each path in + // the owned list into the committed tree. Files that exist in the + // committed tree but are not in the owned list are left untouched, so + // hand-written code can live alongside generated code in the same + // directory. Bless, } impl Mode { + // Read the mode from the `STDARCH_GEN_MODE` environment variable. + // + // Recognized values: + // - `"check"` → [`Mode::Check`] + // - `"bless"` → [`Mode::Bless`] + // - anything else, including unset → [`Mode::Write`] pub fn from_env() -> Self { match std::env::var("STDARCH_GEN_MODE").as_deref() { Ok("check") => Mode::Check, @@ -32,9 +54,18 @@ pub enum Error { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MismatchKind { + // Owned file produced by the generator but absent from the committed tree. + // Means the committed tree needs to be regenerated. MissingInCommitted, + // Owned file present in the committed tree but the generator no longer + // produces it. The file must be removed from the committed tree and + // the owned list. ExtraInCommitted, + // Owned file exists on both sides but contents differ. ContentsDiffer, + // In Bless mode the owned list claims this path but the generator + // did not produce it. Indicates a stale owned list . + // Blessing cannot proceed because there is nothing to copy. MissingFromGenerated, } @@ -79,8 +110,28 @@ impl From for Error { pub type Result = std::result::Result; -// owned is the list of relative paths the generator manages. Files in -// committed not listed here are left untouched by Bless and ignored by Check. +// Run a generator under the chosen `mode`, reconciling its output with `committed`. +// +// Arguments: +// - `committed` — the directory holding the in-tree (committed) source files. +// - `owned` — relative paths within `committed` that the generator manages. +// Anything in `committed` not listed here is treated as hand-written and is +// left untouched by `Bless` and ignored by `Check`. The slice allows a +// generator-managed file to coexist with hand-written files in the same +// directory. +// - `mode` — what to do with the generator's output. +// - `generate` — closure that writes the generator's output into the +// directory it is given. Its error is wrapped in [`Error::Generator`]. +// +// Behavior per mode: +// - [`Mode::Write`]: invokes `generate(committed)` directly. Owned list is +// not consulted. +// - [`Mode::Check`]: runs the generator into a temp dir and compares each +// path in `owned` against the committed copy. First mismatch returns an +// [`Error::Mismatch`]. +// - [`Mode::Bless`]: runs the generator into a temp dir and copies each +// path in `owned` into `committed`. Errors if any owned path was not +// produced by the generator. pub fn run(committed: &Path, owned: &[&str], mode: Mode, generate: F) -> Result<()> where F: FnOnce(&Path) -> std::result::Result<(), E>, From f10694472306c2d2e212c42cc634d5849d6f48ef Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Sat, 13 Jun 2026 07:03:08 +0000 Subject: [PATCH 3/3] update review changes and fixes --- crates/stdarch-gen-common/src/lib.rs | 208 ++++++++++--------------- crates/stdarch-gen-hexagon/src/main.rs | 30 ++-- 2 files changed, 96 insertions(+), 142 deletions(-) diff --git a/crates/stdarch-gen-common/src/lib.rs b/crates/stdarch-gen-common/src/lib.rs index 993ac69aa0..2d83e1eb64 100644 --- a/crates/stdarch-gen-common/src/lib.rs +++ b/crates/stdarch-gen-common/src/lib.rs @@ -6,36 +6,35 @@ use std::fs; use std::io; use std::path::{Path, PathBuf}; -// Controls what `run` does with the generator's output. +/// Controls what `run` does with the generator's output. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Mode { - // Write the generator's output directly into the target directory. - // - // This is the default for standalone runs. The owned list is not consulted - // whatever the generator emits lands on disk as-is. + /// Write the generator's output directly into `committed`. + /// + /// This is the default for standalone runs. `owned` is not consulted + /// whatever the generator emits lands on disk as-is. Write, - // Verify that the committed tree matches the generator's output. - // - // Runs the generator into a temp directory, then compares each path - // in the owned list. Returns an error on the first mismatch. + /// Verify that the `committed` matches the generator's output for `owned`. + /// + /// Runs the generator into a temp directory, then compares the produced file + /// against the committed copy. Returns an error on the first mismatch. Check, - // Update the committed tree to match the generator's output. - // - // Runs the generator into a temp directory and copies each path in - // the owned list into the committed tree. Files that exist in the - // committed tree but are not in the owned list are left untouched, so - // hand-written code can live alongside generated code in the same - // directory. + /// Update the `committed` to match the generator's output for `owned`. + /// + /// Runs the generator into a temp directory and copies the produced file + /// into `committed`. If the generator no longer produces `owned`, the + /// committed copy is deleted. Files in `committed` that are not `owned` + /// are left untouched. Bless, } impl Mode { - // Read the mode from the `STDARCH_GEN_MODE` environment variable. - // - // Recognized values: - // - `"check"` → [`Mode::Check`] - // - `"bless"` → [`Mode::Bless`] - // - anything else, including unset → [`Mode::Write`] + /// Read the mode from the `STDARCH_GEN_MODE` environment variable. + /// + /// Recognized values: + /// - `"check"` → [`Mode::Check`] + /// - `"bless"` → [`Mode::Bless`] + /// - anything else, including unset → [`Mode::Write`] pub fn from_env() -> Self { match std::env::var("STDARCH_GEN_MODE").as_deref() { Ok("check") => Mode::Check, @@ -54,19 +53,14 @@ pub enum Error { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum MismatchKind { - // Owned file produced by the generator but absent from the committed tree. - // Means the committed tree needs to be regenerated. + /// Owned file produced by the generator but absent from the `committed`. + /// Means the `committed` needs to be regenerated. MissingInCommitted, - // Owned file present in the committed tree but the generator no longer - // produces it. The file must be removed from the committed tree and - // the owned list. + /// Owned file present in the `committed` but the generator no longer + /// produces it. The file must be removed from the `committed` . ExtraInCommitted, - // Owned file exists on both sides but contents differ. + /// Owned file exists on both sides but contents differ. ContentsDiffer, - // In Bless mode the owned list claims this path but the generator - // did not produce it. Indicates a stale owned list . - // Blessing cannot proceed because there is nothing to copy. - MissingFromGenerated, } impl fmt::Display for Error { @@ -81,11 +75,6 @@ impl fmt::Display for Error { write!(f, "{}: committed but no longer generated", path.display()) } MismatchKind::ContentsDiffer => write!(f, "{}: contents differ", path.display()), - MismatchKind::MissingFromGenerated => write!( - f, - "{}: declared owned but generator did not produce it", - path.display() - ), }, Error::Generator(e) => write!(f, "generator failed: {e}"), } @@ -110,29 +99,27 @@ impl From for Error { pub type Result = std::result::Result; -// Run a generator under the chosen `mode`, reconciling its output with `committed`. -// -// Arguments: -// - `committed` — the directory holding the in-tree (committed) source files. -// - `owned` — relative paths within `committed` that the generator manages. -// Anything in `committed` not listed here is treated as hand-written and is -// left untouched by `Bless` and ignored by `Check`. The slice allows a -// generator-managed file to coexist with hand-written files in the same -// directory. -// - `mode` — what to do with the generator's output. -// - `generate` — closure that writes the generator's output into the -// directory it is given. Its error is wrapped in [`Error::Generator`]. -// -// Behavior per mode: -// - [`Mode::Write`]: invokes `generate(committed)` directly. Owned list is -// not consulted. -// - [`Mode::Check`]: runs the generator into a temp dir and compares each -// path in `owned` against the committed copy. First mismatch returns an -// [`Error::Mismatch`]. -// - [`Mode::Bless`]: runs the generator into a temp dir and copies each -// path in `owned` into `committed`. Errors if any owned path was not -// produced by the generator. -pub fn run(committed: &Path, owned: &[&str], mode: Mode, generate: F) -> Result<()> +/// Run a generator under the chosen `mode`, reconciling its output with `committed`. +/// +/// Arguments: +/// - `committed` — the directory holding the in-tree (committed) source files. +/// - `owned` — the file inside `committed` that the generator produces. +/// Anything else in `committed` is treated as hand-written and is +/// left untouched by `Bless` and ignored by `Check`. So generated files +/// coexist with hand-written files in the same directory. +/// - `mode` — what to do with the generator's output. +/// - `generate` — closure that writes the generator's output into the +/// directory it is given. Its error is wrapped in [`Error::Generator`]. +/// +/// Behavior per mode: +/// - [`Mode::Write`]: invokes `generate(committed)` directly. owned is +/// not consulted. +/// - [`Mode::Check`]: runs the generator into a temp dir and compares +/// `owned` against the committed copy. Mismatch returns [`Error::Mismatch`]. +/// - [`Mode::Bless`]: runs the generator into a temp dir and copies `owned` +/// into `committed`, or removes `committed`'s copy if the generator no +/// longer produces it. +pub fn run(committed: &Path, owned: &str, mode: Mode, generate: F) -> Result<()> where F: FnOnce(&Path) -> std::result::Result<(), E>, E: Into>, @@ -155,61 +142,50 @@ where } } -fn compare(generated: &Path, committed: &Path, owned: &[&str]) -> Result<()> { - for rel in owned { - let rel_path = PathBuf::from(rel); - let gen_path = generated.join(&rel_path); - let comm_path = committed.join(&rel_path); - match (gen_path.exists(), comm_path.exists()) { - (true, false) => { - return Err(Error::Mismatch { +fn compare(generated: &Path, committed: &Path, owned: &str) -> Result<()> { + let rel_path = PathBuf::from(owned); + let gen_path = generated.join(&rel_path); + let comm_path = committed.join(&rel_path); + match (gen_path.exists(), comm_path.exists()) { + (true, false) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::MissingInCommitted, + }), + (false, true) => Err(Error::Mismatch { + path: rel_path, + kind: MismatchKind::ExtraInCommitted, + }), + (false, false) => Ok(()), + (true, true) => { + if fs::read(&gen_path)? != fs::read(&comm_path)? { + Err(Error::Mismatch { path: rel_path, - kind: MismatchKind::MissingInCommitted, - }); - } - (false, true) => { - return Err(Error::Mismatch { - path: rel_path, - kind: MismatchKind::ExtraInCommitted, - }); - } - (false, false) => continue, - (true, true) => { - let g = fs::read(&gen_path)?; - let c = fs::read(&comm_path)?; - if g != c { - return Err(Error::Mismatch { - path: rel_path, - kind: MismatchKind::ContentsDiffer, - }); - } + kind: MismatchKind::ContentsDiffer, + }) + } else { + Ok(()) } } } - Ok(()) } -fn apply_bless(scratch: &Path, committed: &Path, owned: &[&str]) -> Result<()> { +fn apply_bless(scratch: &Path, committed: &Path, owned: &str) -> Result<()> { fs::create_dir_all(committed)?; - for rel in owned { - let rel_path = PathBuf::from(rel); - let from = scratch.join(&rel_path); - if !from.exists() { - return Err(Error::Mismatch { - path: rel_path, - kind: MismatchKind::MissingFromGenerated, - }); - } - let to = committed.join(&rel_path); + let rel_path = PathBuf::from(owned); + let from = scratch.join(&rel_path); + let to = committed.join(&rel_path); + if from.exists() { if let Some(parent) = to.parent() { fs::create_dir_all(parent)?; } fs::copy(&from, &to)?; + } else if to.exists() { + fs::remove_file(&to)?; } Ok(()) } -#[cfg(test)] +#[cfg(all(test, not(target_os = "ios")))] mod tests { use super::*; @@ -226,7 +202,7 @@ mod tests { let committed = tmp.path().join("c"); run( &committed, - &["a.txt"], + "a.txt", Mode::Write, |out| -> std::result::Result<(), io::Error> { write(&out.join("a.txt"), b"hi"); @@ -244,7 +220,7 @@ mod tests { write(&committed.join("a.txt"), b"hi"); run( &committed, - &["a.txt"], + "a.txt", Mode::Check, |out| -> std::result::Result<(), io::Error> { write(&out.join("a.txt"), b"hi"); @@ -261,7 +237,7 @@ mod tests { write(&committed.join("a.txt"), b"hi"); let e = run( &committed, - &["a.txt"], + "a.txt", Mode::Check, |out| -> std::result::Result<(), io::Error> { write(&out.join("a.txt"), b"HI"); @@ -286,7 +262,7 @@ mod tests { write(&committed.join("a.txt"), b"hi"); run( &committed, - &["a.txt"], + "a.txt", Mode::Check, |out| -> std::result::Result<(), io::Error> { write(&out.join("a.txt"), b"hi"); @@ -303,7 +279,7 @@ mod tests { write(&committed.join("a.txt"), b"hi"); let e = run( &committed, - &["a.txt"], + "a.txt", Mode::Check, |_| -> std::result::Result<(), io::Error> { Ok(()) }, ) @@ -325,7 +301,7 @@ mod tests { write(&committed.join("old.txt"), b"old"); run( &committed, - &["new.txt"], + "new.txt", Mode::Bless, |out| -> std::result::Result<(), io::Error> { write(&out.join("new.txt"), b"new"); @@ -337,24 +313,4 @@ mod tests { assert_eq!(fs::read(committed.join("old.txt")).unwrap(), b"old"); assert_eq!(fs::read(committed.join("new.txt")).unwrap(), b"new"); } - - #[test] - fn bless_fails_when_generator_drops_owned_file() { - let tmp = tempfile::tempdir().unwrap(); - let committed = tmp.path().join("c"); - let e = run( - &committed, - &["a.txt"], - Mode::Bless, - |_| -> std::result::Result<(), io::Error> { Ok(()) }, - ) - .unwrap_err(); - assert!(matches!( - e, - Error::Mismatch { - kind: MismatchKind::MissingFromGenerated, - .. - } - )); - } } diff --git a/crates/stdarch-gen-hexagon/src/main.rs b/crates/stdarch-gen-hexagon/src/main.rs index a40e934482..992e6bc2ae 100644 --- a/crates/stdarch-gen-hexagon/src/main.rs +++ b/crates/stdarch-gen-hexagon/src/main.rs @@ -1699,22 +1699,20 @@ fn main() -> Result<(), String> { let mode = Mode::from_env(); println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); - run( - &hexagon_dir, - &["v64.rs", "v128.rs"], - mode, - |out_dir| -> Result<(), String> { - let v64_path = out_dir.join("v64.rs"); - generate_module_file(&intrinsics, &v64_path, VectorMode::V64)?; - println!(" Output: {}", v64_path.display()); - - let v128_path = out_dir.join("v128.rs"); - generate_module_file(&intrinsics, &v128_path, VectorMode::V128)?; - println!(" Output: {}", v128_path.display()); - Ok(()) - }, - ) - .map_err(|e| e.to_string())?; + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + run( + &hexagon_dir, + filename, + mode, + |out_dir| -> Result<(), String> { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + println!(" Output: {}", path.display()); + Ok(()) + }, + ) + .map_err(|e| e.to_string())?; + } println!("\n=== Results ==="); println!(