@ -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