Use diversion when calling external tools with a very long argument list (#9492)

It's not just on Windows that the length of the command passed to Sys.command
can exceed system limits:
- On Linux there is a per-argument limit of 2^17 bytes
  (the whole command is a single argument to /bin/sh)
- On macOS with default parameters, the limit is between 70000 and 80000
- On BSDs with default parameters, the limit is around 2^18.

In parallel, response files (@file) are supported by all the C compilers
we've encountered: GCC, Clang, Intel's ICC, and even CompCert.  They all
seem to follow quoting rules similar to that of the native shell
for the contents of the response file.

This commit forces the use of a response file when the total size of
the arguments to the linker is greater than 2^16.

Arguments passed via a response file are quoted using Filename.quote
as if they were passed on the command line.

Closes: #9482
Closes: #8549
master
Xavier Leroy 2020-04-25 19:09:35 +02:00 committed by GitHub
parent 1e98c52e93
commit 68c6f89409
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 17 additions and 3 deletions

View File

@ -240,6 +240,10 @@ OCaml 4.11
Use KEEP_TEST_DIR_ON_SUCCESS=1 to keep all artifacts.
(Gabriel Scherer, review by Sébastien Hinderer)
- #9482, #9492: use diversions (@file) to work around OS limitations
on length of Sys.command argument.
(Xavier Leroy, report by Jérémie Dimino, review by David Allsopp)
### Manual and documentation:
- #9141: beginning of the ocamltest reference manual

View File

@ -27,8 +27,17 @@ let command cmdline =
let run_command cmdline = ignore(command cmdline)
(* Build @responsefile to work around Windows limitations on
command-line length *)
(* Build @responsefile to work around OS limitations on
command-line length.
Under Windows, the max length is 8187 minus the length of the
COMSPEC variable (or 7 if it's not set). To be on the safe side,
we'll use a response file if we need to pass 4096 or more bytes of
arguments.
For Unix-like systems, the threshold is 2^16 (64 KiB), which is
within the lowest observed limits (2^17 per argument under Linux;
between 70000 and 80000 for macOS).
*)
let build_diversion lst =
let (responsefile, oc) = Filename.open_temp_file "camlresp" "" in
List.iter (fun f -> Printf.fprintf oc "%s\n" f) lst;
@ -40,7 +49,8 @@ let quote_files lst =
let lst = List.filter (fun f -> f <> "") lst in
let quoted = List.map Filename.quote lst in
let s = String.concat " " quoted in
if String.length s >= 4096 && Sys.os_type = "Win32"
if String.length s >= 65536
|| (String.length s >= 4096 && Sys.os_type = "Win32")
then build_diversion quoted
else s