Skip to content

Commit 2e8b42c

Browse files
committed
Remove dependency on pipe, unless parallel
1 parent 2b52daf commit 2e8b42c

File tree

6 files changed

+96
-298
lines changed

6 files changed

+96
-298
lines changed

Cargo.toml

+8-4
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,17 @@ exclude = ["/.github", "tests", "src/bin"]
1919
edition = "2018"
2020
rust-version = "1.53"
2121

22-
[target.'cfg(unix)'.dependencies]
23-
# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866
22+
# Don't turn on the feature "std" for these dependencies, see https://github.com/rust-lang/cargo/issues/4866
2423
# which is still an issue with `resolver = "1"`.
25-
libc = { version = "0.2.62", default-features = false }
24+
25+
[dependencies]
26+
memchr = { version = "2.7.1", default-features = false, optional = true }
27+
28+
[target.'cfg(unix)'.dependencies]
29+
libc = { version = "0.2.62", default-features = false, optional = true }
2630

2731
[features]
28-
parallel = []
32+
parallel = ["libc", "memchr"]
2933

3034
[dev-dependencies]
3135
tempfile = "3"

src/command_helpers.rs

+61-86
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,12 @@ use std::{
44
collections::hash_map,
55
ffi::OsString,
66
fmt::Display,
7-
fs::{self, File},
7+
fs,
88
hash::Hasher,
9-
io::{self, BufRead, BufReader, Read, Write},
9+
io::{self, BufRead, BufReader, Read, Stdout, Write},
1010
path::Path,
1111
process::{Child, Command, Stdio},
1212
sync::Arc,
13-
thread::{self, JoinHandle},
1413
};
1514

1615
use crate::{Error, ErrorKind, Object};
@@ -41,83 +40,55 @@ impl CargoOutput {
4140
}
4241
}
4342

44-
pub(crate) fn print_thread(&self) -> Result<Option<PrintThread>, Error> {
45-
self.warnings.then(PrintThread::new).transpose()
43+
fn stdio_for_warnings(&self) -> Stdio {
44+
if self.warnings {
45+
Stdio::piped()
46+
} else {
47+
Stdio::null()
48+
}
4649
}
47-
}
48-
49-
pub(crate) struct PrintThread {
50-
handle: Option<JoinHandle<()>>,
51-
pipe_writer: Option<File>,
52-
}
5350

