Skip to content

Commit 5a045cc

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

File tree

10 files changed

+200
-326
lines changed

10 files changed

+200
-326
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ rust-version = "1.53"
2222
[target.'cfg(unix)'.dependencies]
2323
# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866
2424
# which is still an issue with `resolver = "1"`.
25-
libc = { version = "0.2.62", default-features = false }
25+
libc = { version = "0.2.62", default-features = false, optional = true }
2626

2727
[features]
28-
parallel = []
28+
parallel = ["libc"]
2929

3030
[dev-dependencies]
3131
tempfile = "3"

dev-tools/gen-windows-sys-binding/windows_sys.list

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
Windows.Win32.Foundation.FILETIME
2-
Windows.Win32.Foundation.INVALID_HANDLE_VALUE
32
Windows.Win32.Foundation.ERROR_NO_MORE_ITEMS
43
Windows.Win32.Foundation.ERROR_SUCCESS
54
Windows.Win32.Foundation.SysFreeString
@@ -20,7 +19,7 @@ Windows.Win32.System.Com.COINIT_MULTITHREADED
2019
Windows.Win32.System.Com.CoCreateInstance
2120
Windows.Win32.System.Com.CoInitializeEx
2221

23-
Windows.Win32.System.Pipes.CreatePipe
22+
Windows.Win32.System.Pipes.PeekNamedPipe
2423

2524
Windows.Win32.System.Registry.RegCloseKey
2625
Windows.Win32.System.Registry.RegEnumKeyExW

src/command_helpers.rs

Lines changed: 114 additions & 95 deletions
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, Read, Write},
1010
path::Path,
11-
process::{Child, Command, Stdio},
11+
process::{Child, ChildStderr, Command, Stdio},
1212
sync::Arc,
13-
thread::{self, JoinHandle},
1413
};
1514

1615
use crate::{Error, ErrorKind, Object};
@@ -41,83 +40,110 @@ 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
}
4750
}
4851

49-
pub(crate) struct PrintThread {
50-
handle: Option<JoinHandle<()>>,
51-
pipe_writer: Option<File>,
52+
pub(crate) struct StderrForwarder {
53+
inner: Option<(ChildStderr, Vec<u8>)>,
5254
}
5355

54-
impl PrintThread {
55-
pub(crate) fn new() -> Result<Self, Error> {
56-
let (pipe_reader, pipe_writer) = crate::os_pipe::pipe()?;
57-
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);
64-
let mut line = Vec::with_capacity(20);
65-
let stdout = io::stdout();
66-
67-
// 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-
}
76-
77-
// read_until does not clear the buffer
78-
line.clear();
79-
}
80-
});
56+
const MIN_BUFFER_CAPACITY: usize = 100;
8157

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)
58+
impl StderrForwarder {
59+
pub(crate) fn new(child: &mut Child) -> Self {
60+
Self {
61+
inner: child
62+
.stderr
63+
.take()
64+
.map(|stderr| (stderr, Vec::with_capacity(MIN_BUFFER_CAPACITY))),
65+
}
10066
}
10167

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)
68+
#[allow(clippy::uninit_vec)]
69+
fn forward(&mut self, non_blocking: bool) -> bool {
70+
if let Some((stderr, buffer)) = self.inner.as_mut() {
71+
loop {
72+
let old_data_end = buffer.len();
73+
74+
// For non-blocking we check to see if there is data available, so we should try to
75+
// read at least that much. For blocking, always read at least the minimum amount.
76+
let to_reserve = if non_blocking {
77+
#[cfg(feature = "parallel")]
78+
match crate::parallel::stderr::bytes_available(stderr) {
79+
Ok(0) => return false,
80+
Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available),
81+
Err(_) => {
82+
// Error: flush remaining data and bail.
83+
if !buffer.is_empty() {
84+
write_warning(&buffer[..]);
85+
}
86+
self.inner = None;
87+
return true;
88+
}
89+
}
90+
#[cfg(not(feature = "parallel"))]
91+
panic!("Non-blocking mode is only available with the parallel feature");
92+
} else {
93+
MIN_BUFFER_CAPACITY
94+
};
95+
buffer.reserve(to_reserve);
96+
97+
// SAFETY: 1) the length is set to the capacity, so we are never using memory beyond
98+
// the underlying buffer and 2) we always call `truncate` below to set the len back
99+
// to the intitialized data.
100+
unsafe {
101+
buffer.set_len(buffer.capacity());
102+
}
103+
match stderr.read(&mut buffer[old_data_end..]) {
104+
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {
105+
// Interrupted, try again.
106+
buffer.truncate(old_data_end);
107+
}
108+
Ok(0) | Err(_) => {
109+
// End of stream: flush remaining data and bail.
110+
if old_data_end > 0 {
111+
write_warning(&buffer[..old_data_end]);
112+
}
113+
self.inner = None;
114+
return true;
115+
}
116+
Ok(bytes_read) => {
117+
buffer.truncate(old_data_end + bytes_read);
118+
let mut consumed = 0;
119+
for line in buffer.split_inclusive(|&b| b == b'\n') {
120+
// Only forward complete lines, leave the rest in the buffer.
121+
if let Some((b'\n', line)) = line.split_last() {
122+
consumed += line.len() + 1;
123+
write_warning(line);
124+
}
125+
}
126+
buffer.drain(..consumed);
127+
}
128+
}
129+
}
130+
} else {
131+
true
132+
}
108133
}
109134
}
110135

