@@ -2,14 +2,15 @@ use std::collections::HashMap;
2
2
use std:: env;
3
3
use std:: ffi:: { OsStr , OsString } ;
4
4
use std:: fmt;
5
+ use std:: io:: Write ;
5
6
use std:: path:: Path ;
6
7
use std:: process:: { Command , Output , Stdio } ;
7
8
8
9
use failure:: Fail ;
9
10
use jobserver:: Client ;
10
11
use shell_escape:: escape;
11
12
12
- use crate :: util:: { process_error, read2, CargoResult , CargoResultExt } ;
13
+ use crate :: util:: { internal , process_error, read2, CargoResult , CargoResultExt , CommandAndResponseFile } ;
13
14
14
15
/// A builder object for an external process, similar to `std::process::Command`.
15
16
#[ derive( Clone , Debug ) ]
@@ -29,6 +30,10 @@ pub struct ProcessBuilder {
29
30
jobserver : Option < Client > ,
30
31
/// `true` to include environment variable in display.
31
32
display_env_vars : bool ,
33
+ /// `true` to use a [@response_file] to workaround command line length limits.
34
+ ///
35
+ /// [@response_file]: https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path
36
+ response_file : bool ,
32
37
}
33
38
34
39
impl fmt:: Display for ProcessBuilder {
@@ -149,6 +154,14 @@ impl ProcessBuilder {
149
154
self
150
155
}
151
156
157
+ /// Enables the use of a [@response_file] to workaround command line length limits.
158
+ ///
159
+ /// [@response_file]: https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path
160
+ pub fn response_file ( & mut self ) -> & mut Self {
161
+ self . response_file = true ;
162
+ self
163
+ }
164
+
152
165
/// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
153
166
pub fn exec ( & self ) -> CargoResult < ( ) > {
154
167
let mut command = self . build_command ( ) ;
@@ -304,16 +317,22 @@ impl ProcessBuilder {
304
317
Ok ( output)
305
318
}
306
319
307
- /// Converts `ProcessBuilder` into a `std::process::Command `, and handles the jobserver, if
320
+ /// Converts `ProcessBuilder` into a `CommandAndResponseFile `, and handles the jobserver, if
308
321
/// present.
309
- pub fn build_command ( & self ) -> Command {
322
+ pub fn build_command ( & self ) -> CommandAndResponseFile {
310
323
let mut command = Command :: new ( & self . program ) ;
311
324
if let Some ( cwd) = self . get_cwd ( ) {
312
325
command. current_dir ( cwd) ;
313
326
}
314
- for arg in & self . args {
315
- command. arg ( arg) ;
316
- }
327
+ let response_file = if let Ok ( Some ( file) ) = self . build_response_file ( ) {
328
+ command. arg ( file. to_path_buf ( ) ) ;
329
+ Some ( file)
330
+ } else {
331
+ for arg in & self . args {
332
+ command. arg ( arg) ;
333
+ }
334
+ None
335
+ } ;
317
336
for ( k, v) in & self . env {
318
337
match * v {
319
338
Some ( ref v) => {
@@ -327,7 +346,19 @@ impl ProcessBuilder {
327
346
if let Some ( ref c) = self . jobserver {
328
347
c. configure ( & mut command) ;
329
348
}
330
- command
349
+ CommandAndResponseFile { command, response_file }
350
+ }
351
+
352
+ fn build_response_file ( & self ) -> CargoResult < Option < tempfile:: TempPath > > {
353
+ if !self . response_file || self . args . len ( ) == 0 {
354
+ return Ok ( None ) ;
355
+ }
356
+ let mut file = tempfile:: NamedTempFile :: new ( ) ?;
357
+ for arg in & self . args {
358
+ let arg = arg. to_str ( ) . ok_or_else ( || internal ( format ! ( "argument {:?} contains invalid unicode" , arg) ) ) ?;
359
+ writeln ! ( file, "{}" , arg) ?;
360
+ }
361
+ Ok ( Some ( file. into_temp_path ( ) ) )
331
362
}
332
363
}
333
364
@@ -340,6 +371,7 @@ pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
340
371
env : HashMap :: new ( ) ,
341
372
jobserver : None ,
342
373
display_env_vars : false ,
374
+ response_file : false ,
343
375
}
344
376
}
345
377
0 commit comments