Skip to content

Commit 9161b3a

Browse files
committedJan 1, 2020
Use response files when invoking rustc
This would workaround windows command line length limits when using lots and lots of features. rust-lang#7759
1 parent 4111e1a commit 9161b3a

File tree

5 files changed

+70
-10
lines changed

5 files changed

+70
-10
lines changed
 

‎crates/cargo-test-support/src/lib.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@ use std::fs;
115115
use std::io::prelude::*;
116116
use std::os;
117117
use std::path::{Path, PathBuf};
118-
use std::process::{Command, Output};
118+
use std::process::{Output};
119119
use std::str;
120120
use std::time::{self, Duration};
121121
use std::usize;
122122

123123
use cargo;
124-
use cargo::util::{is_ci, CargoResult, ProcessBuilder, ProcessError, Rustc};
124+
use cargo::util::{is_ci, CargoResult, CommandAndResponseFile, ProcessBuilder, ProcessError, Rustc};
125125
use filetime;
126126
use serde_json::{self, Value};
127127
use url::Url;
@@ -828,7 +828,7 @@ impl Execs {
828828
p.exec_with_output()
829829
}
830830

831-
pub fn build_command(&mut self) -> Command {
831+
pub fn build_command(&mut self) -> CommandAndResponseFile {
832832
self.ran = true;
833833
// TODO avoid unwrap
834834
let p = (&self.process_builder).clone().unwrap();

‎src/cargo/core/compiler/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -548,6 +548,7 @@ fn rustdoc<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult
548548
let bcx = cx.bcx;
549549
let mut rustdoc = cx.compilation.rustdoc_process(unit.pkg, unit.target)?;
550550
rustdoc.inherit_jobserver(&cx.jobserver);
551+
rustdoc.response_file();
551552
rustdoc.arg("--crate-name").arg(&unit.target.crate_name());
552553
add_path_args(bcx, unit, &mut rustdoc);
553554
add_cap_lints(bcx, unit, &mut rustdoc);
@@ -709,6 +710,7 @@ fn build_base_args<'a, 'cfg>(
709710
} = unit.profile;
710711
let test = unit.mode.is_any_test();
711712

713+
cmd.response_file();
712714
cmd.arg("--crate-name").arg(&unit.target.crate_name());
713715

714716
let edition = unit.target.edition();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use std::ops::{Deref, DerefMut};
2+
use std::process::Command;
3+
4+
use tempfile::TempPath;
5+
6+
/// A wrapper around `Command` which extends the lifetime of associated
7+
/// temporary response files until the command is executed.
8+
pub struct CommandAndResponseFile {
9+
pub command: Command,
10+
pub response_file: Option<TempPath>,
11+
}
12+
13+
impl Deref for CommandAndResponseFile {
14+
type Target = Command;
15+
fn deref(&self) -> &Self::Target {
16+
&self.command
17+
}
18+
}
19+
20+
impl DerefMut for CommandAndResponseFile {
21+
fn deref_mut(&mut self) -> &mut Self::Target {
22+
&mut self.command
23+
}
24+
}

‎src/cargo/util/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::time::Duration;
22

33
pub use self::canonical_url::CanonicalUrl;
4+
pub use self::command_and_response_file::CommandAndResponseFile;
45
pub use self::config::{homedir, Config, ConfigValue};
56
pub use self::dependency_queue::DependencyQueue;
67
pub use self::diagnostic_server::RustfixDiagnosticServer;
@@ -29,6 +30,7 @@ pub use self::workspace::{
2930
};
3031

3132
mod canonical_url;
33+
mod command_and_response_file;
3234
pub mod command_prelude;
3335
pub mod config;
3436
pub mod cpu;

‎src/cargo/util/process_builder.rs

+39-7
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ use std::collections::HashMap;
22
use std::env;
33
use std::ffi::{OsStr, OsString};
44
use std::fmt;
5+
use std::io::Write;
56
use std::path::Path;
67
use std::process::{Command, Output, Stdio};
78

89
use failure::Fail;
910
use jobserver::Client;
1011
use shell_escape::escape;
1112

12-
use crate::util::{process_error, read2, CargoResult, CargoResultExt};
13+
use crate::util::{internal, process_error, read2, CargoResult, CargoResultExt, CommandAndResponseFile};
1314

1415
/// A builder object for an external process, similar to `std::process::Command`.
1516
#[derive(Clone, Debug)]
@@ -29,6 +30,10 @@ pub struct ProcessBuilder {
2930
jobserver: Option<Client>,
3031
/// `true` to include environment variable in display.
3132
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,
3237
}
3338

3439
impl fmt::Display for ProcessBuilder {
@@ -149,6 +154,14 @@ impl ProcessBuilder {
149154
self
150155
}
151156

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+
152165
/// Runs the process, waiting for completion, and mapping non-success exit codes to an error.
153166
pub fn exec(&self) -> CargoResult<()> {
154167
let mut command = self.build_command();
@@ -304,16 +317,22 @@ impl ProcessBuilder {
304317
Ok(output)
305318
}
306319

307-
/// Converts `ProcessBuilder` into a `std::process::Command`, and handles the jobserver, if
320+
/// Converts `ProcessBuilder` into a `CommandAndResponseFile`, and handles the jobserver, if
308321
/// present.
309-
pub fn build_command(&self) -> Command {
322+
pub fn build_command(&self) -> CommandAndResponseFile {
310323
let mut command = Command::new(&self.program);
311324
if let Some(cwd) = self.get_cwd() {
312325
command.current_dir(cwd);
313326
}
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+
};
317336
for (k, v) in &self.env {
318337
match *v {
319338
Some(ref v) => {
@@ -327,7 +346,19 @@ impl ProcessBuilder {
327346
if let Some(ref c) = self.jobserver {
328347
c.configure(&mut command);
329348
}
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()))
331362
}
332363
}
333364

@@ -340,6 +371,7 @@ pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
340371
env: HashMap::new(),
341372
jobserver: None,
342373
display_env_vars: false,
374+
response_file: false,
343375
}
344376
}
345377

0 commit comments

Comments
 (0)
Please sign in to comment.