Skip to content

run_make_support: rename Command::stdin to stdin_buf and add std{in,out,err} config helpers #129973

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 71 additions & 14 deletions src/tools/run-make-support/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,81 @@ use crate::{
/// This is a custom command wrapper that simplifies working with commands and makes it easier to
/// ensure that we check the exit status of executed processes.
///
/// # A [`Command`] must be executed
/// # A [`Command`] must be executed exactly once
///
/// A [`Command`] is armed by a [`DropBomb`] on construction to enforce that it will be executed. If
/// a [`Command`] is constructed but never executed, the drop bomb will explode and cause the test
/// to panic. Execution methods [`run`] and [`run_fail`] will defuse the drop bomb. A test
/// containing constructed but never executed commands is dangerous because it can give a false
/// sense of confidence.
///
/// Each [`Command`] invocation can also only be executed once, because we want to enforce
/// `std{in,out,err}` config via [`std::process::Stdio`] but [`std::process::Stdio`] is not
/// cloneable.
///
/// In this sense, [`Command`] exhibits linear type semantics but enforced at run-time.
///
/// [`run`]: Self::run
/// [`run_fail`]: Self::run_fail
/// [`run_unchecked`]: Self::run_unchecked
#[derive(Debug)]
pub struct Command {
cmd: StdCommand,
stdin: Option<Box<[u8]>>,
// Convience for providing a quick stdin buffer.
stdin_buf: Option<Box<[u8]>>,

// Configurations for child process's std{in,out,err} handles.
stdin: Option<Stdio>,
stdout: Option<Stdio>,
stderr: Option<Stdio>,

// Emulate linear type semantics.
drop_bomb: DropBomb,
already_executed: bool,
}

impl Command {
#[track_caller]
pub fn new<P: AsRef<OsStr>>(program: P) -> Self {
let program = program.as_ref();
Self { cmd: StdCommand::new(program), stdin: None, drop_bomb: DropBomb::arm(program) }
Self {
cmd: StdCommand::new(program),
stdin_buf: None,
drop_bomb: DropBomb::arm(program),
stdin: None,
stdout: None,
stderr: None,
already_executed: false,
}
}

/// Specify a stdin input
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.stdin = Some(input.as_ref().to_vec().into_boxed_slice());
/// Specify a stdin input buffer. This is a convenience helper,
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.stdin_buf = Some(input.as_ref().to_vec().into_boxed_slice());
self
}

/// Configuration for the child process’s standard input (stdin) handle.
///
/// See [`std::process::Command::stdin`].
pub fn stdin<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
self.stdin = Some(cfg.into());
self
}

/// Configuration for the child process’s standard output (stdout) handle.
///
/// See [`std::process::Command::stdout`].
pub fn stdout<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
self.stdout = Some(cfg.into());
self
}

/// Configuration for the child process’s standard error (stderr) handle.
///
/// See [`std::process::Command::stderr`].
pub fn stderr<T: Into<Stdio>>(&mut self, cfg: T) -> &mut Self {
self.stderr = Some(cfg.into());
self
}

Expand Down Expand Up @@ -105,6 +152,8 @@ impl Command {
}

