@@ -137,44 +137,69 @@ impl Command {
137
137
/// Returns a `true` if we're pretty sure that this'll blow OS spawn limits,
138
138
/// or `false` if we should attempt to spawn and see what the OS says.
139
139
pub ( crate ) fn very_likely_to_exceed_some_spawn_limit ( & self ) -> bool {
140
- // We mostly only care about Windows in this method, on Unix the limits
141
- // can be gargantuan anyway so we're pretty unlikely to hit them
142
- if cfg ! ( unix) {
143
- return false ;
144
- }
145
-
146
140
// Right now LLD doesn't support the `@` syntax of passing an argument
147
141
// through files, so regardless of the platform we try to go to the OS
148
142
// on this one.
149
143
if let Program :: Lld ( ..) = self . program {
150
144
return false ;
151
145
}
152
146
153
- // Ok so on Windows to spawn a process is 32,768 characters in its
154
- // command line [1]. Unfortunately we don't actually have access to that
155
- // as it's calculated just before spawning. Instead we perform a
156
- // poor-man's guess as to how long our command line will be. We're
157
- // assuming here that we don't have to escape every character...
158
- //
159
- // Turns out though that `cmd.exe` has even smaller limits, 8192
160
- // characters [2]. Linkers can often be batch scripts (for example
161
- // Emscripten, Gecko's current build system) which means that we're
162
- // running through batch scripts. These linkers often just forward
163
- // arguments elsewhere (and maybe tack on more), so if we blow 8192
164
- // bytes we'll typically cause them to blow as well.
165
- //
166
- // Basically as a result just perform an inflated estimate of what our
167
- // command line will look like and test if it's > 8192 (we actually
168
- // test against 6k to artificially inflate our estimate). If all else
169
- // fails we'll fall back to the normal unix logic of testing the OS
170
- // error code if we fail to spawn and automatically re-spawning the
171
- // linker with smaller arguments.
172
- //
173
- // [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
174
- // [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553
175
-
176
- let estimated_command_line_len = self . args . iter ( ) . map ( |a| a. len ( ) ) . sum :: < usize > ( ) ;
177
- estimated_command_line_len > 1024 * 6
147
+ if cfg ! ( windows) {
148
+ // Ok so on Windows to spawn a process is 32,768 characters in its
149
+ // command line [1]. Unfortunately we don't actually have access to that
150
+ // as it's calculated just before spawning. Instead we perform a
151
+ // poor-man's guess as to how long our command line will be. We're
152
+ // assuming here that we don't have to escape every character...
153
+ //
154
+ // Turns out though that `cmd.exe` has even smaller limits, 8192
155
+ // characters [2]. Linkers can often be batch scripts (for example
156
+ // Emscripten, Gecko's current build system) which means that we're
157
+ // running through batch scripts. These linkers often just forward
158
+ // arguments elsewhere (and maybe tack on more), so if we blow 8192
159
+ // bytes we'll typically cause them to blow as well.
160
+ //
161
+ // Basically as a result just perform an inflated estimate of what our
162
+ // command line will look like and test if it's > 8192 (we actually
163
+ // test against 6k to artificially inflate our estimate). If all else
164
+ // fails we'll fall back to the normal unix logic of testing the OS
165
+ // error code if we fail to spawn and automatically re-spawning the
166
+ // linker with smaller arguments.
167
+ //
168
+ // [1]: https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa
169
+ // [2]: https://devblogs.microsoft.com/oldnewthing/?p=41553
170
+ let estimated_command_line_len = self . args . iter ( ) . map ( |a| a. len ( ) ) . sum :: < usize > ( ) ;
171
+ estimated_command_line_len > 1024 * 6
172
+ } else if cfg ! ( unix) {
173
+ // On Unix the limits can be gargantuan anyway so we're pretty
174
+ // unlikely to hit them, but might still exceed it.
175
+ // We consult ARG_MAX here to get an estimate.
176
+ let ptr_size = mem:: size_of :: < usize > ( ) ;
177
+ // arg + \0 + pointer
178
+ let args_size = self . args . iter ( ) . fold ( 0 , |acc, a| {
179
+ let arg = a. as_encoded_bytes ( ) . len ( ) ;
180
+ let nul = 1 ;
181
+ acc. saturating_add ( arg) . saturating_add ( nul) . saturating_add ( ptr_size)
182
+ } ) ;
183
+ // key + `=` + value + \0 + pointer
184
+ let envs_size = self . env . iter ( ) . fold ( 0 , |acc, ( k, v) | {
185
+ let k = k. as_encoded_bytes ( ) . len ( ) ;
186
+ let eq = 1 ;
187
+ let v = v. as_encoded_bytes ( ) . len ( ) ;
188
+ let nul = 1 ;
189
+ acc. saturating_add ( k)
190
+ . saturating_add ( eq)
191
+ . saturating_add ( v)
192
+ . saturating_add ( nul)
193
+ . saturating_add ( ptr_size)
194
+ } ) ;
195
+ let arg_max = match unsafe { libc:: sysconf ( libc:: _SC_ARG_MAX) } {
196
+ -1 => return false , // Go to OS anyway.
197
+ max => max as usize ,
198
+ } ;
199
+ args_size + envs_size > arg_max
200
+ } else {
201
+ false
202
+ }
178
203
}
179
204
}
180
205
0 commit comments