[driver] Add skeleton capture and analysis driver to infer.ml

Summary:
This diff adds a skeleton implementation of the capture and analysis
driver to infer.ml, and removes some unnecessary code from infer.py.
With this, individual capture and analysis modules can be added, or
moved from python.

Reviewed By: jvillard

Differential Revision: D4109547

fbshipit-source-id: 0dce2bf
master
Josh Berdine 8 years ago committed by Facebook Github Bot
parent 7fc5b4d55d
commit 216812a615

@ -211,10 +211,6 @@ def main():
if not (buck_not_in_compilation_database_mode or
mod_name == 'javac' or
mod_name == 'java'):
# Something should be already captured, otherwise analysis would fail
if not os.path.exists(os.path.join(args.infer_out, 'captured')):
print('There was nothing to analyze, exiting')
exit(os.EX_USAGE)
analysis = analyze.AnalyzerWrapper(args)
analysis.analyze_and_report()
analysis.save_stats()

@ -9,7 +9,27 @@
open! Utils
(** Top-level driver that orchestrates build system integration, frontends, and backend *)
(** Top-level driver that orchestrates build system integration, frontends, backend, and
reporting *)
module L = Logging
let rec rmtree name =
match Unix.opendir name with
| dir -> (
match Unix.readdir dir with
| entry when entry = Filename.current_dir_name || entry = Filename.parent_dir_name ->
()
| entry ->
rmtree entry
| exception End_of_file ->
Unix.closedir dir ;
Unix.rmdir name
)
| exception Unix.Unix_error (Unix.ENOTDIR, _, _) ->
Unix.unlink name
| exception Unix.Unix_error (Unix.ENOENT, _, _) ->
()
(** as the Config.fail_on_bug flag mandates, exit with error when an issue is reported *)
let fail_on_issue_epilogue () =
@ -20,12 +40,58 @@ let fail_on_issue_epilogue () =
if issues <> [] then exit Config.fail_on_issue_exit_code
| None -> ()
let () =
(* permissions used for created files *)
let file_perm = 0o0666
let create_results_dir () =
create_path (Config.results_dir // Config.captured_dir_name) ;
create_path (Config.results_dir // Config.sources_dir_name) ;
create_path (Config.results_dir // Config.specs_dir_name)
let touch_start_file () =
let start = Config.results_dir // Config.start_filename in
let flags =
Unix.O_CREAT :: Unix.O_WRONLY :: (if Config.continue_capture then [Unix.O_EXCL] else []) in
(* create new file, or open existing file for writing to update modified timestamp *)
try Unix.close (Unix.openfile start flags file_perm)
with Unix.Unix_error (Unix.EEXIST, _, _) -> ()
type build_mode = Analyze | Ant | Buck | Gradle | Java | Javac | Make | Mvn | Ndk | Xcode
let build_mode_of_string path =
match Filename.basename path with
| "analyze" -> Analyze
| "ant" -> Ant
| "buck" -> Buck
| "gradle" | "gradlew" -> Gradle
| "java" -> Java
| "javac" -> Javac
| "cc" | "clang" | "clang++" | "cmake" | "configure" | "g++" | "gcc" | "make" | "waf" -> Make
| "mvn" -> Mvn
| "ndk-build" -> Ndk
| "xcodebuild" -> Xcode
| cmd -> failwithf "Unsupported build command %s" cmd
let remove_results_dir build_mode =
if not (build_mode = Analyze || Config.buck || Config.reactive_mode) then
rmtree Config.results_dir
let run_command cmd_list after_wait =
let cmd = Array.of_list cmd_list in
let pid = Unix.create_process cmd.(0) cmd Unix.stdin Unix.stdout Unix.stderr in
let _, status = Unix.waitpid [] pid in
let exit_code = match status with Unix.WEXITED i -> i | _ -> 1 in
after_wait exit_code ;
if exit_code <> 0 then (
L.err "Failed to execute: %s@\n" (String.concat " " cmd_list) ;
exit exit_code
)
let capture build_cmd = function
| build_mode ->
let in_buck_mode = build_mode = Buck in
let infer_py = Config.lib_dir // "python" // "infer.py" in
let build_cmd = IList.rev Config.rest in
let in_buck_mode = match build_cmd with "buck" :: _ -> true | _ -> false in
let args_py =
Array.of_list (
run_command (
infer_py ::
Config.anon_args @
(match Config.analyzer with None -> [] | Some a ->
@ -67,22 +133,51 @@ let () =
["--xcode-developer-dir"; d]) @
(if Config.rest = [] then [] else
("--" :: build_cmd))
) in
let pid = Unix.create_process args_py.(0) args_py Unix.stdin Unix.stdout Unix.stderr in
let _, status = Unix.waitpid [] pid in
let exit_code = match status with
| Unix.WEXITED i -> i
| _ -> 1 in
) (fun exit_code ->
if exit_code = Config.infer_py_argparse_error_exit_code then
(* swallow infer.py argument parsing error *)
Config.print_usage_exit ();
if exit_code <> 0 then (
prerr_endline ("Failed to execute: " ^ (String.concat " " (Array.to_list args_py))) ;
exit exit_code
Config.print_usage_exit ()
)
let analyze = function
| Buck when Config.use_compilation_database = None ->
(* 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. *)
()
| Java | Javac ->
(* In Java and Javac modes, analysis is invoked from capture. *)
()
| Analyze | Ant | Buck | Gradle | Make | Mvn | Ndk | Xcode ->
if not (Sys.file_exists Config.(results_dir // captured_dir_name)) then (
L.err "There was nothing to analyze, exiting" ;
Config.print_usage_exit ()
);
(match Config.analyzer with
| None | Some (Infer | Eradicate | Checkers | Tracing | Crashcontext | Quandary) ->
(* Still handled by infer.py through capture function above *)
()
| Some Linters ->
(* Still handled by infer.py through capture function above *)
()
| Some (Capture | Compile) ->
(* Still handled by infer.py through capture function above *)
()
)
let epilogue build_mode =
if Config.is_originator then (
if Config.analyzer = Some Config.Crashcontext then
Crashcontext.crashcontext_epilogue ~in_buck_mode;
Crashcontext.crashcontext_epilogue ~in_buck_mode:(build_mode = Buck);
if Config.fail_on_bug then
fail_on_issue_epilogue ();
)
let () =
let build_cmd = IList.rev Config.rest in
let build_mode = match build_cmd with path :: _ -> build_mode_of_string path | [] -> Analyze in
remove_results_dir build_mode ;
create_results_dir () ;
touch_start_file () ;
capture build_cmd build_mode ;
analyze build_mode ;
epilogue build_mode

Loading…
Cancel
Save