From 68c6f894095a393d2c60704cfe58184b961cd749 Mon Sep 17 00:00:00 2001 From: Xavier Leroy Date: Sat, 25 Apr 2020 19:09:35 +0200 Subject: [PATCH] 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 --- Changes | 4 ++++ utils/ccomp.ml | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/Changes b/Changes index 140c4d912..4498b3d17 100644 --- a/Changes +++ b/Changes @@ -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 diff --git a/utils/ccomp.ml b/utils/ccomp.ml index cdf6840f6..2de6bb16c 100644 --- a/utils/ccomp.ml +++ b/utils/ccomp.ml @@ -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