/// Run the constructed command and assert that it is successfully run.
///
/// By default, std{in,out,err} are [`Stdio::piped()`].
#[track_caller]
pub fn run(&mut self) -> CompletedProcess {
let output = self.command_output();
Expand All @@ -115,6 +164,8 @@ impl Command {
}

/// Run the constructed command and assert that it does not successfully run.
///
/// By default, std{in,out,err} are [`Stdio::piped()`].
#[track_caller]
pub fn run_fail(&mut self) -> CompletedProcess {
let output = self.command_output();
Expand All @@ -124,24 +175,30 @@ impl Command {
output
}

/// Run the command but do not check its exit status.
/// Only use if you explicitly don't care about the exit status.
/// Prefer to use [`Self::run`] and [`Self::run_fail`]
/// whenever possible.
/// Run the command but do not check its exit status. Only use if you explicitly don't care
/// about the exit status.
///
/// Prefer to use [`Self::run`] and [`Self::run_fail`] whenever possible.
#[track_caller]
pub fn run_unchecked(&mut self) -> CompletedProcess {
self.command_output()
}

#[track_caller]
fn command_output(&mut self) -> CompletedProcess {
if self.already_executed {
panic!("command was already executed");
} else {
self.already_executed = true;
}

self.drop_bomb.defuse();
// let's make sure we piped all the input and outputs
self.cmd.stdin(Stdio::piped());
self.cmd.stdout(Stdio::piped());
self.cmd.stderr(Stdio::piped());
self.cmd.stdin(self.stdin.take().unwrap_or(Stdio::piped()));
self.cmd.stdout(self.stdout.take().unwrap_or(Stdio::piped()));
self.cmd.stderr(self.stderr.take().unwrap_or(Stdio::piped()));

let output = if let Some(input) = &self.stdin {
let output = if let Some(input) = &self.stdin_buf {
let mut child = self.cmd.spawn().unwrap();

{
Expand Down
7 changes: 4 additions & 3 deletions src/tools/run-make-support/src/external_deps/llvm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,10 @@ impl LlvmFilecheck {
Self { cmd }
}

/// Pipe a read file into standard input containing patterns that will be matched against the .patterns(path) call.
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.cmd.stdin(input);
/// Provide a buffer representing standard input containing patterns that will be matched
/// against the `.patterns(path)` call.
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.cmd.stdin_buf(input);
self
}

Expand Down
6 changes: 3 additions & 3 deletions src/tools/run-make-support/src/external_deps/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,9 +291,9 @@ impl Rustc {
self
}

/// Specify a stdin input
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.cmd.stdin(input);
/// Specify a stdin input buffer.
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.cmd.stdin_buf(input);
self
}

Expand Down
6 changes: 3 additions & 3 deletions src/tools/run-make-support/src/external_deps/rustdoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,9 @@ impl Rustdoc {
self
}

/// Specify a stdin input
pub fn stdin<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.cmd.stdin(input);
/// Specify a stdin input buffer.
pub fn stdin_buf<I: AsRef<[u8]>>(&mut self, input: I) -> &mut Self {
self.cmd.stdin_buf(input);
self
}

Expand Down
2 changes: 2 additions & 0 deletions src/tools/run-make-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,15 @@ pub mod rfs {
}

// Re-exports of third-party library crates.
// tidy-alphabetical-start
pub use bstr;
pub use gimli;
pub use libc;
pub use object;
pub use regex;
pub use serde_json;
pub use wasmparser;
// tidy-alphabetical-end

// Re-exports of external dependencies.
pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc};
Expand Down
2 changes: 1 addition & 1 deletion tests/run-make/clear-error-blank-output/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
use run_make_support::rustc;

fn main() {
let output = rustc().output("").stdin(b"fn main() {}").run_fail();
let output = rustc().output("").stdin_buf(b"fn main() {}").run_fail();
output.assert_stderr_not_contains("panic");
}
2 changes: 1 addition & 1 deletion tests/run-make/comment-section/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() {

rustc()
.arg("-")
.stdin("fn main() {}")
.stdin_buf("fn main() {}")
.emit("link,obj")
.arg("-Csave-temps")
.target(target)
Expand Down
2 changes: 1 addition & 1 deletion tests/run-make/compile-stdin/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
use run_make_support::{run, rustc};

fn main() {
rustc().arg("-").stdin("fn main() {}").run();
rustc().arg("-").stdin_buf("fn main() {}").run();
run("rust_out");
}
2 changes: 1 addition & 1 deletion tests/run-make/libtest-junit/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ fn run_tests(extra_args: &[&str], expected_file: &str) {
.run_fail();
let test_stdout = &cmd_out.stdout_utf8();

python_command().arg("validate_junit.py").stdin(test_stdout).run();
python_command().arg("validate_junit.py").stdin_buf(test_stdout).run();

diff()
.expected_file(expected_file)
Expand Down
2 changes: 1 addition & 1 deletion tests/run-make/llvm-ident/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fn main() {
.codegen_units(16)
.opt_level("2")
.target(&env_var("TARGET"))
.stdin("fn main(){}")
.stdin_buf("fn main(){}")
.run();

// `llvm-dis` is used here since `--emit=llvm-ir` does not emit LLVM IR
Expand Down
4 changes: 2 additions & 2 deletions tests/run-make/llvm-outputs/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ fn main() {
let p = cwd();
path_bc = p.join("nonexistant_dir_bc");
path_ir = p.join("nonexistant_dir_ir");
rustc().input("-").stdin("fn main() {}").out_dir(&path_bc).emit("llvm-bc").run();
rustc().input("-").stdin("fn main() {}").out_dir(&path_ir).emit("llvm-ir").run();
rustc().input("-").stdin_buf("fn main() {}").out_dir(&path_bc).emit("llvm-bc").run();
rustc().input("-").stdin_buf("fn main() {}").out_dir(&path_ir).emit("llvm-ir").run();
assert!(path_bc.exists());
assert!(path_ir.exists());
});
Expand Down
2 changes: 1 addition & 1 deletion tests/run-make/no-builtins-attribute/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ use run_make_support::{llvm_filecheck, rfs, rustc};
fn main() {
rustc().input("no_builtins.rs").emit("link").run();
rustc().input("main.rs").emit("llvm-ir").run();
llvm_filecheck().patterns("filecheck.main.txt").stdin(rfs::read("main.ll")).run();
llvm_filecheck().patterns("filecheck.main.txt").stdin_buf(rfs::read("main.ll")).run();
}
5 changes: 4 additions & 1 deletion tests/run-make/pgo-branch-weights/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,8 @@ fn main() {
.codegen_units(1)
.emit("llvm-ir")
.run();
llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run();
llvm_filecheck()
.patterns("filecheck-patterns.txt")
.stdin_buf(rfs::read("interesting.ll"))
.run();
}
5 changes: 4 additions & 1 deletion tests/run-make/pgo-indirect-call-promotion/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,8 @@ fn main() {
.codegen_units(1)
.emit("llvm-ir")
.run();
llvm_filecheck().patterns("filecheck-patterns.txt").stdin(rfs::read("interesting.ll")).run();
llvm_filecheck()
.patterns("filecheck-patterns.txt")
.stdin_buf(rfs::read("interesting.ll"))
.run();
}
2 changes: 1 addition & 1 deletion tests/run-make/pgo-use/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,5 @@ fn main() {
let lines: Vec<_> = ir.lines().rev().collect();
let mut reversed_ir = lines.join("\n");
reversed_ir.push('\n');
llvm_filecheck().patterns("filecheck-patterns.txt").stdin(reversed_ir.as_bytes()).run();
llvm_filecheck().patterns("filecheck-patterns.txt").stdin_buf(reversed_ir.as_bytes()).run();
}
2 changes: 1 addition & 1 deletion tests/run-make/separate-link/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use run_make_support::{run, rustc};

fn main() {
rustc().stdin(b"fn main(){}").arg("-Zno-link").arg("-").run();
rustc().stdin_buf(b"fn main(){}").arg("-Zno-link").arg("-").run();
rustc().arg("-Zlink-only").input("rust_out.rlink").run();
run("rust_out");
}
2 changes: 1 addition & 1 deletion tests/run-make/static-pie/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ fn ok_compiler_version(compiler: &str) -> bool {
}

let compiler_output =
cmd(compiler).stdin(trigger).arg("-").arg("-E").arg("-x").arg("c").run().stdout_utf8();
cmd(compiler).stdin_buf(trigger).arg("-").arg("-E").arg("-x").arg("c").run().stdout_utf8();
let re = Regex::new(r"(?m)^(\d+)").unwrap();
let version: u32 =
re.captures(&compiler_output).unwrap().get(1).unwrap().as_str().parse().unwrap();
Expand Down
4 changes: 2 additions & 2 deletions tests/run-make/stdin-rustc/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ const NOT_UTF8: &[u8] = &[0xff, 0xff, 0xff];

fn main() {
// echo $HELLO_WORLD | rustc -
rustc().arg("-").stdin(HELLO_WORLD).run();
rustc().arg("-").stdin_buf(HELLO_WORLD).run();
assert!(
PathBuf::from(if !is_windows() { "rust_out" } else { "rust_out.exe" })
.try_exists()
.unwrap()
);

// echo $NOT_UTF8 | rustc -
rustc().arg("-").stdin(NOT_UTF8).run_fail().assert_stderr_contains(
rustc().arg("-").stdin_buf(NOT_UTF8).run_fail().assert_stderr_contains(
"error: couldn't read from stdin, as it did not contain valid UTF-8",
);
}
4 changes: 2 additions & 2 deletions tests/run-make/stdin-rustdoc/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ fn main() {
let out_dir = PathBuf::from("doc");

// rustdoc -
rustdoc().arg("-").out_dir(&out_dir).stdin(INPUT).run();
rustdoc().arg("-").out_dir(&out_dir).stdin_buf(INPUT).run();
assert!(out_dir.join("rust_out/struct.F.html").try_exists().unwrap());

// rustdoc --test -
rustdoc().arg("--test").arg("-").stdin(INPUT).run();
rustdoc().arg("--test").arg("-").stdin_buf(INPUT).run();

// rustdoc file.rs -
rustdoc().arg("file.rs").arg("-").run_fail();
Expand Down
2 changes: 1 addition & 1 deletion tests/run-make/sysroot-crates-are-unstable/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ fn check_crate_is_unstable(cr: &Crate) {
.target(target())
.extern_(name, path)
.input("-")
.stdin(format!("extern crate {name};"))
.stdin_buf(format!("extern crate {name};"))
.run_fail();

// Make sure it failed for the intended reason, not some other reason.
Expand Down
2 changes: 1 addition & 1 deletion tests/run-make/unknown-mod-stdin/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
use run_make_support::{diff, rustc};

fn main() {
let out = rustc().crate_type("rlib").stdin(b"mod unknown;").arg("-").run_fail();
let out = rustc().crate_type("rlib").stdin_buf(b"mod unknown;").arg("-").run_fail();
diff()
.actual_text("actual-stdout", out.stdout_utf8())
.expected_file("unknown-mod.stdout")
Expand Down
12 changes: 8 additions & 4 deletions tests/run-make/x86_64-fortanix-unknown-sgx-lvi/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,23 @@ fn check(func_re: &str, mut checks: &str) {
// This is because frame pointers are optional, and them being enabled requires
// an additional `popq` in the pattern checking file.
if func_re == "std::io::stdio::_print::[[:alnum:]]+" {
let output = llvm_filecheck().stdin(&dump).patterns(checks).run_unchecked();
let output = llvm_filecheck().stdin_buf(&dump).patterns(checks).run_unchecked();
if !output.status().success() {
checks = "print.without_frame_pointers.checks";
llvm_filecheck().stdin(&dump).patterns(checks).run();
llvm_filecheck().stdin_buf(&dump).patterns(checks).run();
}
} else {
llvm_filecheck().stdin(&dump).patterns(checks).run();
llvm_filecheck().stdin_buf(&dump).patterns(checks).run();
}
if !["rust_plus_one_global_asm", "cmake_plus_one_c_global_asm", "cmake_plus_one_cxx_global_asm"]
.contains(&func_re)
{
// The assembler cannot avoid explicit `ret` instructions. Sequences
// of `shlq $0x0, (%rsp); lfence; retq` are used instead.
llvm_filecheck().args(&["--implicit-check-not", "ret"]).stdin(dump).patterns(checks).run();
llvm_filecheck()
.args(&["--implicit-check-not", "ret"])
.stdin_buf(dump)
.patterns(checks)
.run();
}
}
Loading