54-
impl PrintThread {
55-
pub(crate) fn new() -> Result<Self, Error> {
56-
let (pipe_reader, pipe_writer) = crate::os_pipe::pipe()?;
51+
#[cfg(feature = "parallel")]
52+
pub(crate) fn reader_for_stderr_as_warnings(
53+
&self,
54+
child: &mut Child,
55+
) -> Option<BufReader<std::process::ChildStderr>> {
56+
self.warnings
57+
.then(|| BufReader::with_capacity(4096, child.stderr.take().unwrap()))
58+
}
5759

58-
// Capture the standard error coming from compilation, and write it out
59-
// with cargo:warning= prefixes. Note that this is a bit wonky to avoid
60-
// requiring the output to be UTF-8, we instead just ship bytes from one
61-
// location to another.
62-
let print = thread::spawn(move || {
63-
let mut stderr = BufReader::with_capacity(4096, pipe_reader);
60+
fn forward_stderr_as_warnings(&self, child: &mut Child) {
61+
if self.warnings {
62+
let mut stderr = BufReader::with_capacity(4096, child.stderr.as_mut().unwrap());
6463
let mut line = Vec::with_capacity(20);
6564
let stdout = io::stdout();
6665

6766
// read_until returns 0 on Eof
68-
while stderr.read_until(b'\n', &mut line).unwrap() != 0 {
69-
{
70-
let mut stdout = stdout.lock();
71-
72-
stdout.write_all(b"cargo:warning=").unwrap();
73-
stdout.write_all(&line).unwrap();
74-
stdout.write_all(b"\n").unwrap();
75-
}
67+
while stderr.read_until(b'\n', &mut line).unwrap_or_default() != 0 {
68+
write_warning(&stdout, &line);
7669

7770
// read_until does not clear the buffer
7871
line.clear();
7972
}
80-
});
81-
82-
Ok(Self {
83-
handle: Some(print),
84-
pipe_writer: Some(pipe_writer),
85-
})
86-
}
87-
88-
/// # Panics
89-
///
90-
/// Will panic if the pipe writer has already been taken.
91-
pub(crate) fn take_pipe_writer(&mut self) -> File {
92-
self.pipe_writer.take().unwrap()
93-
}
94-
95-
/// # Panics
96-
///
97-
/// Will panic if the pipe writer has already been taken.
98-
pub(crate) fn clone_pipe_writer(&self) -> Result<File, Error> {
99-
self.try_clone_pipe_writer().map(Option::unwrap)
100-
}
101-
102-
pub(crate) fn try_clone_pipe_writer(&self) -> Result<Option<File>, Error> {
103-
self.pipe_writer
104-
.as_ref()
105-
.map(File::try_clone)
106-
.transpose()
107-
.map_err(From::from)
73+
}
10874
}
10975
}
11076

111-
impl Drop for PrintThread {
112-
fn drop(&mut self) {
113-
// Drop pipe_writer first to avoid deadlock
114-
self.pipe_writer.take();
77+
fn write_warning(stdout: &Stdout, line: &[u8]) {
78+
let mut stdout = stdout.lock();
11579

116-
self.handle.take().unwrap().join().unwrap();
117-
}
80+
stdout.write_all(b"cargo:warning=").unwrap();
81+
stdout.write_all(line).unwrap();
82+
stdout.write_all(b"\n").unwrap();
11883
}
11984

120-
fn wait_on_child(cmd: &Command, program: &str, child: &mut Child) -> Result<(), Error> {
85+
fn wait_on_child(
86+
cmd: &Command,
87+
program: &str,
88+
child: &mut Child,
89+
cargo_output: &CargoOutput,
90+
) -> Result<(), Error> {
91+
cargo_output.forward_stderr_as_warnings(child);
12192
let status = match child.wait() {
12293
Ok(s) => s,
12394
Err(e) => {
@@ -193,20 +164,13 @@ pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<
193164
Ok(objects)
194165
}
195166

196-
fn run_inner(cmd: &mut Command, program: &str, pipe_writer: Option<File>) -> Result<(), Error> {
197-
let mut child = spawn(cmd, program, pipe_writer)?;
198-
wait_on_child(cmd, program, &mut child)
199-
}
200-
201167
pub(crate) fn run(
202168
cmd: &mut Command,
203169
program: &str,
204-
print: Option<&PrintThread>,
170+
cargo_output: &CargoOutput,
205171
) -> Result<(), Error> {
206-
let pipe_writer = print.map(PrintThread::clone_pipe_writer).transpose()?;
207-
run_inner(cmd, program, pipe_writer)?;
208-
209-
Ok(())
172+
let mut child = spawn(cmd, program, cargo_output)?;
173+
wait_on_child(cmd, program, &mut child, cargo_output)
210174
}
211175

212176
pub(crate) fn run_output(
@@ -216,12 +180,7 @@ pub(crate) fn run_output(
216180
) -> Result<Vec<u8>, Error> {
217181
cmd.stdout(Stdio::piped());
218182

219-
let mut print = cargo_output.print_thread()?;
220-
let mut child = spawn(
221-
cmd,
222-
program,
223-
print.as_mut().map(PrintThread::take_pipe_writer),
224-
)?;
183+
let mut child = spawn(cmd, program, cargo_output)?;
225184

226185
let mut stdout = vec![];
227186
child
@@ -231,15 +190,15 @@ pub(crate) fn run_output(
231190
.read_to_end(&mut stdout)
232191
.unwrap();
233192

234-
wait_on_child(cmd, program, &mut child)?;
193+
wait_on_child(cmd, program, &mut child, cargo_output)?;
235194

236195
Ok(stdout)
237196
}
238197

239198
pub(crate) fn spawn(
240199
cmd: &mut Command,
241200
program: &str,
242-
pipe_writer: Option<File>,
201+
cargo_output: &CargoOutput,
243202
) -> Result<Child, Error> {
244203
struct ResetStderr<'cmd>(&'cmd mut Command);
245204

@@ -254,10 +213,7 @@ pub(crate) fn spawn(
254213
println!("running: {:?}", cmd);
255214

256215
let cmd = ResetStderr(cmd);
257-
let child = cmd
258-
.0
259-
.stderr(pipe_writer.map_or_else(Stdio::null, Stdio::from))
260-
.spawn();
216+
let child = cmd.0.stderr(cargo_output.stdio_for_warnings()).spawn();
261217
match child {
262218
Ok(child) => Ok(child),
263219
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
@@ -307,8 +263,27 @@ pub(crate) fn try_wait_on_child(
307263
program: &str,
308264
child: &mut Child,
309265
stdout: &mut dyn io::Write,
266+
child_stderr_reader: &mut Option<BufReader<std::process::ChildStderr>>,
310267
) -> Result<Option<()>, Error> {
311-
match child.try_wait() {
268+
// Check the child status THEN forward stderr messages, so that we don't race
269+
// between the child printing messages and then exiting.
270+
let wait_result = child.try_wait();
271+
272+
if let Some(child_stderr_reader) = child_stderr_reader.as_mut() {
273+
if let Ok(mut available) = child_stderr_reader.fill_buf() {
274+
let stdout = io::stdout();
275+
let original_size = available.len();
276+
while let Some(i) = memchr::memchr(b'\n', available) {
277+
let (line, remainder) = available.split_at(i);
278+
write_warning(&stdout, line);
279+
available = &remainder[1..];
280+
}
281+
let consumed = original_size - available.len();
282+
child_stderr_reader.consume(consumed);
283+
}
284+
}
285+
286+
match wait_result {
312287
Ok(Some(status)) => {
313288
let _ = writeln!(stdout, "{}", status);
314289

0 commit comments

Comments
 (0)