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.
155 lines
6.0 KiB
155 lines
6.0 KiB
(*
|
|
* Copyright (c) 2016-present, Facebook, Inc.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
|
|
open! IStd
|
|
module F = Format
|
|
module L = Logging
|
|
|
|
let create_cmd (source_file, (compilation_data: CompilationDatabase.compilation_data)) =
|
|
let swap_executable cmd =
|
|
if String.is_suffix ~suffix:"++" cmd then Config.wrappers_dir ^/ "clang++"
|
|
else Config.wrappers_dir ^/ "clang"
|
|
in
|
|
let arg_file =
|
|
ClangQuotes.mk_arg_file "cdb_clang_args" ClangQuotes.EscapedNoQuotes
|
|
compilation_data.escaped_arguments
|
|
in
|
|
( source_file
|
|
, { CompilationDatabase.directory= compilation_data.directory
|
|
; executable= swap_executable compilation_data.executable
|
|
; escaped_arguments= ["@" ^ arg_file; "-fsyntax-only"] } )
|
|
|
|
|
|
let invoke_cmd (source_file, (cmd: CompilationDatabase.compilation_data)) =
|
|
let argv = cmd.executable :: cmd.escaped_arguments in
|
|
( match Spawn.spawn ~cwd:(Path cmd.directory) ~prog:cmd.executable ~argv () with
|
|
| pid ->
|
|
!ProcessPoolState.update_status (Mtime_clock.now ()) (SourceFile.to_string source_file) ;
|
|
Unix.waitpid (Pid.of_int pid)
|
|
|> Result.map_error ~f:(fun unix_error ->
|
|
Unix.Exit_or_signal.to_string_hum (Error unix_error) )
|
|
| exception Unix.Unix_error (err, f, arg) ->
|
|
Error (F.asprintf "%s(%s): %s@." f arg (Unix.Error.message err)) )
|
|
|> function
|
|
| Ok () ->
|
|
()
|
|
| Error error ->
|
|
let log_or_die fmt =
|
|
if Config.linters_ignore_clang_failures || Config.keep_going then
|
|
L.debug Capture Quiet fmt
|
|
else L.die ExternalError fmt
|
|
in
|
|
log_or_die "Error running compilation for '%a': %a:@\n%s@." SourceFile.pp source_file
|
|
Pp.cli_args argv error
|
|
|
|
|
|
let run_compilation_database compilation_database should_capture_file =
|
|
let compilation_data =
|
|
CompilationDatabase.filter_compilation_data compilation_database ~f:should_capture_file
|
|
in
|
|
let number_of_jobs = List.length compilation_data in
|
|
L.(debug Capture Quiet)
|
|
"Starting %s %d files@\n%!" Config.clang_frontend_action_string number_of_jobs ;
|
|
L.progress "Starting %s %d files@\n%!" Config.clang_frontend_action_string number_of_jobs ;
|
|
let compilation_commands = List.map ~f:create_cmd compilation_data in
|
|
let runner = Tasks.Runner.create ~jobs:Config.jobs ~f:invoke_cmd in
|
|
Tasks.Runner.run runner ~tasks:compilation_commands ;
|
|
L.progress "@." ;
|
|
L.(debug Analysis Medium) "Ran %d jobs" number_of_jobs
|
|
|
|
|
|
(** Computes the compilation database files. *)
|
|
let get_compilation_database_files_buck ~prog ~args =
|
|
let dep_depth =
|
|
match Config.buck_compilation_database with Some (Deps depth) -> Some depth | _ -> None
|
|
in
|
|
match
|
|
Buck.add_flavors_to_buck_arguments ~filter_kind:`Yes ~dep_depth
|
|
~extra_flavors:Config.append_buck_flavors args
|
|
with
|
|
| {command= "build" as command; rev_not_targets; targets} ->
|
|
let targets_args = Buck.store_args_in_file targets in
|
|
let build_args =
|
|
command
|
|
:: List.rev_append rev_not_targets
|
|
("--config" :: "*//cxx.pch_enabled=false" :: targets_args)
|
|
in
|
|
Logging.(debug Linters Quiet)
|
|
"Processed buck command is: 'buck %a'@\n" (Pp.seq F.pp_print_string) build_args ;
|
|
Process.create_process_and_wait ~prog ~args:build_args ;
|
|
let buck_targets_shell =
|
|
prog
|
|
:: "targets"
|
|
:: List.rev_append
|
|
(Buck.filter_compatible `Targets rev_not_targets)
|
|
("--show-output" :: targets_args)
|
|
in
|
|
let on_target_lines = function
|
|
| [] ->
|
|
L.(die ExternalError) "There are no files to process, exiting"
|
|
| lines ->
|
|
L.(debug Capture Quiet)
|
|
"Reading compilation database from:@\n%a@\n"
|
|
(Pp.seq ~sep:"\n" F.pp_print_string)
|
|
lines ;
|
|
(* this assumes that flavors do not contain spaces *)
|
|
let split_regex = Str.regexp "#[^ ]* " in
|
|
let scan_output compilation_database_files line =
|
|
match Str.bounded_split split_regex line 2 with
|
|
| [_; filename] ->
|
|
`Raw filename :: compilation_database_files
|
|
| _ ->
|
|
L.(die ExternalError)
|
|
"Failed to parse `buck targets --show-output ...` line of output:@\n%s" line
|
|
in
|
|
List.fold ~f:scan_output ~init:[] lines
|
|
in
|
|
Utils.with_process_lines
|
|
~debug:L.(debug Capture Quiet)
|
|
~cmd:buck_targets_shell ~tmp_prefix:"buck_targets_" ~f:on_target_lines
|
|
| _ ->
|
|
Process.print_error_and_exit "Incorrect buck command: %s %a. Please use buck build <targets>"
|
|
prog (Pp.seq F.pp_print_string) args
|
|
|
|
|
|
(** Compute the compilation database files. *)
|
|
let get_compilation_database_files_xcodebuild ~prog ~args =
|
|
let tmp_file = Filename.temp_file "cdb" ".json" in
|
|
let xcodebuild_prog, xcodebuild_args = (prog, prog :: args) in
|
|
let xcpretty_prog = "xcpretty" in
|
|
let xcpretty_args =
|
|
[xcpretty_prog; "--report"; "json-compilation-database"; "--output"; tmp_file]
|
|
in
|
|
L.(debug Capture Quiet)
|
|
"Running %s | %s@\n@."
|
|
(List.to_string ~f:Fn.id xcodebuild_args)
|
|
(List.to_string ~f:Fn.id xcpretty_args) ;
|
|
let producer_status, consumer_status =
|
|
Process.pipeline ~producer_prog:xcodebuild_prog ~producer_args:xcodebuild_args
|
|
~consumer_prog:xcpretty_prog ~consumer_args:xcpretty_args
|
|
in
|
|
match (producer_status, consumer_status) with
|
|
| Ok (), Ok () ->
|
|
[`Escaped tmp_file]
|
|
| _ ->
|
|
L.(die ExternalError) "There was an error executing the build command"
|
|
|
|
|
|
let capture_files_in_database ~changed_files compilation_database =
|
|
let filter_changed =
|
|
match changed_files with
|
|
| None ->
|
|
fun _ -> true
|
|
| Some changed_files_set ->
|
|
fun source_file -> SourceFile.Set.mem source_file changed_files_set
|
|
in
|
|
run_compilation_database compilation_database filter_changed
|
|
|
|
|
|
let capture_file_in_database compilation_database source_file =
|
|
run_compilation_database compilation_database (SourceFile.equal source_file)
|