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.
577 lines
22 KiB
577 lines
22 KiB
(*
|
|
* 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
|
|
open! PVariant
|
|
|
|
(** 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
|
|
| BuckGenrule of string
|
|
| BuckCompilationDB of string * 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
|
|
| Python of string list
|
|
| PythonCapture of Config.build_system * string list
|
|
| XcodeXcpretty of string * string list
|
|
[@@deriving compare]
|
|
|
|
let equal_mode = [%compare.equal : mode]
|
|
|
|
let pp_mode fmt mode =
|
|
match mode with
|
|
| Analyze
|
|
| BuckGenrule _
|
|
| BuckCompilationDB _
|
|
| ClangCompilationDB _
|
|
| Python _
|
|
| PythonCapture (_, _)
|
|
| XcodeXcpretty _ ->
|
|
(* these are pretty boring, do not log anything *)
|
|
()
|
|
| 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
|
|
| 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 () =
|
|
if not Config.flavors then
|
|
(* we do not need to keep the capture data in Buck/Java mode *)
|
|
ResultsDatabase.reset_capture_tables () ;
|
|
ResultsDatabase.db_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 =
|
|
let open Config in
|
|
[ backend_stats_dir_name
|
|
; classnames_dir_name
|
|
; frontend_stats_dir_name
|
|
; multicore_dir_name
|
|
; reporting_stats_dir_name ]
|
|
in
|
|
List.mem ~equal:String.equal dirs_to_delete
|
|
in
|
|
let should_delete_file =
|
|
let files_to_delete =
|
|
[ 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" :: ".csv" :: ".json" :: (if Config.flavors then [] else [".cg"])
|
|
in
|
|
fun name ->
|
|
(* Keep the JSON report *)
|
|
not (String.equal (Filename.basename name) Config.report_json)
|
|
&& ( 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 check_captured_empty mode =
|
|
let clean_command_opt = clean_compilation_command mode in
|
|
if Config.capture && Utils.directory_is_empty Config.captured_dir then (
|
|
( match clean_command_opt with
|
|
| Some clean_command ->
|
|
L.user_warning "@\nNothing to compile. Try running `%s` first.@." clean_command
|
|
| None ->
|
|
L.user_warning "@\nNothing to compile. Try cleaning the build first.@." ) ;
|
|
true )
|
|
else false
|
|
|
|
|
|
let register_perf_stats_report () =
|
|
let stats_dir = Filename.concat Config.results_dir Config.backend_stats_dir_name in
|
|
let stats_base = Config.perf_stats_prefix ^ ".json" in
|
|
let stats_file = Filename.concat stats_dir stats_base in
|
|
PerfStats.register_report_at_exit stats_file
|
|
|
|
|
|
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 ()
|
|
|
|
|
|
(* Create the .start file, and update the timestamp unless in continue mode *)
|
|
let touch_start_file_unless_continue () =
|
|
let start = Config.results_dir ^/ Config.start_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 not (Sys.file_exists start = `Yes) then create ()
|
|
else if not Config.continue_capture then ( delete () ; create () )
|
|
|
|
|
|
exception Infer_error of string
|
|
|
|
let default_error_handling : Unix.Exit_or_signal.t -> unit = function
|
|
| Ok _ ->
|
|
()
|
|
| Error _ as status when Config.keep_going ->
|
|
(* Log error and proceed past the failure when keep going mode is on *)
|
|
L.external_error "%s" (Unix.Exit_or_signal.to_string_hum status) ;
|
|
()
|
|
| Error _ as status ->
|
|
raise (Infer_error (Unix.Exit_or_signal.to_string_hum status))
|
|
|
|
|
|
let run_command ?(cleanup= default_error_handling) ~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
|
|
"@\nxcpretty 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 = Unix.getcwd () 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 db_files in
|
|
CaptureCompilationDatabase.capture_files_in_database compilation_database
|
|
|
|
|
|
let capture ~changed_files mode =
|
|
match mode with
|
|
| Analyze ->
|
|
()
|
|
| BuckCompilationDB (prog, args) ->
|
|
L.progress "Capturing using Buck's compilation database...@." ;
|
|
let json_cdb = CaptureCompilationDatabase.get_compilation_database_files_buck ~prog ~args in
|
|
capture_with_compilation_database ~changed_files json_cdb
|
|
| BuckGenrule path ->
|
|
L.progress "Capturing for Buck genrule compatibility...@." ;
|
|
JMain.from_arguments path
|
|
| 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
|
|
| Python args ->
|
|
(* pretend prog is the root directory of the project *)
|
|
PythonMain.go args
|
|
| PythonCapture (build_system, build_cmd) ->
|
|
L.progress "Capturing in %s mode...@." (Config.string_of_build_system build_system) ;
|
|
let in_buck_mode = Config.equal_build_system build_system BBuck in
|
|
let infer_py = Config.lib_dir ^/ "python" ^/ "infer.py" in
|
|
let args =
|
|
List.rev_append Config.anon_args
|
|
( [ "--analyzer"
|
|
; List.Assoc.find_exn ~equal:Config.equal_analyzer
|
|
(List.map ~f:(fun (n, a) -> (a, n)) Config.string_to_analyzer)
|
|
Config.analyzer ]
|
|
@ ( match Config.blacklist with
|
|
| Some s when in_buck_mode ->
|
|
["--blacklist-regex"; s]
|
|
| _ ->
|
|
[] )
|
|
@ (if not Config.continue_capture then [] else ["--continue"])
|
|
@ ( match Config.java_jar_compiler with
|
|
| None ->
|
|
[]
|
|
| Some p ->
|
|
["--java-jar-compiler"; p] )
|
|
@ ( match List.rev Config.buck_build_args with
|
|
| args when in_buck_mode ->
|
|
List.map ~f:(fun arg -> ["--Xbuck"; "'" ^ arg ^ "'"]) args |> List.concat
|
|
| _ ->
|
|
[] )
|
|
@ (if not Config.debug_mode then [] else ["--debug"])
|
|
@ (if not Config.debug_exceptions then [] else ["--debug-exceptions"])
|
|
@ (if Config.filtering then [] else ["--no-filtering"])
|
|
@ (if not Config.flavors || not in_buck_mode then [] else ["--use-flavors"])
|
|
@ "-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"])
|
|
@ (if not Config.reactive_mode then [] else ["--reactive"])
|
|
@ "--out"
|
|
:: Config.results_dir
|
|
::
|
|
( match Config.xcode_developer_dir with
|
|
| None ->
|
|
[]
|
|
| Some d ->
|
|
["--xcode-developer-dir"; d] )
|
|
@ "--"
|
|
::
|
|
( if in_buck_mode && Config.flavors then
|
|
(* 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 prog, buck_args = IList.uncons_exn build_cmd in
|
|
let {Buck.command; rev_not_targets; targets} =
|
|
Buck.add_flavors_to_buck_arguments ~filter_kind:`Auto ~dep_depth:None
|
|
~extra_flavors:[] buck_args
|
|
in
|
|
let all_args = List.rev_append rev_not_targets targets in
|
|
let updated_buck_cmd = prog :: command :: Buck.store_args_in_file all_args in
|
|
Logging.(debug Capture Quiet)
|
|
"Processed buck command '%a'@\n" (Pp.seq Pp.string) updated_buck_cmd ;
|
|
updated_buck_cmd
|
|
else 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 ()
|
|
| Error _ as status ->
|
|
raise (Infer_error (Unix.Exit_or_signal.to_string_hum status))
|
|
| Ok _ ->
|
|
())
|
|
()
|
|
| 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
|
|
|
|
|
|
let run_parallel_analysis ~changed_files : unit =
|
|
let multicore_dir = Config.results_dir ^/ Config.multicore_dir_name in
|
|
Utils.rmtree multicore_dir ;
|
|
Unix.mkdir_p multicore_dir ;
|
|
InferAnalyze.main ~changed_files ~makefile:(multicore_dir ^/ "Makefile") ;
|
|
run_command ~prog:"make"
|
|
~args:
|
|
( "--directory"
|
|
:: multicore_dir
|
|
:: (if Config.keep_going then "--keep-going" else "--no-keep-going")
|
|
:: "--jobs"
|
|
:: string_of_int Config.jobs
|
|
:: Option.value_map
|
|
~f:(fun l -> ["--load-average"; string_of_float l])
|
|
~default:[] Config.load_average
|
|
@ if Config.debug_mode then [] else ["--silent"] )
|
|
()
|
|
|
|
|
|
let execute_analyze ~changed_files =
|
|
if Int.equal Config.jobs 1 || Config.cluster_cmdline <> None then
|
|
InferAnalyze.main ~changed_files ~makefile:""
|
|
else run_parallel_analysis ~changed_files
|
|
|
|
|
|
let report ?(suppress_console= false) () =
|
|
let report_json = Config.(results_dir ^/ report_json) in
|
|
InferPrint.main ~report_json:(Some report_json) ;
|
|
(* 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 = Option.value ~default:(Config.results_dir ^/ "bugs.txt") Config.issues_txt in
|
|
let args =
|
|
if_true "--pmd-xml" Config.pmd_xml
|
|
@@ if_true "--quiet"
|
|
(Config.quiet || suppress_console)
|
|
[ "--issues-json"
|
|
; report_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)
|
|
|
|
|
|
let analyze_and_report ?suppress_console_report ~changed_files mode =
|
|
let should_analyze, should_report =
|
|
match (Config.command, mode, Config.analyzer) with
|
|
| _, PythonCapture (BBuck, _), _ when not Config.flavors ->
|
|
(* 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)
|
|
| _, _, Linters ->
|
|
(false, true)
|
|
| (Capture | Compile), _, _ | _, _, (CaptureOnly | CompileOnly) ->
|
|
(false, false)
|
|
| _, _, (Checkers | Crashcontext) ->
|
|
(true, true)
|
|
in
|
|
let should_merge =
|
|
match mode with
|
|
| PythonCapture (BBuck, _) when Config.flavors && CLOpt.(equal_command Run) Config.command ->
|
|
(* if doing capture + analysis of buck with flavors, we always need to merge targets before the analysis phase *)
|
|
true
|
|
| _ ->
|
|
(* else rely on the command line value *) Config.merge
|
|
in
|
|
if should_merge then MergeCapture.merge_captured_targets () ;
|
|
if (should_analyze || should_report)
|
|
&& (Sys.file_exists Config.captured_dir <> `Yes || check_captured_empty mode)
|
|
then L.user_error "There was nothing to analyze.@\n@."
|
|
else if should_analyze then execute_analyze ~changed_files ;
|
|
if should_report && Config.report then report ?suppress_console:suppress_console_report ()
|
|
|
|
|
|
(** 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 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
|
|
| `Python ->
|
|
Version.python_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"
|
|
| `Python ->
|
|
"python"
|
|
| `Xcode ->
|
|
"clang and xcode"
|
|
in
|
|
L.(die UserError)
|
|
"Unsupported build mode: %s@\nInfer was built with %s analyzers disabled.@ Please rebuild infer with %s enabled.@."
|
|
requested_mode_string analyzer_string analyzer_string
|
|
|
|
|
|
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
|
|
| BPython ->
|
|
Config.string_of_build_system build_system |> assert_supported_mode `Python
|
|
| BXcode ->
|
|
Config.string_of_build_system build_system |> assert_supported_mode `Xcode
|
|
| BBuck ->
|
|
let analyzer, build_string =
|
|
if Config.flavors then (`Clang, "buck with flavors")
|
|
else if Option.is_some Config.buck_compilation_database then
|
|
(`Clang, "buck compilation database")
|
|
else (
|
|
if Config.reactive_mode then
|
|
L.user_error
|
|
"WARNING: The reactive analysis mode is not compatible with the Buck integration for Java" ;
|
|
(`Java, Config.string_of_build_system build_system) )
|
|
in
|
|
assert_supported_mode analyzer build_string
|
|
| BAnalyze ->
|
|
()
|
|
|
|
|
|
let mode_of_build_command build_cmd =
|
|
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 ->
|
|
build_system
|
|
| None ->
|
|
Config.build_system_of_exe_name (Filename.basename prog)
|
|
in
|
|
assert_supported_build_system build_system ;
|
|
match (build_system : Config.build_system) with
|
|
| BAnalyze ->
|
|
CLOpt.warnf
|
|
"WARNING: `infer -- analyze` is deprecated; use the `infer analyze` subcommand instead@." ;
|
|
Analyze
|
|
| BBuck when Option.is_some Config.buck_compilation_database ->
|
|
BuckCompilationDB (prog, List.append args (List.rev Config.buck_build_args))
|
|
| 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)
|
|
| BPython ->
|
|
Python args
|
|
| BXcode when Config.xcpretty ->
|
|
XcodeXcpretty (prog, args)
|
|
| (BAnt | BBuck | BGradle | BNdk | BXcode) 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.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.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) )
|
|
|
|
|
|
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 Config.dump_duplicate_symbols then reset_duplicates_file () ;
|
|
(* infer might be called from a Makefile and itself uses `make` to run the analysis in parallel,
|
|
but cannot communicate with the parent make command. Since infer won't interfere with them
|
|
anyway, pretend that we are not called from another make to prevent make falling back to a
|
|
mono-threaded execution. *)
|
|
Unix.unsetenv "MAKEFLAGS" ;
|
|
if Config.developer_mode then register_perf_stats_report () ;
|
|
if not Config.buck_cache_mode && not Config.infer_is_clang && not Config.infer_is_javac then
|
|
touch_start_file_unless_continue () ;
|
|
()
|
|
|
|
|
|
let run_epilogue mode =
|
|
( if CLOpt.is_originator then
|
|
let in_buck_mode = match mode with PythonCapture (BBuck, _) -> true | _ -> false in
|
|
if Config.developer_mode then StatsAggregator.generate_files () ;
|
|
if Config.equal_analyzer Config.analyzer Config.Crashcontext then
|
|
Crashcontext.crashcontext_epilogue ~in_buck_mode ;
|
|
if Config.fail_on_bug then fail_on_issue_epilogue () ) ;
|
|
if Config.buck_cache_mode then clean_results_dir () ;
|
|
()
|
|
|
|
|
|
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
|