111-
impl Drop for PrintThread {
112-
fn drop(&mut self) {
113-
// Drop pipe_writer first to avoid deadlock
114-
self.pipe_writer.take();
115-
116-
self.handle.take().unwrap().join().unwrap();
117-
}
136+
fn write_warning(line: &[u8]) {
137+
let stdout = io::stdout();
138+
let mut stdout = stdout.lock();
139+
stdout.write_all(b"cargo:warning=").unwrap();
140+
stdout.write_all(line).unwrap();
141+
stdout.write_all(b"\n").unwrap();
118142
}
119143

120144
fn wait_on_child(cmd: &Command, program: &str, child: &mut Child) -> Result<(), Error> {
145+
StderrForwarder::new(child).forward(false);
146+
121147
let status = match child.wait() {
122148
Ok(s) => s,
123149
Err(e) => {
@@ -193,20 +219,13 @@ pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<
193219
Ok(objects)
194220
}
195221

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-
201222
pub(crate) fn run(
202223
cmd: &mut Command,
203224
program: &str,
204-
print: Option<&PrintThread>,
225+
cargo_output: &CargoOutput,
205226
) -> Result<(), Error> {
206-
let pipe_writer = print.map(PrintThread::clone_pipe_writer).transpose()?;
207-
run_inner(cmd, program, pipe_writer)?;
208-
209-
Ok(())
227+
let mut child = spawn(cmd, program, cargo_output)?;
228+
wait_on_child(cmd, program, &mut child)
210229
}
211230

212231
pub(crate) fn run_output(
@@ -216,12 +235,7 @@ pub(crate) fn run_output(
216235
) -> Result<Vec<u8>, Error> {
217236
cmd.stdout(Stdio::piped());
218237

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-
)?;
238+
let mut child = spawn(cmd, program, cargo_output)?;
225239

226240
let mut stdout = vec![];
227241
child
@@ -239,7 +253,7 @@ pub(crate) fn run_output(
239253
pub(crate) fn spawn(
240254
cmd: &mut Command,
241255
program: &str,
242-
pipe_writer: Option<File>,
256+
cargo_output: &CargoOutput,
243257
) -> Result<Child, Error> {
244258
struct ResetStderr<'cmd>(&'cmd mut Command);
245259

@@ -254,10 +268,7 @@ pub(crate) fn spawn(
254268
println!("running: {:?}", cmd);
255269

256270
let cmd = ResetStderr(cmd);
257-
let child = cmd
258-
.0
259-
.stderr(pipe_writer.map_or_else(Stdio::null, Stdio::from))
260-
.spawn();
271+
let child = cmd.0.stderr(cargo_output.stdio_for_warnings()).spawn();
261272
match child {
262273
Ok(child) => Ok(child),
263274
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
@@ -307,9 +318,14 @@ pub(crate) fn try_wait_on_child(
307318
program: &str,
308319
child: &mut Child,
309320
stdout: &mut dyn io::Write,
321+
stderr_forwarder: &mut StderrForwarder,
310322
) -> Result<Option<()>, Error> {
323+
stderr_forwarder.forward(true);
324+
311325
match child.try_wait() {
312326
Ok(Some(status)) => {
327+
stderr_forwarder.forward(false);
328+
313329
let _ = writeln!(stdout, "{}", status);
314330

315331
if status.success() {
@@ -325,12 +341,15 @@ pub(crate) fn try_wait_on_child(
325341
}
326342
}
327343
Ok(None) => Ok(None),
328-
Err(e) => Err(Error::new(
329-
ErrorKind::ToolExecError,
330-
format!(
331-
"Failed to wait on spawned child process, command {:?} with args {:?}: {}.",
332-
cmd, program, e
333-
),
334-
)),
344+
Err(e) => {
345+
stderr_forwarder.forward(false);
346+
Err(Error::new(
347+
ErrorKind::ToolExecError,
348+
format!(
349+
"Failed to wait on spawned child process, command {:?} with args {:?}: {}.",
350+
cmd, program, e
351+
),
352+
))
353+
}
335354
}
336355
}

0 commit comments

Comments
 (0)