@@ -137,44 +137,69 @@ impl Command {
137137 /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits,
138138 /// or `false` if we should attempt to spawn and see what the OS says.
139139 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-
146140 // Right now LLD doesn't support the `@` syntax of passing an argument
147141 // through files, so regardless of the platform we try to go to the OS
148142 // on this one.
149143 if let Program :: Lld ( ..) = self . program {
150144 return false ;
151145 }
152146
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 ( 0usize , |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 ( 0usize , |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. saturating_add ( envs_size) > arg_max
200+ } else {
201+ false
202+ }
178203 }
179204}
180205
0 commit comments