Summary: This will be needed to re-use the functions now in Driver.ml in other contexts without always adding to infer.ml. For instance, this is used in a later diff to do a diff-analysis orchestrator that needs to run the capture and analysis several times. Reviewed By: jberdine Differential Revision: D5319862 fbshipit-source-id: caf9551master
parent
83148a71aa
commit
2f569e2b97
@ -0,0 +1,518 @@
|
||||
(*
|
||||
* 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
|
||||
|
||||
type build_system =
|
||||
| BAnalyze | BAnt | BBuck | BClang | BGradle | BJava | BJavac | BMake | BMvn
|
||||
| BNdk | BXcode
|
||||
[@@deriving compare]
|
||||
|
||||
let equal_build_system = [%compare.equal : build_system]
|
||||
|
||||
(* List of ([build system], [executable name]). Several executables may map to the same build
|
||||
system. In that case, the first one in the list will be used for printing, eg, in which mode
|
||||
infer is running. *)
|
||||
let build_system_exe_assoc = [
|
||||
BAnalyze, "analyze"; BAnt, "ant"; BBuck, "buck"; BGradle, "gradle"; BGradle, "gradlew";
|
||||
BJava, "java"; BJavac, "javac";
|
||||
BClang, "cc"; BClang, "clang"; BClang, "gcc"; BClang, "clang++"; BClang, "c++"; BClang, "g++";
|
||||
BMake, "make"; BMake, "configure"; BMake, "cmake"; BMake, "waf";
|
||||
BMvn, "mvn"; BMvn, "mvnw"; BNdk, "ndk-build"; BXcode, "xcodebuild";
|
||||
]
|
||||
|
||||
let build_system_of_exe_name name =
|
||||
try
|
||||
List.Assoc.find_exn (List.Assoc.inverse build_system_exe_assoc) name
|
||||
with Not_found ->
|
||||
invalid_argf "Unsupported build command %s" name
|
||||
|
||||
let string_of_build_system build_system =
|
||||
List.Assoc.find_exn build_system_exe_assoc build_system
|
||||
|
||||
(* 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
|
||||
| PythonCapture of build_system * string list
|
||||
| XcodeXcpretty of string * string list
|
||||
[@@deriving compare]
|
||||
|
||||
let equal_driver_mode = [%compare.equal : mode]
|
||||
|
||||
let pp_driver_mode fmt driver_mode =
|
||||
let log_argfile_arg fname =
|
||||
try
|
||||
F.fprintf fmt "-- Contents of '%s'@\n" fname;
|
||||
In_channel.iter_lines ~f:(F.fprintf fmt "%s@\n") (In_channel.create fname);
|
||||
F.fprintf fmt "-- /Contents of '%s'@." fname;
|
||||
with exn ->
|
||||
F.fprintf fmt " Error reading file '%s':@\n %a@." fname Exn.pp exn in
|
||||
match driver_mode with
|
||||
| Analyze | BuckGenrule _ | BuckCompilationDB _ | ClangCompilationDB _ | PythonCapture (_,_)
|
||||
| XcodeXcpretty _ ->
|
||||
(* these are pretty boring, do not log anything *)
|
||||
()
|
||||
| Javac (_, prog, args) ->
|
||||
F.fprintf fmt "Javac driver mode:@\nprog = %s@\n" prog;
|
||||
let log_arg arg =
|
||||
F.fprintf fmt "Arg: %s@\n" arg;
|
||||
(* "@fname" means that fname is an arg file containing additional arguments to pass to
|
||||
javac. *)
|
||||
String.chop_prefix ~prefix:"@" arg
|
||||
|>
|
||||
(* Sometimes these argfiles go away at the end of the build and we cannot inspect them after
|
||||
the fact, so log them now. *)
|
||||
Option.iter ~f:log_argfile_arg in
|
||||
List.iter ~f:log_arg args
|
||||
| Maven (prog, args) ->
|
||||
F.fprintf fmt "Maven driver mode:@\nprog = %s@\n" prog;
|
||||
List.iter ~f:(F.fprintf fmt "Arg: %s@\n") args
|
||||
| Clang (_, prog, args) ->
|
||||
F.fprintf fmt "Clang driver mode:@\nprog = %s@\n" prog;
|
||||
List.iter ~f:(F.fprintf fmt "Arg: %s@\n") args
|
||||
|
||||
(* A clean command for each driver mode to be suggested to the user
|
||||
in case nothing got captured. *)
|
||||
let clean_compilation_command driver_mode =
|
||||
match driver_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 () =
|
||||
(* 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 =
|
||||
"backend_stats" :: "classnames" :: "filelists" :: "frontend_stats" :: "multicore" ::
|
||||
"reporting_stats" :: "sources" ::
|
||||
if Config.flavors then []
|
||||
else ["attributes"] in
|
||||
List.mem ~equal:String.equal dirs_to_delete in
|
||||
let should_delete_file =
|
||||
let suffixes_to_delete =
|
||||
".txt" :: ".csv" :: ".json" ::
|
||||
if Config.flavors then []
|
||||
else [ ".cfg"; ".cg" ] in
|
||||
fun name ->
|
||||
(* Keep the JSON report *)
|
||||
not (String.equal (Filename.basename name) "report.json")
|
||||
&& List.exists ~f:(Filename.check_suffix name) suffixes_to_delete in
|
||||
let rec clean name =
|
||||
let rec cleandir dir = match Unix.readdir dir with
|
||||
| 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 (
|
||||
clean (name ^/ entry)
|
||||
);
|
||||
cleandir dir (* next entry *)
|
||||
| exception End_of_file ->
|
||||
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
|
||||
clean Config.results_dir
|
||||
|
||||
let check_captured_empty driver_mode =
|
||||
let clean_command_opt = clean_compilation_command driver_mode in
|
||||
(* if merge is passed, the captured folder will be empty at this point,
|
||||
but will be filled later on. *)
|
||||
if Utils.dir_is_empty Config.captured_dir && not Config.merge 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 ())
|
||||
|
||||
|
||||
let run_command ~prog ~args cleanup =
|
||||
Unix.waitpid (Unix.fork_exec ~prog ~args:(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 = function
|
||||
| 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) ->
|
||||
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) ->
|
||||
L.progress "Capturing in javac mode...@.";
|
||||
Javac.capture compiler ~prog ~args
|
||||
| Maven (prog, args) ->
|
||||
L.progress "Capturing in maven mode...@.";
|
||||
Maven.capture ~prog ~args
|
||||
| PythonCapture (build_system, build_cmd) ->
|
||||
L.progress "Capturing in %s mode...@." (string_of_build_system build_system);
|
||||
let in_buck_mode = 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.create_harness then [] else
|
||||
["--android-harness"]) @
|
||||
(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.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;
|
||||
Buck.add_flavors_to_buck_command build_cmd
|
||||
else build_cmd
|
||||
) in
|
||||
run_command ~prog:infer_py ~args
|
||||
(function
|
||||
| Result.Error (`Exit_non_zero exit_code) ->
|
||||
if Int.equal exit_code Config.infer_py_argparse_error_exit_code then
|
||||
(* swallow infer.py argument parsing error *)
|
||||
Config.print_usage_exit ()
|
||||
| _ ->
|
||||
()
|
||||
)
|
||||
| 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 =
|
||||
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:(
|
||||
"-C" :: multicore_dir ::
|
||||
"-k" ::
|
||||
"-j" :: (string_of_int Config.jobs) ::
|
||||
(Option.value_map ~f:(fun l -> ["-l"; string_of_float l]) ~default:[] Config.load_average) @
|
||||
(if Config.debug_mode then [] else ["-s"])
|
||||
) (fun _ -> ())
|
||||
|
||||
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 () =
|
||||
let report_csv =
|
||||
if Config.buck_cache_mode then None else Some (Config.results_dir ^/ "report.csv") in
|
||||
let report_json = Some (Config.results_dir ^/ "report.json") in
|
||||
InferPrint.main ~report_csv ~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 or in quiet mode. *)
|
||||
match Config.quiet || Config.buck_cache_mode, Config.report_hook with
|
||||
| true, _
|
||||
| false, None ->
|
||||
()
|
||||
| false, Some prog ->
|
||||
let if_some key opt args = match opt with None -> args | Some arg -> key :: arg :: args in
|
||||
let if_true key opt args = if not opt then args else key :: args in
|
||||
let args =
|
||||
if_some "--issues-csv" report_csv @@
|
||||
if_some "--issues-json" report_json @@
|
||||
if_some "--issues-txt" Config.bugs_txt @@
|
||||
if_true "--pmd-xml" Config.pmd_xml [
|
||||
"--project-root"; Config.project_root;
|
||||
"--results-dir"; Config.results_dir
|
||||
] in
|
||||
if is_error (Unix.waitpid (Unix.fork_exec ~prog ~args:(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 ~changed_files driver_mode =
|
||||
let should_analyze, should_report = match driver_mode, Config.analyzer with
|
||||
| PythonCapture (BBuck, _), _ ->
|
||||
(* In Buck mode when compilation db is not used, analysis is invoked either from capture or
|
||||
a separate Analyze invocation is necessary, depending on the buck flavor used. *)
|
||||
false, false
|
||||
| _ when Config.maven ->
|
||||
(* Called from Maven, only do capture. *)
|
||||
false, false
|
||||
| _, (CaptureOnly | CompileOnly) ->
|
||||
false, false
|
||||
| _, (BiAbduction | Checkers | Crashcontext | Eradicate) ->
|
||||
true, true
|
||||
| _, Linters ->
|
||||
false, true in
|
||||
if (should_analyze || should_report) &&
|
||||
(((Sys.file_exists Config.captured_dir) <> `Yes) ||
|
||||
check_captured_empty driver_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 ()
|
||||
|
||||
(** 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 ["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 exit Config.fail_on_issue_exit_code
|
||||
| Error error ->
|
||||
L.internal_error "Failed to read report file '%s': %s@." issues_json error ;
|
||||
()
|
||||
|
||||
let log_infer_args driver_mode =
|
||||
L.environment_info "INFER_ARGS = %s@\n"
|
||||
(Option.value (Sys.getenv CLOpt.args_env_var) ~default:"<not found>");
|
||||
List.iter ~f:(L.environment_info "anon arg: %s@\n") Config.anon_args;
|
||||
List.iter ~f:(L.environment_info "rest arg: %s@\n") Config.rest;
|
||||
L.environment_info "Project root = %s@\n" Config.project_root;
|
||||
L.environment_info "CWD = %s@\n" (Sys.getcwd ());
|
||||
L.environment_info "Driver mode:@\n%a@." pp_driver_mode driver_mode
|
||||
|
||||
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
|
||||
failwithf
|
||||
"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 with
|
||||
| BAnt | BGradle | BJava | BJavac | BMvn ->
|
||||
string_of_build_system build_system
|
||||
|> assert_supported_mode `Java
|
||||
| BClang | BMake | BNdk ->
|
||||
string_of_build_system build_system
|
||||
|> assert_supported_mode `Clang
|
||||
| BXcode ->
|
||||
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, string_of_build_system build_system)
|
||||
) in
|
||||
assert_supported_mode analyzer build_string
|
||||
| BAnalyze ->
|
||||
()
|
||||
|
||||
let driver_mode_of_build_cmd 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 = build_system_of_exe_name (Filename.basename prog) in
|
||||
assert_supported_build_system build_system;
|
||||
match build_system_of_exe_name (Filename.basename prog) 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)
|
||||
| BXcode when Config.xcpretty ->
|
||||
XcodeXcpretty (prog, args)
|
||||
| BAnt | BBuck | BGradle | BNdk | BXcode as build_system ->
|
||||
PythonCapture (build_system, build_cmd)
|
||||
|
||||
let get_driver_mode () =
|
||||
match Config.generated_classes with
|
||||
| _ when Config.maven ->
|
||||
(* infer is pretending to be javac in the Maven integration *)
|
||||
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 ->
|
||||
driver_mode_of_build_cmd (List.rev Config.rest)
|
||||
|
||||
let mode_from_command_line = lazy (
|
||||
match Config.generated_classes with
|
||||
| _ when Config.maven ->
|
||||
(* infer is pretending to be javac in the Maven integration *)
|
||||
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 ->
|
||||
driver_mode_of_build_cmd (List.rev Config.rest)
|
||||
)
|
||||
|
||||
let run_prologue driver_mode =
|
||||
if CLOpt.is_originator then L.environment_info "%a@\n" Config.pp_version () ;
|
||||
if Config.debug_mode || Config.stats_mode then log_infer_args driver_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";
|
||||
register_perf_stats_report () ;
|
||||
if not Config.buck_cache_mode then touch_start_file_unless_continue () ;
|
||||
()
|
||||
|
||||
let run_epilogue driver_mode =
|
||||
if CLOpt.is_originator then (
|
||||
let in_buck_mode = match driver_mode with | PythonCapture (BBuck, _) -> true | _ -> false in
|
||||
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 ();
|
||||
()
|
@ -0,0 +1,44 @@
|
||||
(*
|
||||
* 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
|
||||
|
||||
(** entry points for top-level functionalities such as capture under various build systems,
|
||||
analysis, and reporting *)
|
||||
|
||||
type build_system
|
||||
|
||||
(** 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
|
||||
| PythonCapture of build_system * string list
|
||||
| XcodeXcpretty of string * string list
|
||||
[@@deriving compare]
|
||||
|
||||
val equal_driver_mode : mode -> mode -> bool
|
||||
|
||||
(** driver mode computed from the command-line arguments and settings in Config *)
|
||||
val mode_from_command_line : mode Lazy.t
|
||||
|
||||
(** prepare the environment for running the given mode *)
|
||||
val run_prologue : mode -> unit
|
||||
|
||||
(** run the capture for the given mode *)
|
||||
val capture : changed_files:SourceFile.Set.t option -> mode -> unit
|
||||
|
||||
(** run the analysis for the given mode *)
|
||||
val analyze_and_report : changed_files:SourceFile.Set.t option -> mode -> unit
|
||||
|
||||
(** cleanup infer-out/ for Buck, generate stats, and generally post-process the results of a run *)
|
||||
val run_epilogue : mode -> unit
|
@ -0,0 +1,36 @@
|
||||
(*
|
||||
* 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
|
||||
|
||||
let reportdiff ~current_report:current_report_fname ~previous_report:previous_report_fname =
|
||||
let load_report filename_opt : Jsonbug_t.report =
|
||||
let empty_report = [] in
|
||||
Option.value_map
|
||||
~f:(fun filename -> Jsonbug_j.report_of_string (In_channel.read_all filename))
|
||||
~default:empty_report filename_opt in
|
||||
let current_report = load_report current_report_fname in
|
||||
let previous_report = load_report previous_report_fname in
|
||||
let diff =
|
||||
let unfiltered_diff = Differential.of_reports ~current_report ~previous_report in
|
||||
if Config.filtering then
|
||||
let file_renamings = match Config.file_renamings with
|
||||
| Some f -> DifferentialFilters.FileRenamings.from_json_file f
|
||||
| None -> DifferentialFilters.FileRenamings.empty in
|
||||
let interesting_paths = Option.map ~f:(fun fname ->
|
||||
List.map ~f:(SourceFile.create ~warn_on_error:false) (In_channel.read_lines fname))
|
||||
Config.differential_filter_files in
|
||||
DifferentialFilters.do_filter
|
||||
unfiltered_diff
|
||||
file_renamings
|
||||
~skip_duplicated_types:Config.skip_duplicated_types
|
||||
~interesting_paths
|
||||
else unfiltered_diff in
|
||||
let out_path = Config.results_dir ^/ "differential" in
|
||||
Unix.mkdir_p out_path;
|
||||
Differential.to_files diff out_path
|
@ -0,0 +1,11 @@
|
||||
(*
|
||||
* 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
|
||||
|
||||
val reportdiff : current_report:string option -> previous_report:string option -> unit
|
Loading…
Reference in new issue