diff --git a/Cargo.lock b/Cargo.lock index 71086e7203470..c856f5bdeba6a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3489,6 +3489,7 @@ dependencies = [ "similar", "tempfile", "wasmparser 0.247.0", + "windows 0.62.2", ] [[package]] diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml index 0e1310862589d..d7cba71110fc7 100644 --- a/src/tools/run-make-support/Cargo.toml +++ b/src/tools/run-make-support/Cargo.toml @@ -26,5 +26,12 @@ build_helper = { path = "../../build_helper" } # Shared with rustdoc rustdoc-json-types = { path = "../../rustdoc-json-types" } +[target.'cfg(windows)'.dependencies.windows] +version = "0.62" +features = [ + "Win32_Foundation", +] + + [lib] crate-type = ["lib", "dylib"] diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs index 0216ae7a30ddb..9c5c50a09b99d 100644 --- a/src/tools/run-make-support/src/command.rs +++ b/src/tools/run-make-support/src/command.rs @@ -295,6 +295,7 @@ impl Command { /// Represents the result of an executed process. /// The various `assert_` helper methods should preferably be used for /// checking the contents of stdout/stderr. +#[derive(Debug)] pub struct CompletedProcess { output: Output, } diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index b9717bda1e2c7..2a78718c4b19e 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -44,6 +44,8 @@ pub use serde_json; pub use similar; pub use tempfile; pub use wasmparser; +#[cfg(windows)] +pub use windows; // Helpers for building names of output artifacts that are potentially target-specific. pub use crate::artifact_names::{ @@ -90,7 +92,7 @@ pub use crate::path_helpers::{ shallow_find_files, source_root, }; // Convenience helpers for running binaries and other commands. -pub use crate::run::{cmd, run, run_fail, run_with_args}; +pub use crate::run::{cmd, run, run_fail, run_fail_with_args, run_with_args}; // Helpers for scoped test execution where certain properties are attempted to be maintained. pub use crate::scoped_run::{run_in_tmpdir, test_while_readonly}; pub use crate::string::{ diff --git a/src/tools/run-make-support/src/run.rs b/src/tools/run-make-support/src/run.rs index 43f0473ddf4a1..b64fe2f02d2b7 100644 --- a/src/tools/run-make-support/src/run.rs +++ b/src/tools/run-make-support/src/run.rs @@ -80,6 +80,12 @@ pub fn run_fail(name: &str) -> CompletedProcess { run_common(name, None).run_fail() } +/// Run a built binary with one or more argument(s) and make sure it fails. +#[track_caller] +pub fn run_fail_with_args(name: &str, args: &[&str]) -> CompletedProcess { + run_common(name, Some(args)).run_fail() +} + /// Create a new custom [`Command`]. This should be preferred to creating [`std::process::Command`] /// directly. #[track_caller] diff --git a/tests/run-make/abort-behavior/main.rs b/tests/run-make/abort-behavior/main.rs new file mode 100644 index 0000000000000..db7401fafbf8a --- /dev/null +++ b/tests/run-make/abort-behavior/main.rs @@ -0,0 +1,10 @@ +#![feature(abort_immediate)] + +fn main() { + let arg = std::env::args().skip(1).next().expect("no argument passed"); + match arg.as_str() { + "abort" => std::process::abort(), + "abort_immediate" => std::process::abort_immediate(), + _ => panic!("unrecognized command {}", arg), + } +} diff --git a/tests/run-make/abort-behavior/rmake.rs b/tests/run-make/abort-behavior/rmake.rs new file mode 100644 index 0000000000000..2d644777fdd45 --- /dev/null +++ b/tests/run-make/abort-behavior/rmake.rs @@ -0,0 +1,63 @@ +//! Check that abort library functions raise the expected signals. + +//@ ignore-cross-compile + +use run_make_support::{bin_name, run_fail_with_args, rustc}; + +#[derive(Debug)] +enum Variant { + Abort, + AbortImmediate, +} + +fn main() { + rustc().input("main.rs").run(); + + check_one(Variant::Abort); + check_one(Variant::AbortImmediate); +} + +fn check_one(variant: Variant) { + println!("checking variant {variant:?}"); + let arg = match variant { + Variant::Abort => "abort", + Variant::AbortImmediate => "abort_immediate", + }; + let bin = &bin_name("main"); + let out = run_fail_with_args(bin, &[arg]); + + let status = out.status(); + println!("output status {status}"); + assert!(!status.success()); + + cfg_select! { + unix => { + use std::assert_matches; + use std::os::unix::process::ExitStatusExt; + + use run_make_support::libc; + + let sig = status.signal().expect("no signal recorded"); + match variant { + Variant::Abort => assert_eq!(sig, libc::SIGABRT), + Variant::AbortImmediate => assert_matches!(sig, libc::SIGILL | libc::SIGTRAP), + }; + } + windows => { + use run_make_support::windows; + use windows::Win32::Foundation::{ + STATUS_ILLEGAL_INSTRUCTION, STATUS_STACK_BUFFER_OVERRUN, + }; + + let code = status.code().unwrap(); + let expected = match variant { + Variant::Abort => STATUS_STACK_BUFFER_OVERRUN.0, + Variant::AbortImmediate => STATUS_ILLEGAL_INSTRUCTION.0, + }; + assert_eq!(code, expected); + } + _ => { + unimplemented!("target may need a new branch") + } + } +}