Auto merge of #11257 - arlosi:build-deadlock-1.65.0, r=weihanglo

[BETA-1.65] Fix deadlock when build scripts are waiting for input on stdin

Beta backport of #11205. Cherry picked d51ed05324.

r? `@ehuss`
This commit is contained in:
bors 2022-10-20 06:00:42 +00:00
commit 4bc8f24d3e
4 changed files with 48 additions and 14 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "cargo-util"
version = "0.2.1"
version = "0.2.2"
edition = "2021"
license = "MIT OR Apache-2.0"
homepage = "https://github.com/rust-lang/cargo"

View File

@ -40,7 +40,7 @@ pub struct ProcessBuilder {
/// See [`ProcessBuilder::retry_with_argfile`] for more information.
retry_with_argfile: bool,
/// Data to write to stdin.
stdin: Vec<u8>,
stdin: Option<Vec<u8>>,
}
impl fmt::Display for ProcessBuilder {
@ -82,7 +82,7 @@ impl ProcessBuilder {
jobserver: None,
display_env_vars: false,
retry_with_argfile: false,
stdin: Vec::new(),
stdin: None,
}
}
@ -212,7 +212,7 @@ impl ProcessBuilder {
/// Sets a value that will be written to stdin of the process on launch.
pub fn stdin<T: Into<Vec<u8>>>(&mut self, stdin: T) -> &mut Self {
self.stdin = stdin.into();
self.stdin = Some(stdin.into());
self
}
@ -284,18 +284,22 @@ impl ProcessBuilder {
fn _output(&self) -> io::Result<Output> {
if !debug_force_argfile(self.retry_with_argfile) {
let mut cmd = self.build_command();
match piped(&mut cmd).spawn() {
match piped(&mut cmd, self.stdin.is_some()).spawn() {
Err(ref e) if self.should_retry_with_argfile(e) => {}
Err(e) => return Err(e),
Ok(mut child) => {
child.stdin.take().unwrap().write_all(&self.stdin)?;
if let Some(stdin) = &self.stdin {
child.stdin.take().unwrap().write_all(stdin)?;
}
return child.wait_with_output();
}
}
}
let (mut cmd, argfile) = self.build_command_with_argfile()?;
let mut child = piped(&mut cmd).spawn()?;
child.stdin.take().unwrap().write_all(&self.stdin)?;
let mut child = piped(&mut cmd, self.stdin.is_some()).spawn()?;
if let Some(stdin) = &self.stdin {
child.stdin.take().unwrap().write_all(stdin)?;
}
let output = child.wait_with_output();
close_tempfile_and_log_error(argfile);
output
@ -340,14 +344,14 @@ impl ProcessBuilder {
let spawn = |mut cmd| {
if !debug_force_argfile(self.retry_with_argfile) {
match piped(&mut cmd).spawn() {
match piped(&mut cmd, false).spawn() {
Err(ref e) if self.should_retry_with_argfile(e) => {}
Err(e) => return Err(e),
Ok(child) => return Ok((child, None)),
}
}
let (mut cmd, argfile) = self.build_command_with_argfile()?;
Ok((piped(&mut cmd).spawn()?, Some(argfile)))
Ok((piped(&mut cmd, false).spawn()?, Some(argfile)))
};
let status = (|| {
@ -541,11 +545,15 @@ fn debug_force_argfile(retry_enabled: bool) -> bool {
cfg!(debug_assertions) && env::var("__CARGO_TEST_FORCE_ARGFILE").is_ok() && retry_enabled
}
/// Creates new pipes for stderr, stdout and stdin.
fn piped(cmd: &mut Command) -> &mut Command {
/// Creates new pipes for stderr, stdout, and optionally stdin.
fn piped(cmd: &mut Command, pipe_stdin: bool) -> &mut Command {
cmd.stdout(Stdio::piped())
.stderr(Stdio::piped())
.stdin(Stdio::piped())
.stdin(if pipe_stdin {
Stdio::piped()
} else {
Stdio::null()
})
}
fn close_tempfile_and_log_error(file: NamedTempFile) {

View File

@ -381,7 +381,7 @@ pub struct DetailedTomlDependency<P: Clone = String> {
package: Option<String>,
public: Option<bool>,
/// One ore more of 'bin', 'cdylib', 'staticlib', 'bin:<name>'.
/// One or more of `bin`, `cdylib`, `staticlib`, `bin:<name>`.
artifact: Option<StringOrVec>,
/// If set, the artifact should also be a dependency
lib: Option<bool>,

View File

@ -4905,3 +4905,29 @@ for more information about build script outputs.
)
.run();
}
#[cargo_test]
fn custom_build_closes_stdin() {
// Ensure stdin is closed to prevent deadlock.
// See https://github.com/rust-lang/cargo/issues/11196
let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.5.0"
build = "build.rs"
"#,
)
.file("src/main.rs", "fn main() {}")
.file(
"build.rs",
r#"fn main() {
let mut line = String::new();
std::io::stdin().read_line(&mut line).unwrap();
}"#,
)
.build();
p.cargo("build").run();
}