|
|
|
(*
|
|
|
|
* Copyright (c) 2017 - present Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*)
|
|
|
|
|
|
|
|
open! IStd
|
|
|
|
|
|
|
|
module L = Logging
|
|
|
|
module F = Format
|
|
|
|
|
|
|
|
type compiler = Java | Javac [@@ deriving compare]
|
|
|
|
|
|
|
|
let compile compiler build_prog build_args =
|
|
|
|
let prog, prog_args =
|
|
|
|
match compiler, Config.java_jar_compiler with
|
|
|
|
| _, None -> (build_prog, ["-J-Duser.language=en"])
|
|
|
|
| Java, Some jar -> (build_prog, ["-jar"; jar])
|
|
|
|
| _, Some jar -> (* fall back to java in PATH to avoid passing -jar to javac *)
|
|
|
|
("java", ["-jar"; jar]) in
|
|
|
|
let cli_args, file_args =
|
|
|
|
let rec has_classes_out = function
|
|
|
|
| [] -> false
|
|
|
|
| ("-d" | "-classes_out")::_ -> true
|
|
|
|
| file_arg::tl when String.is_prefix file_arg ~prefix:"@" -> (
|
|
|
|
let fname = String.slice file_arg 1 (String.length file_arg) in
|
|
|
|
match In_channel.read_lines fname with
|
|
|
|
| lines ->
|
|
|
|
(* crude but we only care about simple cases that will not involve trickiness, eg
|
|
|
|
unbalanced or escaped quotes such as "ending in\"" *)
|
|
|
|
let lines_without_quotes =
|
|
|
|
List.map ~f:(String.strip ~drop:(function '"' | '\'' -> true | _ -> false)) lines in
|
|
|
|
has_classes_out lines_without_quotes || has_classes_out tl
|
|
|
|
| exception _ ->
|
|
|
|
has_classes_out tl)
|
|
|
|
| _::tl ->
|
|
|
|
has_classes_out tl in
|
|
|
|
let args =
|
|
|
|
"-verbose" :: "-g" ::
|
|
|
|
(* Ensure that some form of "-d ..." is passed to javac. It's unclear whether this is strictly
|
|
|
|
needed but the tests break without this for now. See discussion in D4397716. *)
|
|
|
|
if has_classes_out build_args then
|
|
|
|
build_args
|
|
|
|
else
|
|
|
|
"-d" :: Config.javac_classes_out :: build_args in
|
|
|
|
List.partition_tf args ~f:(fun arg ->
|
|
|
|
(* As mandated by javac, argument files must not contain certain arguments. *)
|
|
|
|
String.is_prefix ~prefix:"-J" arg || String.is_prefix ~prefix:"@" arg) in
|
|
|
|
(* Pass non-special args via a file to avoid exceeding the command line size limit. *)
|
|
|
|
let args_file =
|
|
|
|
let file = Filename.temp_file "args_" "" in
|
|
|
|
let quoted_file_args =
|
|
|
|
List.map file_args ~f:(fun arg ->
|
|
|
|
if String.contains arg '\'' then arg else F.sprintf "'%s'" arg) in
|
|
|
|
Out_channel.with_file file ~f:(fun oc -> Out_channel.output_lines oc quoted_file_args) ;
|
|
|
|
file in
|
|
|
|
let cli_file_args = cli_args @ ["@" ^ args_file] in
|
|
|
|
let args = prog_args @ cli_file_args in
|
|
|
|
let verbose_out_file = Filename.temp_file "javac_" ".out" in
|
|
|
|
Unix.with_file verbose_out_file ~mode:[Unix.O_WRONLY] ~f:(
|
|
|
|
fun verbose_out_fd ->
|
|
|
|
L.out "Logging into %s@\n" verbose_out_file;
|
|
|
|
L.out "Current working directory: '%s'@." (Sys.getcwd ());
|
|
|
|
try
|
|
|
|
L.out "Trying to execute: '%s' '%s'@." prog (String.concat ~sep:"' '" args);
|
|
|
|
Unix_.fork_redirect_exec_wait ~prog ~args ~stderr:verbose_out_fd ()
|
|
|
|
with exn ->
|
|
|
|
try
|
|
|
|
L.out "*** Failed!@\nTrying to execute javac instead: '%s' '%s'@\nLogging into %s@."
|
|
|
|
"javac" (String.concat ~sep:"' '" cli_file_args) verbose_out_file;
|
|
|
|
Unix_.fork_redirect_exec_wait ~prog:"javac" ~args:cli_file_args ~stderr:verbose_out_fd ()
|
|
|
|
with _ ->
|
|
|
|
L.stderr "Failed to execute: %s %s@." prog (String.concat ~sep:" " args);
|
|
|
|
raise exn
|
|
|
|
);
|
|
|
|
verbose_out_file
|
|
|
|
|
|
|
|
|
|
|
|
let capture compiler ~prog ~args =
|
|
|
|
let verbose_out_file = compile compiler prog args in
|
|
|
|
if Config.analyzer <> Config.Compile then
|
|
|
|
JMain.from_verbose_out verbose_out_file;
|
|
|
|
if not (Config.debug_mode || Config.stats_mode) then Unix.unlink verbose_out_file
|