Skip to content

Commit 1c78a61

Browse files
committed
Fix deadlock when build scripts are waiting for input on stdin
1 parent e691e18 commit 1c78a61

File tree

3 files changed

+47
-13
lines changed

3 files changed

+47
-13
lines changed

crates/cargo-util/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cargo-util"
3-
version = "0.2.1"
3+
version = "0.2.2"
44
edition = "2021"
55
license = "MIT OR Apache-2.0"
66
homepage = "https://github.com/rust-lang/cargo"

crates/cargo-util/src/process_builder.rs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub struct ProcessBuilder {
4040
/// See [`ProcessBuilder::retry_with_argfile`] for more information.
4141
retry_with_argfile: bool,
4242
/// Data to write to stdin.
43-
stdin: Vec<u8>,
43+
stdin: Option<Vec<u8>>,
4444
}
4545

4646
impl fmt::Display for ProcessBuilder {
@@ -82,7 +82,7 @@ impl ProcessBuilder {
8282
jobserver: None,
8383
display_env_vars: false,
8484
retry_with_argfile: false,
85-
stdin: Vec::new(),
85+
stdin: None,
8686
}
8787
}
8888

@@ -212,7 +212,7 @@ impl ProcessBuilder {
212212

213213
/// Sets a value that will be written to stdin of the process on launch.
214214
pub fn stdin<T: Into<Vec<u8>>>(&mut self, stdin: T) -> &mut Self {
215-
self.stdin = stdin.into();
215+
self.stdin = Some(stdin.into());
216216
self
217217
}
218218

@@ -284,18 +284,22 @@ impl ProcessBuilder {
284284
fn _output(&self) -> io::Result<Output> {
285285
if !debug_force_argfile(self.retry_with_argfile) {
286286
let mut cmd = self.build_command();
287-
match piped(&mut cmd).spawn() {
287+
match piped(&mut cmd, self.stdin.is_some()).spawn() {
288288
Err(ref e) if self.should_retry_with_argfile(e) => {}
289289
Err(e) => return Err(e),
290290
Ok(mut child) => {
291-
child.stdin.take().unwrap().write_all(&self.stdin)?;
291+
if let Some(stdin) = &self.stdin {
292+
child.stdin.take().unwrap().write_all(stdin)?;
293+
}
292294
return child.wait_with_output();
293295
}
294296
}
295297
}
296298
let (mut cmd, argfile) = self.build_command_with_argfile()?;
297-
let mut child = piped(&mut cmd).spawn()?;
298-
child.stdin.take().unwrap().write_all(&self.stdin)?;
299+
let mut child = piped(&mut cmd, self.stdin.is_some()).spawn()?;
300+
if let Some(stdin) = &self.stdin {
301+
child.stdin.take().unwrap().write_all(stdin)?;
302+
}
299303
let output = child.wait_with_output();
300304
close_tempfile_and_log_error(argfile);
301305
output
@@ -340,14 +344,14 @@ impl ProcessBuilder {
340344

341345
let spawn = |mut cmd| {
342346
if !debug_force_argfile(self.retry_with_argfile) {
343-
match piped(&mut cmd).spawn() {
347+
match piped(&mut cmd, false).spawn() {
344348
Err(ref e) if self.should_retry_with_argfile(e) => {}
345349
Err(e) => return Err(e),
346350
Ok(child) => return Ok((child, None)),
347351
}
348352
}
349353
let (mut cmd, argfile) = self.build_command_with_argfile()?;
350-
Ok((piped(&mut cmd).spawn()?, Some(argfile)))
354+
Ok((piped(&mut cmd, false).spawn()?, Some(argfile)))
351355
};
352356

353357
let status = (|| {
@@ -541,11 +545,15 @@ fn debug_force_argfile(retry_enabled: bool) -> bool {
541545
cfg!(debug_assertions) && env::var("__CARGO_TEST_FORCE_ARGFILE").is_ok() && retry_enabled
542546
}
543547

544-
/// Creates new pipes for stderr, stdout and stdin.
545-
fn piped(cmd: &mut Command) -> &mut Command {
548+
/// Creates new pipes for stderr, stdout, and optionally stdin.
549+
fn piped(cmd: &mut Command, pipe_stdin: bool) -> &mut Command {
546550
cmd.stdout(Stdio::piped())
547551
.stderr(Stdio::piped())
548-
.stdin(Stdio::piped())
552+
.stdin(if pipe_stdin {
553+
Stdio::piped()
554+
} else {
555+
Stdio::null()
556+
})
549557
}
550558

551559
fn close_tempfile_and_log_error(file: NamedTempFile) {

tests/testsuite/build_script.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4905,3 +4905,29 @@ for more information about build script outputs.
49054905
)
49064906
.run();
49074907
}
4908+
4909+
#[cargo_test]
4910+
fn custom_build_closes_stdin() {
4911+
// Ensure stdin is closed to prevent deadlock.
4912+
// See https://github.com/rust-lang/cargo/issues/11196
4913+
let p = project()
4914+
.file(
4915+
"Cargo.toml",
4916+
r#"
4917+
[package]
4918+
name = "foo"
4919+
version = "0.5.0"
4920+
build = "build.rs"
4921+
"#,
4922+
)
4923+
.file("src/main.rs", "fn main() {}")
4924+
.file(
4925+
"build.rs",
4926+
r#"fn main() {
4927+
let mut line = String::new();
4928+
std::io::stdin().read_line(&mut line).unwrap();
4929+
}"#,
4930+
)
4931+
.build();
4932+
p.cargo("build").run();
4933+
}

0 commit comments

Comments
 (0)