|
14 | 14 | use std::ffi::{OsStr, OsString};
|
15 | 15 | use std::fmt;
|
16 | 16 | use std::io;
|
| 17 | +use std::mem; |
17 | 18 | use std::process::{self, Output, Child};
|
18 | 19 |
|
| 20 | +#[derive(Clone)] |
19 | 21 | pub struct Command {
|
20 |
| - program: OsString, |
| 22 | + program: Program, |
21 | 23 | args: Vec<OsString>,
|
22 | 24 | env: Vec<(OsString, OsString)>,
|
23 | 25 | }
|
24 | 26 |
|
| 27 | +#[derive(Clone)] |
| 28 | +enum Program { |
| 29 | + Normal(OsString), |
| 30 | + CmdBatScript(OsString), |
| 31 | +} |
| 32 | + |
25 | 33 | impl Command {
|
26 | 34 | pub fn new<P: AsRef<OsStr>>(program: P) -> Command {
|
27 |
| - Command::_new(program.as_ref()) |
| 35 | + Command::_new(Program::Normal(program.as_ref().to_owned())) |
| 36 | + } |
| 37 | + |
| 38 | + pub fn bat_script<P: AsRef<OsStr>>(program: P) -> Command { |
| 39 | + Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) |
28 | 40 | }
|
29 | 41 |
|
30 |
| - fn _new(program: &OsStr) -> Command { |
| 42 | + fn _new(program: Program) -> Command { |
31 | 43 | Command {
|
32 |
| - program: program.to_owned(), |
| 44 | + program, |
33 | 45 | args: Vec::new(),
|
34 | 46 | env: Vec::new(),
|
35 | 47 | }
|
@@ -86,24 +98,60 @@ impl Command {
|
86 | 98 | }
|
87 | 99 |
|
88 | 100 | pub fn command(&self) -> process::Command {
|
89 |
| - let mut ret = process::Command::new(&self.program); |
| 101 | + let mut ret = match self.program { |
| 102 | + Program::Normal(ref p) => process::Command::new(p), |
| 103 | + Program::CmdBatScript(ref p) => { |
| 104 | + let mut c = process::Command::new("cmd"); |
| 105 | + c.arg("/c").arg(p); |
| 106 | + c |
| 107 | + } |
| 108 | + }; |
90 | 109 | ret.args(&self.args);
|
91 | 110 | ret.envs(self.env.clone());
|
92 | 111 | return ret
|
93 | 112 | }
|
94 | 113 |
|
95 | 114 | // extensions
|
96 | 115 |
|
97 |
| - pub fn get_program(&self) -> &OsStr { |
98 |
| - &self.program |
| 116 | + pub fn take_args(&mut self) -> Vec<OsString> { |
| 117 | + mem::replace(&mut self.args, Vec::new()) |
99 | 118 | }
|
100 | 119 |
|
101 |
| - pub fn get_args(&self) -> &[OsString] { |
102 |
| - &self.args |
103 |
| - } |
| 120 | + /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, |
| 121 | + /// or `false` if we should attempt to spawn and see what the OS says. |
| 122 | + pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { |
| 123 | + // We mostly only care about Windows in this method, on Unix the limits |
| 124 | + // can be gargantuan anyway so we're pretty unlikely to hit them |
| 125 | + if cfg!(unix) { |
| 126 | + return false |
| 127 | + } |
104 | 128 |
|
105 |
| - pub fn get_env(&self) -> &[(OsString, OsString)] { |
106 |
| - &self.env |
| 129 | + // Ok so on Windows to spawn a process is 32,768 characters in its |
| 130 | + // command line [1]. Unfortunately we don't actually have access to that |
| 131 | + // as it's calculated just before spawning. Instead we perform a |
| 132 | + // poor-man's guess as to how long our command line will be. We're |
| 133 | + // assuming here that we don't have to escape every character... |
| 134 | + // |
| 135 | + // Turns out though that `cmd.exe` has even smaller limits, 8192 |
| 136 | + // characters [2]. Linkers can often be batch scripts (for example |
| 137 | + // Emscripten, Gecko's current build system) which means that we're |
| 138 | + // running through batch scripts. These linkers often just forward |
| 139 | + // arguments elsewhere (and maybe tack on more), so if we blow 8192 |
| 140 | + // bytes we'll typically cause them to blow as well. |
| 141 | + // |
| 142 | + // Basically as a result just perform an inflated estimate of what our |
| 143 | + // command line will look like and test if it's > 8192 (we actually |
| 144 | + // test against 6k to artificially inflate our estimate). If all else |
| 145 | + // fails we'll fall back to the normal unix logic of testing the OS |
| 146 | + // error code if we fail to spawn and automatically re-spawning the |
| 147 | + // linker with smaller arguments. |
| 148 | + // |
| 149 | + // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx |
| 150 | + // [2]: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 |
| 151 | + |
| 152 | + let estimated_command_line_len = |
| 153 | + self.args.iter().map(|a| a.len()).sum::<usize>(); |
| 154 | + estimated_command_line_len > 1024 * 6 |
107 | 155 | }
|
108 | 156 | }
|
109 | 157 |
|
|
0 commit comments