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