diff --git a/Cargo.lock b/Cargo.lock index 552b446a1e753..8a9701acae11f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3343,6 +3343,7 @@ version = "0.0.0" dependencies = [ "object 0.34.0", "regex", + "similar", "wasmparser", ] @@ -5138,6 +5139,12 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "similar" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa42c91313f1d05da9b26f267f931cf178d4aba455b4c4622dd7355eb80c6640" + [[package]] name = "siphasher" version = "0.3.11" diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 3ea35c7940c21..61a24c97e77a2 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -5,5 +5,6 @@ edition = "2021" [dependencies] object = "0.34.0" +similar = "2.5.0" wasmparser = "0.118.2" regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs new file mode 100644 index 0000000000000..54532c6e35bab --- /dev/null +++ b/src/tools/run-make-support/src/diff/mod.rs @@ -0,0 +1,81 @@ +use similar::TextDiff; +use std::path::Path; + +#[cfg(test)] +mod tests; + +pub fn diff() -> Diff { + Diff::new() +} + +#[derive(Debug)] +pub struct Diff { + expected: Option, + expected_name: Option, + actual: Option, + actual_name: Option, +} + +impl Diff { + /// Construct a bare `diff` invocation. + pub fn new() -> Self { + Self { expected: None, expected_name: None, actual: None, actual_name: None } + } + + /// Specify the expected output for the diff from a file. + pub fn expected_file>(&mut self, path: P) -> &mut Self { + let path = path.as_ref(); + let content = std::fs::read_to_string(path).expect("failed to read file"); + let name = path.to_string_lossy().to_string(); + + self.expected = Some(content); + self.expected_name = Some(name); + self + } + + /// Specify the expected output for the diff from a given text string. + pub fn expected_text>(&mut self, name: &str, text: T) -> &mut Self { + self.expected = Some(String::from_utf8_lossy(text.as_ref()).to_string()); + self.expected_name = Some(name.to_string()); + self + } + + /// Specify the actual output for the diff from a file. + pub fn actual_file>(&mut self, path: P) -> &mut Self { + let path = path.as_ref(); + let content = std::fs::read_to_string(path).expect("failed to read file"); + let name = path.to_string_lossy().to_string(); + + self.actual = Some(content); + self.actual_name = Some(name); + self + } + + /// Specify the actual output for the diff from a given text string. + pub fn actual_text>(&mut self, name: &str, text: T) -> &mut Self { + self.actual = Some(String::from_utf8_lossy(text.as_ref()).to_string()); + self.actual_name = Some(name.to_string()); + self + } + + /// Executes the diff process, prints any differences to the standard error. + #[track_caller] + pub fn run(&self) { + let expected = self.expected.as_ref().expect("expected text not set"); + let actual = self.actual.as_ref().expect("actual text not set"); + let expected_name = self.expected_name.as_ref().unwrap(); + let actual_name = self.actual_name.as_ref().unwrap(); + + let output = TextDiff::from_lines(expected, actual) + .unified_diff() + .header(expected_name, actual_name) + .to_string(); + + if !output.is_empty() { + panic!( + "test failed: `{}` is different from `{}`\n\n{}", + expected_name, actual_name, output + ) + } + } +} diff --git a/src/tools/run-make-support/src/diff/tests.rs b/src/tools/run-make-support/src/diff/tests.rs new file mode 100644 index 0000000000000..e6d72544b7ebc --- /dev/null +++ b/src/tools/run-make-support/src/diff/tests.rs @@ -0,0 +1,39 @@ +#[cfg(test)] +mod tests { + use crate::*; + + #[test] + fn test_diff() { + let expected = "foo\nbar\nbaz\n"; + let actual = "foo\nbar\nbaz\n"; + diff().expected_text("EXPECTED_TEXT", expected).actual_text("ACTUAL_TEXT", actual).run(); + } + + #[test] + fn test_should_panic() { + let expected = "foo\nbar\nbaz\n"; + let actual = "foo\nbaz\nbar\n"; + + let output = std::panic::catch_unwind(|| { + diff() + .expected_text("EXPECTED_TEXT", expected) + .actual_text("ACTUAL_TEXT", actual) + .run(); + }) + .unwrap_err(); + + let expected_output = "\ +test failed: `EXPECTED_TEXT` is different from `ACTUAL_TEXT` + +--- EXPECTED_TEXT ++++ ACTUAL_TEXT +@@ -1,3 +1,3 @@ + foo ++baz + bar +-baz +"; + + assert_eq!(output.downcast_ref::().unwrap(), expected_output); + } +} diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index e723e824ed6c8..76e8838d27c72 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -5,6 +5,7 @@ pub mod cc; pub mod clang; +pub mod diff; pub mod llvm_readobj; pub mod run; pub mod rustc; @@ -20,6 +21,7 @@ pub use wasmparser; pub use cc::{cc, extra_c_flags, extra_cxx_flags, Cc}; pub use clang::{clang, Clang}; +pub use diff::{diff, Diff}; pub use llvm_readobj::{llvm_readobj, LlvmReadobj}; pub use run::{run, run_fail}; pub use rustc::{aux_build, rustc, Rustc}; diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs index 9bf41c6e2e9a5..ddaae3236c2b5 100644 --- a/src/tools/run-make-support/src/rustc.rs +++ b/src/tools/run-make-support/src/rustc.rs @@ -148,6 +148,13 @@ impl Rustc { self } + /// Specify the print request. + pub fn print(&mut self, request: &str) -> &mut Self { + self.cmd.arg("--print"); + self.cmd.arg(request); + self + } + /// Add an extra argument to the linker invocation, via `-Clink-arg`. pub fn link_arg(&mut self, link_arg: &str) -> &mut Self { self.cmd.arg(format!("-Clink-arg={link_arg}")); diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 93188b4fbaebd..9b3c0d0f1a570 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -189,7 +189,6 @@ run-make/no-builtins-attribute/Makefile run-make/no-builtins-lto/Makefile run-make/no-cdylib-as-rdylib/Makefile run-make/no-duplicate-libs/Makefile -run-make/no-input-file/Makefile run-make/no-intermediate-extras/Makefile run-make/obey-crate-type-flag/Makefile run-make/optimization-remarks-dir-pgo/Makefile diff --git a/tests/run-make/no-input-file/Makefile b/tests/run-make/no-input-file/Makefile deleted file mode 100644 index a754573a52410..0000000000000 --- a/tests/run-make/no-input-file/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -include ../tools.mk - -all: - $(RUSTC) --print crate-name 2>&1 | diff - no-input-file.stderr diff --git a/tests/run-make/no-input-file/rmake.rs b/tests/run-make/no-input-file/rmake.rs new file mode 100644 index 0000000000000..26df7e80dfbff --- /dev/null +++ b/tests/run-make/no-input-file/rmake.rs @@ -0,0 +1,9 @@ +extern crate run_make_support; + +use run_make_support::{diff, rustc}; + +fn main() { + let output = rustc().print("crate-name").run_fail_assert_exit_code(1); + + diff().expected_file("no-input-file.stderr").actual_text("output", output.stderr).run(); +}