Skip to content

Commit 8871ce0

Browse files
committed
run_make_support: make each command invocation only-run-once
1 parent afed862 commit 8871ce0

File tree

1 file changed

+16
-1
lines changed

1 file changed

+16
-1
lines changed

Diff for: src/tools/run-make-support/src/command.rs

+16-1
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,20 @@ use crate::{
1515
/// This is a custom command wrapper that simplifies working with commands and makes it easier to
1616
/// ensure that we check the exit status of executed processes.
1717
///
18-
/// # A [`Command`] must be executed
18+
/// # A [`Command`] must be executed exactly once
1919
///
2020
/// A [`Command`] is armed by a [`DropBomb`] on construction to enforce that it will be executed. If
2121
/// a [`Command`] is constructed but never executed, the drop bomb will explode and cause the test
2222
/// to panic. Execution methods [`run`] and [`run_fail`] will defuse the drop bomb. A test
2323
/// containing constructed but never executed commands is dangerous because it can give a false
2424
/// sense of confidence.
2525
///
26+
/// Each [`Command`] invocation can also only be executed once, because we want to enforce
27+
/// `std{in,out,err}` config via [`std::process::Stdio`] but [`std::process::Stdio`] is not
28+
/// cloneable.
29+
///
30+
/// In this sense, [`Command`] exhibits linear type semantics but enforced at run-time.
31+
///
2632
/// [`run`]: Self::run
2733
/// [`run_fail`]: Self::run_fail
2834
/// [`run_unchecked`]: Self::run_unchecked
@@ -37,7 +43,9 @@ pub struct Command {
3743
stdout: Option<Stdio>,
3844
stderr: Option<Stdio>,
3945

46+
// Emulate linear type semantics.
4047
drop_bomb: DropBomb,
48+
already_executed: bool,
4149
}
4250

4351
impl Command {
@@ -51,6 +59,7 @@ impl Command {
5159
stdin: None,
5260
stdout: None,
5361
stderr: None,
62+
already_executed: false,
5463
}
5564
}
5665

@@ -177,6 +186,12 @@ impl Command {
177186

178187
#[track_caller]
179188
fn command_output(&mut self) -> CompletedProcess {
189+
if self.already_executed {
190+
panic!("command was already executed");
191+
} else {
192+
self.already_executed = true;
193+
}
194+
180195
self.drop_bomb.defuse();
181196
// let's make sure we piped all the input and outputs
182197
self.cmd.stdin(self.stdin.take().unwrap_or(Stdio::piped()));

0 commit comments

Comments
 (0)