@@ -13,7 +13,7 @@ use std::fmt;
13
13
use std:: io;
14
14
use std:: iter:: once;
15
15
use std:: path:: Path ;
16
- use std:: process:: { Child , Command , ExitStatus , Output , Stdio } ;
16
+ use std:: process:: { Command , ExitStatus , Output , Stdio } ;
17
17
18
18
/// A builder object for an external process, similar to [`std::process::Command`].
19
19
#[ derive( Clone , Debug ) ]
@@ -213,11 +213,19 @@ impl ProcessBuilder {
213
213
214
214
/// Like [`Command::status`] but with a better error message.
215
215
pub fn status ( & self ) -> Result < ExitStatus > {
216
- self . build_and_spawn ( |_| { } )
217
- . and_then ( |mut child| child. wait ( ) )
218
- . with_context ( || {
219
- ProcessError :: new ( & format ! ( "could not execute process {self}" ) , None , None )
220
- } )
216
+ self . _status ( )
217
+ . with_context ( || ProcessError :: could_not_execute ( self ) )
218
+ }
219
+
220
+ fn _status ( & self ) -> io:: Result < ExitStatus > {
221
+ let mut cmd = self . build_command ( ) ;
222
+ match cmd. spawn ( ) {
223
+ Err ( ref e) if self . should_retry_with_argfile ( e) => { }
224
+ Err ( e) => return Err ( e) ,
225
+ Ok ( mut child) => return child. wait ( ) ,
226
+ }
227
+ let ( mut cmd, _argfile) = self . build_command_with_argfile ( ) ?;
228
+ cmd. spawn ( ) ?. wait ( )
221
229
}
222
230
223
231
/// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
@@ -256,15 +264,19 @@ impl ProcessBuilder {
256
264
257
265
/// Like [`Command::output`] but with a better error message.
258
266
pub fn output ( & self ) -> Result < Output > {
259
- self . build_and_spawn ( |cmd| {
260
- cmd. stdout ( Stdio :: piped ( ) )
261
- . stderr ( Stdio :: piped ( ) )
262
- . stdin ( Stdio :: null ( ) ) ;
263
- } )
264
- . and_then ( |child| child. wait_with_output ( ) )
265
- . with_context ( || {
266
- ProcessError :: new ( & format ! ( "could not execute process {self}" ) , None , None )
267
- } )
267
+ self . _output ( )
268
+ . with_context ( || ProcessError :: could_not_execute ( self ) )
269
+ }
270
+
271
+ fn _output ( & self ) -> io:: Result < Output > {
272
+ let mut cmd = self . build_command ( ) ;
273
+ match piped ( & mut cmd) . spawn ( ) {
274
+ Err ( ref e) if self . should_retry_with_argfile ( e) => { }
275
+ Err ( e) => return Err ( e) ,
276
+ Ok ( child) => return child. wait_with_output ( ) ,
277
+ }
278
+ let ( mut cmd, _argfile) = self . build_command_with_argfile ( ) ?;
279
+ piped ( & mut cmd) . spawn ( ) ?. wait_with_output ( )
268
280
}
269
281
270
282
/// Executes the process, returning the stdio output, or an error if non-zero exit status.
@@ -303,12 +315,20 @@ impl ProcessBuilder {
303
315
let mut callback_error = None ;
304
316
let mut stdout_pos = 0 ;
305
317
let mut stderr_pos = 0 ;
318
+
319
+ let spawn = |mut cmd| {
320
+ match piped ( & mut cmd) . spawn ( ) {
321
+ Err ( ref e) if self . should_retry_with_argfile ( e) => { }
322
+ Err ( e) => return Err ( e) ,
323
+ Ok ( child) => return Ok ( ( child, None ) ) ,
324
+ } ;
325
+ let ( mut cmd, argfile) = self . build_command_with_argfile ( ) ?;
326
+ Ok ( ( piped ( & mut cmd) . spawn ( ) ?, Some ( argfile) ) )
327
+ } ;
328
+
306
329
let status = ( || {
307
- let mut child = self . build_and_spawn ( |cmd| {
308
- cmd. stdout ( Stdio :: piped ( ) )
309
- . stderr ( Stdio :: piped ( ) )
310
- . stdin ( Stdio :: null ( ) ) ;
311
- } ) ?;
330
+ let cmd = self . build_command ( ) ;
331
+ let ( mut child, _argfile) = spawn ( cmd) ?;
312
332
let out = child. stdout . take ( ) . unwrap ( ) ;
313
333
let err = child. stderr . take ( ) . unwrap ( ) ;
314
334
read2 ( out, err, & mut |is_out, data, eof| {
@@ -356,9 +376,7 @@ impl ProcessBuilder {
356
376
} ) ?;
357
377
child. wait ( )
358
378
} ) ( )
359
- . with_context ( || {
360
- ProcessError :: new ( & format ! ( "could not execute process {}" , self ) , None , None )
361
- } ) ?;
379
+ . with_context ( || ProcessError :: could_not_execute ( self ) ) ?;
362
380
let output = Output {
363
381
status,
364
382
stdout,
@@ -386,28 +404,6 @@ impl ProcessBuilder {
386
404
Ok ( output)
387
405
}
388
406
389
- /// Builds a command from `ProcessBuilder` and spawn it.
390
- ///
391
- /// There is a risk when spawning a process, it might hit "command line
392
- /// too big" OS error. To handle those kind of OS errors, this method try
393
- /// to reinvoke the command with a `@<path>` argfile that contains all the
394
- /// arguments.
395
- ///
396
- /// * `apply`: Modify the command before invoking. Useful for updating [`Stdio`].
397
- fn build_and_spawn ( & self , apply : impl Fn ( & mut Command ) ) -> io:: Result < Child > {
398
- let mut cmd = self . build_command ( ) ;
399
- apply ( & mut cmd) ;
400
-
401
- match cmd. spawn ( ) {
402
- Err ( ref e) if self . should_retry_with_argfile ( e) => {
403
- let ( mut cmd, _argfile) = self . build_command_with_argfile ( ) ?;
404
- apply ( & mut cmd) ;
405
- cmd. spawn ( )
406
- }
407
- res => res,
408
- }
409
- }
410
-
411
407
/// Builds the command with an `@<path>` argfile that contains all the
412
408
/// arguments. This is primarily served for rustc/rustdoc command family.
413
409
fn build_command_with_argfile ( & self ) -> io:: Result < ( Command , NamedTempFile ) > {
@@ -509,6 +505,13 @@ impl ProcessBuilder {
509
505
}
510
506
}
511
507
508
+ /// Creates new pipes for stderr and stdout. Ignores stdin.
509
+ fn piped ( cmd : & mut Command ) -> & mut Command {
510
+ cmd. stdout ( Stdio :: piped ( ) )
511
+ . stderr ( Stdio :: piped ( ) )
512
+ . stdin ( Stdio :: null ( ) )
513
+ }
514
+
512
515
#[ cfg( unix) ]
513
516
mod imp {
514
517
use super :: { ProcessBuilder , ProcessError } ;
0 commit comments