You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
7.7 KiB
188 lines
7.7 KiB
(*
|
|
* Copyright (c) 2016 - 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! Utils
|
|
|
|
module CLOpt = CommandLineOption
|
|
module F = Format
|
|
|
|
let capture_text =
|
|
if Config.analyzer = Config.Linters then "linting"
|
|
else "translating"
|
|
|
|
let replace_header_file_with_source_file file_path =
|
|
let file_path = DB.source_file_to_abs_path file_path in
|
|
let possible_file_replacements file_path =
|
|
IList.map (fun suffix -> (Filename.chop_extension file_path) ^ suffix ) [".m"; ".mm"] in
|
|
let file =
|
|
if Filename.check_suffix file_path ".h" || Filename.check_suffix file_path ".hh" then
|
|
try
|
|
IList.find Sys.file_exists (possible_file_replacements file_path)
|
|
with Not_found ->
|
|
Logging.out "Couldn't find any replacement source file for file %s " file_path;
|
|
file_path
|
|
else file_path in
|
|
DB.abs_source_file_from_path file
|
|
|
|
(** Read the files to compile from the changed files index. *)
|
|
let read_files_to_compile () =
|
|
let changed_files = DB.SourceFileSet.empty in
|
|
match DB.read_changed_files_index with
|
|
| None ->
|
|
(match Config.changed_files_index with
|
|
| Some index ->
|
|
Process.print_error_and_exit "Error reading the changed files index %s.\n%!" index
|
|
| None -> changed_files)
|
|
| Some lines ->
|
|
IList.fold_left
|
|
(fun changed_files line ->
|
|
let file = replace_header_file_with_source_file (DB.abs_source_file_from_path line) in
|
|
DB.SourceFileSet.add file changed_files)
|
|
changed_files lines
|
|
|
|
(** The buck targets are assumed to start with //, aliases are not supported. *)
|
|
let check_args_for_targets args =
|
|
if not (IList.exists (Utils.string_is_prefix "//") args) then
|
|
let args_s = String.concat " " args in
|
|
Process.print_error_and_exit
|
|
"Error reading buck command %s. Please, pass buck targets, aliases are not allowed.\n%!"
|
|
args_s
|
|
|
|
let add_flavor_to_targets args =
|
|
let flavor =
|
|
match Config.use_compilation_database with
|
|
| Some `Deps -> "#uber-compilation-database"
|
|
| Some `NoDeps -> "#compilation-database"
|
|
| _ -> assert false (* cannot happen *) in
|
|
let process_arg arg =
|
|
(* Targets are assumed to start with //, aliases are not allowed *)
|
|
if Utils.string_is_prefix "//" arg then arg ^ flavor
|
|
else arg in
|
|
IList.map process_arg args
|
|
|
|
let create_files_stack compilation_database =
|
|
let stack = Stack.create () in
|
|
let add_to_stack file _ =
|
|
Stack.push file stack in
|
|
CompilationDatabase.iter compilation_database add_to_stack;
|
|
stack
|
|
|
|
let swap_command cmd =
|
|
let plusplus = "++" in
|
|
let clang = "clang" in
|
|
let clangplusplus = "clang++" in
|
|
if Utils.string_is_suffix plusplus cmd then
|
|
Config.wrappers_dir // clangplusplus
|
|
else
|
|
Config.wrappers_dir // clang
|
|
|
|
let run_compilation_file compilation_database file =
|
|
try
|
|
let compilation_data = CompilationDatabase.find compilation_database file in
|
|
let wrapper_cmd = swap_command compilation_data.command in
|
|
let arg_file =
|
|
ClangQuotes.mk_arg_file
|
|
"cdb_clang_args_" ClangQuotes.EscapedNoQuotes [compilation_data.args] in
|
|
let args = Array.of_list [wrapper_cmd; "@" ^ arg_file] in
|
|
let env =
|
|
let env0 = Unix.environment () in
|
|
let found = ref false in
|
|
Array.iteri (fun i key_val ->
|
|
match string_split_character key_val '=' with
|
|
| Some var, args when string_equal var CLOpt.args_env_var ->
|
|
found := true ;
|
|
env0.(i) <-
|
|
F.sprintf "%s=%s%c--fcp-syntax-only" CLOpt.args_env_var args CLOpt.env_var_sep
|
|
| _ ->
|
|
()
|
|
) env0 ;
|
|
if !found then
|
|
env0
|
|
else
|
|
Array.append env0 [|CLOpt.args_env_var ^ "=--fcp-syntax-only"|] in
|
|
(Some compilation_data.dir, wrapper_cmd, args, env)
|
|
with Not_found ->
|
|
Process.print_error_and_exit "Failed to find compilation data for %s \n%!" file
|
|
|
|
let run_compilation_database compilation_database =
|
|
let number_of_files = CompilationDatabase.get_size compilation_database in
|
|
Logging.out "Starting %s %d files \n%!" capture_text number_of_files;
|
|
Logging.stdout "Starting %s %d files \n%!" capture_text number_of_files;
|
|
let jobs_stack = create_files_stack compilation_database in
|
|
let capture_text_upper = String.capitalize capture_text in
|
|
let job_to_string = fun file -> capture_text_upper ^ " " ^ file in
|
|
Process.run_jobs_in_parallel jobs_stack (run_compilation_file compilation_database) job_to_string
|
|
|
|
let should_add_file_to_cdb changed_files file_path =
|
|
match Config.changed_files_index with
|
|
| Some _ -> DB.SourceFileSet.mem (DB.source_file_from_string file_path) changed_files
|
|
| None -> true
|
|
|
|
(** Computes the compilation database files. *)
|
|
let get_compilation_database_files_buck () =
|
|
let cmd = IList.rev_append Config.rest (IList.rev Config.buck_build_args) in
|
|
match cmd with
|
|
| buck :: build :: args ->
|
|
(check_args_for_targets args;
|
|
let args_with_flavor = add_flavor_to_targets args in
|
|
let buck_build = Array.of_list
|
|
(buck :: build :: "--config" :: "*//cxx.pch_enabled=false" :: args_with_flavor) in
|
|
Process.create_process_and_wait buck_build;
|
|
let buck_targets_list = buck :: "targets" :: "--show-output" :: args_with_flavor in
|
|
let buck_targets = String.concat " " buck_targets_list in
|
|
try
|
|
match fst @@ Utils.with_process_in buck_targets Std.input_list with
|
|
| [] -> Logging.stdout "There are no files to process, exiting."; exit 0
|
|
| lines ->
|
|
Logging.out "Reading compilation database from:@\n%s@\n" (String.concat "\n" lines);
|
|
let scan_output compilation_database_files chan =
|
|
Scanf.sscanf chan "%s %s"
|
|
(fun target file -> StringMap.add target file compilation_database_files) in
|
|
(* Map from targets to json output *)
|
|
let compilation_database_files = IList.fold_left scan_output StringMap.empty lines in
|
|
IList.map (snd) (StringMap.bindings compilation_database_files)
|
|
with Unix.Unix_error (err, _, _) ->
|
|
Process.print_error_and_exit
|
|
"Cannot execute %s\n%!"
|
|
(buck_targets ^ " " ^ (Unix.error_message err)))
|
|
| _ ->
|
|
let cmd = String.concat " " cmd in
|
|
Process.print_error_and_exit "Incorrect buck command: %s. Please use buck build <targets>" cmd
|
|
|
|
(** Compute the compilation database files. *)
|
|
let get_compilation_database_files_xcodebuild () =
|
|
let cmd_and_args = IList.rev Config.rest in
|
|
let temp_dir = Config.results_dir // "clang" in
|
|
create_dir temp_dir;
|
|
let tmp_file = Filename.temp_file ~temp_dir:temp_dir "cdb" ".json" in
|
|
let xcodebuild_cmd, xcodebuild_args =
|
|
match cmd_and_args with
|
|
| [] -> failwith("Build command cannot be empty")
|
|
| cmd :: _ -> cmd, cmd_and_args in
|
|
let xcpretty_cmd = "xcpretty" in
|
|
let xcpretty_cmd_args =
|
|
[xcpretty_cmd; "--report"; "json-compilation-database"; "--output"; tmp_file] in
|
|
let producer_status, consumer_status =
|
|
Process.pipeline ~producer_prog:xcodebuild_cmd ~producer_args:xcodebuild_args
|
|
~consumer_prog:xcpretty_cmd ~consumer_args:xcpretty_cmd_args in
|
|
match producer_status, consumer_status with
|
|
| Ok (), Ok () -> [tmp_file]
|
|
| _ ->
|
|
Logging.stderr "There was an error executing the build command";
|
|
exit 1
|
|
|
|
|
|
let capture_files_in_database db_json_files =
|
|
let changed_files = read_files_to_compile () in
|
|
let compilation_database = CompilationDatabase.empty () in
|
|
IList.iter
|
|
(CompilationDatabase.decode_json_file
|
|
compilation_database (should_add_file_to_cdb changed_files)) db_json_files;
|
|
run_compilation_database compilation_database
|