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.
622 lines
24 KiB
622 lines
24 KiB
(*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*)
|
|
open! IStd
|
|
open PolyVariantEqual
|
|
|
|
(** entry points for top-level functionalities such as capture, analysis, and reporting *)
|
|
|
|
module CLOpt = CommandLineOption
|
|
module L = Logging
|
|
module F = Format
|
|
|
|
(* based on the build_system and options passed to infer, we run in different driver modes *)
|
|
type mode =
|
|
| Analyze
|
|
| Ant of {prog: string; args: string list}
|
|
| BuckClangFlavor of {build_cmd: string list}
|
|
| BuckCompilationDB of BuckMode.clang_compilation_db_deps * string * string list
|
|
| BuckGenrule of string
|
|
| BuckGenruleMaster of string list
|
|
| Clang of Clang.compiler * string * string list
|
|
| ClangCompilationDB of [`Escaped of string | `Raw of string] list
|
|
| Javac of Javac.compiler * string * string list
|
|
| Maven of string * string list
|
|
| NdkBuild of {build_cmd: string list}
|
|
| PythonCapture of Config.build_system * string list
|
|
| XcodeBuild of {prog: string; args: string list}
|
|
| XcodeXcpretty of string * string list
|
|
|
|
let is_analyze_mode = function Analyze -> true | _ -> false
|
|
|
|
let pp_mode fmt = function
|
|
| Analyze ->
|
|
F.fprintf fmt "Analyze driver mode"
|
|
| Ant {prog; args} ->
|
|
F.fprintf fmt "Ant driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
|
|
| BuckClangFlavor {build_cmd} ->
|
|
F.fprintf fmt "BuckClangFlavor driver mode: build_cmd = %a" Pp.cli_args build_cmd
|
|
| BuckGenrule prog ->
|
|
F.fprintf fmt "BuckGenRule driver mode:@\nprog = '%s'" prog
|
|
| BuckGenruleMaster build_cmd ->
|
|
F.fprintf fmt "BuckGenrule driver mode:@\nbuild command = %a" Pp.cli_args build_cmd
|
|
| BuckCompilationDB (deps, prog, args) ->
|
|
F.fprintf fmt "BuckCompilationDB driver mode:@\nprog = '%s'@\nargs = %a@\ndeps = %a" prog
|
|
Pp.cli_args args BuckMode.pp_clang_compilation_db_deps deps
|
|
| ClangCompilationDB _ ->
|
|
F.fprintf fmt "ClangCompilationDB driver mode"
|
|
| PythonCapture (bs, args) ->
|
|
F.fprintf fmt "PythonCapture driver mode:@\nbuild system = '%s'@\nargs = %a"
|
|
(Config.string_of_build_system bs)
|
|
Pp.cli_args args
|
|
| XcodeBuild {prog; args} ->
|
|
F.fprintf fmt "XcodeBuild driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
|
|
| XcodeXcpretty (prog, args) ->
|
|
F.fprintf fmt "XcodeXcpretty driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
|
|
| Javac (_, prog, args) ->
|
|
F.fprintf fmt "Javac driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
|
|
| Maven (prog, args) ->
|
|
F.fprintf fmt "Maven driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
|
|
| NdkBuild {build_cmd} ->
|
|
F.fprintf fmt "NdkBuild driver mode: build_cmd = %a" Pp.cli_args build_cmd
|
|
| Clang (_, prog, args) ->
|
|
F.fprintf fmt "Clang driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args
|
|
|
|
|
|
(* A clean command for each driver mode to be suggested to the user
|
|
in case nothing got captured. *)
|
|
let clean_compilation_command mode =
|
|
match mode with
|
|
| BuckCompilationDB (_, prog, _) | Clang (_, prog, _) ->
|
|
Some (prog ^ " clean")
|
|
| XcodeXcpretty (prog, args) ->
|
|
Some (String.concat ~sep:" " (List.append (prog :: args) ["clean"]))
|
|
| _ ->
|
|
None
|
|
|
|
|
|
(** Clean up the results dir to select only what's relevant to go in the Buck cache. In particular,
|
|
get rid of non-deterministic outputs.*)
|
|
let clean_results_dir () =
|
|
let cache_capture =
|
|
Config.genrule_mode || Option.exists Config.buck_mode ~f:BuckMode.is_clang_flavors
|
|
in
|
|
if cache_capture then DBWriter.canonicalize () ;
|
|
(* make sure we are done with the database *)
|
|
ResultsDatabase.db_close () ;
|
|
(* In Buck flavors mode we keep all capture data, but in Java mode we keep only the tenv *)
|
|
let should_delete_dir =
|
|
let dirs_to_delete = ResultsDir.dirs_to_clean ~cache_capture in
|
|
List.mem ~equal:String.equal dirs_to_delete
|
|
in
|
|
let should_delete_file =
|
|
let files_to_delete =
|
|
(* we do not need to keep the database in Buck/Java mode *)
|
|
(if cache_capture then [] else [ResultsDatabase.database_filename])
|
|
@ [ Config.log_file
|
|
; (* some versions of sqlite do not clean up after themselves *)
|
|
ResultsDatabase.database_filename ^ "-shm"
|
|
; ResultsDatabase.database_filename ^ "-wal" ]
|
|
in
|
|
let suffixes_to_delete = [".txt"; ".json"] in
|
|
fun name ->
|
|
(* Keep the JSON report and the JSON costs report *)
|
|
(not
|
|
(List.exists
|
|
~f:(String.equal (Filename.basename name))
|
|
[ Config.report_json
|
|
; Config.costs_report_json
|
|
; Config.test_determinator_output
|
|
; Config.export_changed_functions_output ]))
|
|
&& ( List.mem ~equal:String.equal files_to_delete (Filename.basename name)
|
|
|| List.exists ~f:(Filename.check_suffix name) suffixes_to_delete )
|
|
in
|
|
let rec delete_temp_results name =
|
|
let rec cleandir dir =
|
|
match Unix.readdir_opt dir with
|
|
| Some entry ->
|
|
if should_delete_dir entry then Utils.rmtree (name ^/ entry)
|
|
else if
|
|
not
|
|
( String.equal entry Filename.current_dir_name
|
|
|| String.equal entry Filename.parent_dir_name )
|
|
then delete_temp_results (name ^/ entry) ;
|
|
cleandir dir (* next entry *)
|
|
| None ->
|
|
Unix.closedir dir
|
|
in
|
|
match Unix.opendir name with
|
|
| dir ->
|
|
cleandir dir
|
|
| exception Unix.Unix_error (Unix.ENOTDIR, _, _) ->
|
|
if should_delete_file name then Unix.unlink name ;
|
|
()
|
|
| exception Unix.Unix_error (Unix.ENOENT, _, _) ->
|
|
()
|
|
in
|
|
delete_temp_results Config.results_dir
|
|
|
|
|
|
let reset_duplicates_file () =
|
|
let start = Config.results_dir ^/ Config.duplicates_filename in
|
|
let delete () = Unix.unlink start in
|
|
let create () =
|
|
Unix.close (Unix.openfile ~perm:0o0666 ~mode:[Unix.O_CREAT; Unix.O_WRONLY] start)
|
|
in
|
|
if Sys.file_exists start = `Yes then delete () ;
|
|
create ()
|
|
|
|
|
|
let command_error_handling ~always_die ~prog ~args = function
|
|
| Ok _ ->
|
|
()
|
|
| Error _ as status ->
|
|
let log =
|
|
if (not always_die) && Config.keep_going then
|
|
(* Log error and proceed past the failure when keep going mode is on *)
|
|
L.external_error
|
|
else L.die InternalError
|
|
in
|
|
log "%a:@\n %s" Pp.cli_args (prog :: args) (Unix.Exit_or_signal.to_string_hum status)
|
|
|
|
|
|
let run_command ~prog ~args ?(cleanup = command_error_handling ~always_die:false ~prog ~args) () =
|
|
Unix.waitpid (Unix.fork_exec ~prog ~argv:(prog :: args) ())
|
|
|> fun status ->
|
|
cleanup status ;
|
|
ok_exn (Unix.Exit_or_signal.or_error status)
|
|
|
|
|
|
let check_xcpretty () =
|
|
match Unix.system "xcpretty --version" with
|
|
| Ok () ->
|
|
()
|
|
| Error _ ->
|
|
L.user_error
|
|
"@\n\
|
|
xcpretty not found in the path. Please consider installing xcpretty for a more robust \
|
|
integration with xcodebuild. Otherwise use the option --no-xcpretty.@\n\
|
|
@."
|
|
|
|
|
|
let capture_with_compilation_database db_files =
|
|
let root = Config.project_root in
|
|
Config.clang_compilation_dbs :=
|
|
List.map db_files ~f:(function
|
|
| `Escaped fname ->
|
|
`Escaped (Utils.filename_to_absolute ~root fname)
|
|
| `Raw fname ->
|
|
`Raw (Utils.filename_to_absolute ~root fname) ) ;
|
|
let compilation_database = CompilationDatabase.from_json_files !Config.clang_compilation_dbs in
|
|
CaptureCompilationDatabase.capture_files_in_database compilation_database
|
|
|
|
|
|
let buck_capture build_cmd =
|
|
let prog_build_cmd_opt =
|
|
let prog, buck_args = (List.hd_exn build_cmd, List.tl_exn build_cmd) in
|
|
match Config.buck_mode with
|
|
| Some ClangFlavors ->
|
|
(* let children infer processes know that they are inside Buck *)
|
|
let infer_args_with_buck =
|
|
String.concat
|
|
~sep:(String.of_char CLOpt.env_var_sep)
|
|
(Option.to_list (Sys.getenv CLOpt.args_env_var) @ ["--buck"])
|
|
in
|
|
Unix.putenv ~key:CLOpt.args_env_var ~data:infer_args_with_buck ;
|
|
let {Buck.command; rev_not_targets; targets} =
|
|
Buck.add_flavors_to_buck_arguments ClangFlavors ~filter_kind:`Auto ~extra_flavors:[]
|
|
buck_args
|
|
in
|
|
if List.is_empty targets then None
|
|
else
|
|
let all_args = List.rev_append rev_not_targets targets in
|
|
let updated_buck_cmd =
|
|
command
|
|
:: List.rev_append Config.buck_build_args_no_inline (Buck.store_args_in_file all_args)
|
|
in
|
|
Logging.(debug Capture Quiet)
|
|
"Processed buck command '%a'@\n" (Pp.seq F.pp_print_string) updated_buck_cmd ;
|
|
Some (prog, updated_buck_cmd)
|
|
| _ ->
|
|
Some (prog, build_cmd)
|
|
in
|
|
Option.iter prog_build_cmd_opt ~f:(fun (prog, buck_build_cmd) ->
|
|
L.progress "Capturing in buck mode...@." ;
|
|
if Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode then (
|
|
RunState.set_merge_capture true ; RunState.store () ) ;
|
|
Buck.clang_flavor_capture ~prog ~buck_build_cmd )
|
|
|
|
|
|
let python_capture build_system build_cmd =
|
|
L.progress "Capturing in %s mode...@." (Config.string_of_build_system build_system) ;
|
|
let infer_py = Config.lib_dir ^/ "python" ^/ "infer.py" in
|
|
let args =
|
|
List.rev_append Config.anon_args
|
|
( (if not Config.continue_capture then [] else ["--continue"])
|
|
@ ( match Config.force_integration with
|
|
| None ->
|
|
[]
|
|
| Some tool ->
|
|
["--force-integration"; Config.string_of_build_system tool] )
|
|
@ (match Config.java_jar_compiler with None -> [] | Some p -> ["--java-jar-compiler"; p])
|
|
@ (if not Config.debug_mode then [] else ["--debug"])
|
|
@ (if Config.filtering then [] else ["--no-filtering"])
|
|
@ "-j" :: string_of_int Config.jobs
|
|
:: (match Config.load_average with None -> [] | Some l -> ["-l"; string_of_float l])
|
|
@ (if not Config.pmd_xml then [] else ["--pmd-xml"])
|
|
@ ["--project-root"; Config.project_root]
|
|
@ (if not Config.quiet then [] else ["--quiet"])
|
|
@ "--out" :: Config.results_dir
|
|
::
|
|
(match Config.xcode_developer_dir with None -> [] | Some d -> ["--xcode-developer-dir"; d])
|
|
@ (if not Config.buck_merge_all_deps then [] else ["--buck-merge-all-deps"])
|
|
@ ("--" :: build_cmd) )
|
|
in
|
|
run_command ~prog:infer_py ~args
|
|
~cleanup:(function
|
|
| Error (`Exit_non_zero exit_code)
|
|
when Int.equal exit_code Config.infer_py_argparse_error_exit_code ->
|
|
(* swallow infer.py argument parsing error *)
|
|
Config.print_usage_exit ()
|
|
| status ->
|
|
command_error_handling ~always_die:true ~prog:infer_py ~args status )
|
|
()
|
|
|
|
|
|
let capture ~changed_files = function
|
|
| Analyze ->
|
|
()
|
|
| Ant {prog; args} ->
|
|
L.progress "Capturing in ant mode...@." ;
|
|
Ant.capture ~prog ~args
|
|
| BuckClangFlavor {build_cmd} ->
|
|
buck_capture build_cmd
|
|
| BuckCompilationDB (deps, prog, args) ->
|
|
L.progress "Capturing using Buck's compilation database...@." ;
|
|
let json_cdb =
|
|
CaptureCompilationDatabase.get_compilation_database_files_buck deps ~prog ~args
|
|
in
|
|
capture_with_compilation_database ~changed_files json_cdb
|
|
| BuckGenrule path ->
|
|
L.progress "Capturing for Buck genrule compatibility...@." ;
|
|
JMain.from_arguments path
|
|
| BuckGenruleMaster build_cmd ->
|
|
L.progress "Capturing for BuckGenruleMaster integration...@." ;
|
|
BuckGenrule.capture build_cmd
|
|
| Clang (compiler, prog, args) ->
|
|
if CLOpt.is_originator then L.progress "Capturing in make/cc mode...@." ;
|
|
Clang.capture compiler ~prog ~args
|
|
| ClangCompilationDB db_files ->
|
|
L.progress "Capturing using compilation database...@." ;
|
|
capture_with_compilation_database ~changed_files db_files
|
|
| Javac (compiler, prog, args) ->
|
|
if CLOpt.is_originator then L.progress "Capturing in javac mode...@." ;
|
|
Javac.capture compiler ~prog ~args
|
|
| Maven (prog, args) ->
|
|
L.progress "Capturing in maven mode...@." ;
|
|
Maven.capture ~prog ~args
|
|
| NdkBuild {build_cmd} ->
|
|
L.progress "Capturing in ndk-build mode...@." ;
|
|
NdkBuild.capture ~build_cmd
|
|
| PythonCapture (build_system, build_cmd) ->
|
|
python_capture build_system build_cmd
|
|
| XcodeBuild {prog; args} ->
|
|
L.progress "Capturing in xcodebuild mode...@." ;
|
|
XcodeBuild.capture ~prog ~args
|
|
| XcodeXcpretty (prog, args) ->
|
|
L.progress "Capturing using xcodebuild and xcpretty...@." ;
|
|
check_xcpretty () ;
|
|
let json_cdb =
|
|
CaptureCompilationDatabase.get_compilation_database_files_xcodebuild ~prog ~args
|
|
in
|
|
capture_with_compilation_database ~changed_files json_cdb
|
|
|
|
|
|
(* shadowed for tracing *)
|
|
let capture ~changed_files mode =
|
|
PerfEvent.(log (fun logger -> log_begin_event logger ~name:"capture" ())) ;
|
|
capture ~changed_files mode ;
|
|
PerfEvent.(log (fun logger -> log_end_event logger ()))
|
|
|
|
|
|
let capture ~changed_files mode =
|
|
ScubaLogging.execute_with_time_logging "capture" (fun () -> capture ~changed_files mode)
|
|
|
|
|
|
let execute_analyze ~changed_files =
|
|
PerfEvent.(log (fun logger -> log_begin_event logger ~name:"analyze" ())) ;
|
|
InferAnalyze.main ~changed_files ;
|
|
PerfEvent.(log (fun logger -> log_end_event logger ()))
|
|
|
|
|
|
let report ?(suppress_console = false) () =
|
|
let issues_json = Config.(results_dir ^/ report_json) in
|
|
JsonReports.write_reports ~issues_json ~costs_json:Config.(results_dir ^/ costs_report_json) ;
|
|
if Config.(test_determinator && process_clang_ast) then
|
|
TestDeterminator.merge_test_determinator_results () ;
|
|
(* Post-process the report according to the user config. By default, calls report.py to create a
|
|
human-readable report.
|
|
|
|
Do not bother calling the report hook when called from within Buck. *)
|
|
match (Config.buck_cache_mode, Config.report_hook) with
|
|
| true, _ | false, None ->
|
|
()
|
|
| false, Some prog ->
|
|
let if_true key opt args = if not opt then args else key :: args in
|
|
let bugs_txt = Config.results_dir ^/ "bugs.txt" in
|
|
let args =
|
|
if_true "--pmd-xml" Config.pmd_xml
|
|
@@ if_true "--quiet"
|
|
(Config.quiet || suppress_console)
|
|
[ "--issues-json"
|
|
; issues_json
|
|
; "--issues-txt"
|
|
; bugs_txt
|
|
; "--project-root"
|
|
; Config.project_root
|
|
; "--results-dir"
|
|
; Config.results_dir ]
|
|
in
|
|
if is_error (Unix.waitpid (Unix.fork_exec ~prog ~argv:(prog :: args) ())) then
|
|
L.external_error
|
|
"** Error running the reporting script:@\n** %s %s@\n** See error above@." prog
|
|
(String.concat ~sep:" " args)
|
|
|
|
|
|
(* shadowed for tracing *)
|
|
let report ?suppress_console () =
|
|
PerfEvent.(log (fun logger -> log_begin_event logger ~name:"report" ())) ;
|
|
report ?suppress_console () ;
|
|
PerfEvent.(log (fun logger -> log_end_event logger ()))
|
|
|
|
|
|
let error_nothing_to_analyze mode =
|
|
let clean_command_opt = clean_compilation_command mode in
|
|
let nothing_to_compile_msg = "Nothing to compile." in
|
|
let please_run_capture_msg =
|
|
match mode with Analyze -> " Have you run `infer capture`?" | _ -> ""
|
|
in
|
|
( match clean_command_opt with
|
|
| Some clean_command ->
|
|
L.user_warning "%s%s Try running `%s` first.@." nothing_to_compile_msg please_run_capture_msg
|
|
clean_command
|
|
| None ->
|
|
L.user_warning "%s%s Try cleaning the build first.@." nothing_to_compile_msg
|
|
please_run_capture_msg ) ;
|
|
L.progress "There was nothing to analyze.@."
|
|
|
|
|
|
let analyze_and_report ?suppress_console_report ~changed_files mode =
|
|
let should_analyze, should_report =
|
|
match (Config.command, mode) with
|
|
| _, BuckClangFlavor _ when not (Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode) ->
|
|
(* In Buck mode when compilation db is not used, analysis is invoked from capture if buck flavors are not used *)
|
|
(false, false)
|
|
| _ when Config.infer_is_clang || Config.infer_is_javac ->
|
|
(* Called from another integration to do capture only. *)
|
|
(false, false)
|
|
| (Capture | Compile | Explore | Report | ReportDiff), _ ->
|
|
(false, false)
|
|
| (Analyze | Run), _ ->
|
|
(true, true)
|
|
in
|
|
let should_analyze = should_analyze && Config.capture in
|
|
let should_merge =
|
|
match mode with
|
|
| _ when Config.merge ->
|
|
(* [--merge] overrides other behaviors *)
|
|
true
|
|
| BuckClangFlavor _
|
|
when Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode
|
|
&& InferCommand.equal Run Config.command ->
|
|
(* if doing capture + analysis of buck with flavors, we always need to merge targets before the analysis phase *)
|
|
true
|
|
| Analyze | BuckGenruleMaster _ ->
|
|
RunState.get_merge_capture ()
|
|
| _ ->
|
|
false
|
|
in
|
|
if should_merge then (
|
|
if Config.export_changed_functions then MergeCapture.merge_changed_functions () ;
|
|
MergeCapture.merge_captured_targets () ;
|
|
RunState.set_merge_capture false ;
|
|
RunState.store () ) ;
|
|
if should_analyze then
|
|
if SourceFiles.is_empty () && Config.capture then error_nothing_to_analyze mode
|
|
else (
|
|
execute_analyze ~changed_files ;
|
|
if Config.starvation_whole_program then Starvation.whole_program_analysis () ) ;
|
|
if should_report && Config.report then report ?suppress_console:suppress_console_report ()
|
|
|
|
|
|
let analyze_and_report ?suppress_console_report ~changed_files mode =
|
|
ScubaLogging.execute_with_time_logging "analyze_and_report" (fun () ->
|
|
analyze_and_report ?suppress_console_report ~changed_files mode )
|
|
|
|
|
|
(** as the Config.fail_on_bug flag mandates, exit with error when an issue is reported *)
|
|
let fail_on_issue_epilogue () =
|
|
let issues_json =
|
|
DB.Results_dir.(path_to_filename Abs_root [Config.report_json]) |> DB.filename_to_string
|
|
in
|
|
match Utils.read_file issues_json with
|
|
| Ok lines ->
|
|
let issues = Jsonbug_j.report_of_string @@ String.concat ~sep:"" lines in
|
|
if not (List.is_empty issues) then L.exit Config.fail_on_issue_exit_code
|
|
| Error error ->
|
|
L.internal_error "Failed to read report file '%s': %s@." issues_json error ;
|
|
()
|
|
|
|
|
|
let assert_supported_mode required_analyzer requested_mode_string =
|
|
let analyzer_enabled =
|
|
match required_analyzer with
|
|
| `Clang ->
|
|
Version.clang_enabled
|
|
| `Java ->
|
|
Version.java_enabled
|
|
| `Xcode ->
|
|
Version.clang_enabled && Version.xcode_enabled
|
|
in
|
|
if not analyzer_enabled then
|
|
let analyzer_string =
|
|
match required_analyzer with
|
|
| `Clang ->
|
|
"clang"
|
|
| `Java ->
|
|
"java"
|
|
| `Xcode ->
|
|
"clang and xcode"
|
|
in
|
|
L.(die UserError)
|
|
"Unsupported build mode: %s@\n\
|
|
Infer was built with %s analyzers disabled.@ Please rebuild infer with %s enabled.@."
|
|
requested_mode_string analyzer_string analyzer_string
|
|
|
|
|
|
let error_no_buck_mode_specified () =
|
|
L.die UserError
|
|
"`buck` command detected on the command line but no Buck integration has been selected. Please \
|
|
specify `--buck-clang`, `--buck-java`, or `--buck-compilation-database`. See `infer capture \
|
|
--help` for more information."
|
|
|
|
|
|
let assert_supported_build_system build_system =
|
|
match (build_system : Config.build_system) with
|
|
| BAnt | BGradle | BJava | BJavac | BMvn ->
|
|
Config.string_of_build_system build_system |> assert_supported_mode `Java
|
|
| BClang | BMake | BNdk ->
|
|
Config.string_of_build_system build_system |> assert_supported_mode `Clang
|
|
| BXcode ->
|
|
Config.string_of_build_system build_system |> assert_supported_mode `Xcode
|
|
| BBuck ->
|
|
let analyzer, build_string =
|
|
match Config.buck_mode with
|
|
| None ->
|
|
error_no_buck_mode_specified ()
|
|
| Some ClangFlavors ->
|
|
(`Clang, "buck with flavors")
|
|
| Some (ClangCompilationDB _) ->
|
|
(`Clang, "buck compilation database")
|
|
| Some JavaGenruleMaster ->
|
|
(`Java, Config.string_of_build_system build_system)
|
|
in
|
|
assert_supported_mode analyzer build_string
|
|
|
|
|
|
let mode_of_build_command build_cmd (buck_mode : BuckMode.t option) =
|
|
match build_cmd with
|
|
| [] ->
|
|
if not (List.is_empty !Config.clang_compilation_dbs) then (
|
|
assert_supported_mode `Clang "clang compilation database" ;
|
|
ClangCompilationDB !Config.clang_compilation_dbs )
|
|
else Analyze
|
|
| prog :: args -> (
|
|
let build_system =
|
|
match Config.force_integration with
|
|
| Some build_system when CLOpt.is_originator ->
|
|
build_system
|
|
| _ ->
|
|
Config.build_system_of_exe_name (Filename.basename prog)
|
|
in
|
|
assert_supported_build_system build_system ;
|
|
match ((build_system : Config.build_system), buck_mode) with
|
|
| BBuck, None ->
|
|
error_no_buck_mode_specified ()
|
|
| BBuck, Some (ClangCompilationDB deps) ->
|
|
BuckCompilationDB (deps, prog, List.append args (List.rev Config.buck_build_args))
|
|
| BBuck, Some ClangFlavors when Config.is_checker_enabled Linters ->
|
|
L.user_warning
|
|
"WARNING: the linters require --buck-compilation-database to be set.@ Alternatively, \
|
|
set --no-linters to disable them and this warning.@." ;
|
|
BuckClangFlavor {build_cmd}
|
|
| BBuck, Some JavaGenruleMaster ->
|
|
BuckGenruleMaster build_cmd
|
|
| BClang, _ ->
|
|
Clang (Clang.Clang, prog, args)
|
|
| BMake, _ ->
|
|
Clang (Clang.Make, prog, args)
|
|
| BJava, _ ->
|
|
Javac (Javac.Java, prog, args)
|
|
| BJavac, _ ->
|
|
Javac (Javac.Javac, prog, args)
|
|
| BMvn, _ ->
|
|
Maven (prog, args)
|
|
| BXcode, _ when Config.xcpretty ->
|
|
XcodeXcpretty (prog, args)
|
|
| BXcode, _ ->
|
|
XcodeBuild {prog; args}
|
|
| BBuck, Some ClangFlavors ->
|
|
BuckClangFlavor {build_cmd}
|
|
| BNdk, _ ->
|
|
NdkBuild {build_cmd}
|
|
| BAnt, _ ->
|
|
Ant {prog; args}
|
|
| (BGradle as build_system), _ ->
|
|
PythonCapture (build_system, build_cmd) )
|
|
|
|
|
|
let mode_from_command_line =
|
|
lazy
|
|
( match Config.generated_classes with
|
|
| _ when Config.infer_is_clang ->
|
|
let prog, args =
|
|
match Array.to_list (Sys.get_argv ()) with
|
|
| prog :: args ->
|
|
(prog, args)
|
|
| [] ->
|
|
assert false
|
|
(* Sys.argv is never empty *)
|
|
in
|
|
Clang (Clang.Clang, prog, args)
|
|
| _ when Config.infer_is_javac ->
|
|
let build_args =
|
|
match Array.to_list (Sys.get_argv ()) with _ :: args -> args | [] -> []
|
|
in
|
|
Javac (Javac.Javac, "javac", build_args)
|
|
| Some path ->
|
|
assert_supported_mode `Java "Buck genrule" ;
|
|
BuckGenrule path
|
|
| None ->
|
|
mode_of_build_command (List.rev Config.rest) Config.buck_mode )
|
|
|
|
|
|
let run_prologue mode =
|
|
if CLOpt.is_originator then L.environment_info "%a@\n" Config.pp_version () ;
|
|
if Config.debug_mode then L.environment_info "Driver mode:@\n%a@." pp_mode mode ;
|
|
if CLOpt.is_originator then (
|
|
if Config.dump_duplicate_symbols then reset_duplicates_file () ;
|
|
(* disable the Buck daemon as changes in the Buck or infer config may be missed otherwise *)
|
|
Unix.putenv ~key:"NO_BUCKD" ~data:"1" ) ;
|
|
()
|
|
|
|
|
|
let run_prologue mode =
|
|
ScubaLogging.execute_with_time_logging "run_prologue" (fun () -> run_prologue mode)
|
|
|
|
|
|
let run_epilogue () =
|
|
if CLOpt.is_originator then (
|
|
if Config.fail_on_bug then fail_on_issue_epilogue () ;
|
|
() ) ;
|
|
if Config.buck_cache_mode then clean_results_dir () ;
|
|
()
|
|
|
|
|
|
let run_epilogue () = ScubaLogging.execute_with_time_logging "run_epilogue" run_epilogue
|
|
|
|
let read_config_changed_files () =
|
|
match Config.changed_files_index with
|
|
| None ->
|
|
None
|
|
| Some index -> (
|
|
match Utils.read_file index with
|
|
| Ok lines ->
|
|
Some (SourceFile.changed_sources_from_changed_files lines)
|
|
| Error error ->
|
|
L.external_error "Error reading the changed files index '%s': %s@." index error ;
|
|
None )
|