From b792d04fbd7d0665f7d928d8feb57835fd1400f1 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Mon, 14 Nov 2016 03:48:24 -0800 Subject: [PATCH] [clang] Integration with xcpretty and compilation database Reviewed By: jberdine Differential Revision: D4112287 fbshipit-source-id: f02d424 --- infer/src/backend/infer.ml | 18 +++++++++++ infer/src/base/Config.ml | 8 +++++ infer/src/base/Config.mli | 1 + infer/src/base/Process.ml | 31 +++++++++++++++++++ infer/src/base/Process.mli | 4 +++ .../integration/CaptureCompilationDatabase.ml | 23 ++++++++++++++ .../CaptureCompilationDatabase.mli | 6 +++- 7 files changed, 90 insertions(+), 1 deletion(-) diff --git a/infer/src/backend/infer.ml b/infer/src/backend/infer.ml index 16f1c9216..22f12d96f 100644 --- a/infer/src/backend/infer.ml +++ b/infer/src/backend/infer.ml @@ -90,6 +90,18 @@ let run_command cmd_list after_wait = exit exit_code ) +let check_xcpretty () = + let open Core.Std in + let exit_code = Unix.system "xcpretty --version" in + match exit_code with + | Ok () -> () + | Error _ -> + Logging.stderr + "@.xcpretty not found in the path. Please, install xcpretty \ + for a more robust integration with xcodebuild. Otherwise use the option \ + --no-xcpretty.@.@."; + Unix.exit_immediately 1 + let capture build_cmd build_mode = let analyze_cmd = "analyze" in let is_analyze_cmd cmd = @@ -98,6 +110,12 @@ let capture build_cmd build_mode = | _ -> false in let build_cmd = match build_mode with + | Xcode when Config.xcpretty -> + check_xcpretty (); + let json_cdb = + CaptureCompilationDatabase.get_compilation_database_files_xcodebuild () in + CaptureCompilationDatabase.capture_files_in_database json_cdb; + [analyze_cmd] | Buck when Option.is_some Config.use_compilation_database -> let json_cdb = CaptureCompilationDatabase.get_compilation_database_files_buck () in CaptureCompilationDatabase.capture_files_in_database json_cdb; diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 693239672..4b5c4928a 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -1269,6 +1269,13 @@ and xcode_developer_dir = ~exes:CLOpt.[Toplevel] ~meta:"XCODE_DEVELOPER_DIR" "Specify the path to Xcode developer directory (Buck flavors only)" +and xcpretty = + CLOpt.mk_bool ~long:"xcpretty" + ~default:true + ~exes:CLOpt.[Toplevel] + "Infer will use xcpretty together with xcodebuild to analyze an iOS app. xcpretty just needs \ + to be in the path, infer command is still just infer -- . (Recommended)" + and xml_specs = CLOpt.mk_bool ~deprecated:["xml"] ~long:"xml-specs" "Export specs into XML files file1.xml ... filen.xml" @@ -1504,6 +1511,7 @@ and worklist_mode = !worklist_mode and write_dotty = !write_dotty and write_html = !write_html and xcode_developer_dir = !xcode_developer_dir +and xcpretty = !xcpretty and xml_specs = !xml_specs diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index fe1d4b422..9d53b90d4 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -267,6 +267,7 @@ val worklist_mode : int val write_dotty : bool val write_html : bool val xcode_developer_dir : string option +val xcpretty : bool val xml_specs : bool diff --git a/infer/src/base/Process.ml b/infer/src/base/Process.ml index 7463064d9..e8b695cfb 100644 --- a/infer/src/base/Process.ml +++ b/infer/src/base/Process.ml @@ -111,3 +111,34 @@ let run_jobs_in_parallel jobs_stack gen_cmd cmd_to_string = run_job (); Logging.stdout ".\n%!"; Logging.out "Waited for %d jobs" !waited_for_jobs + +let pipeline ~producer_prog ~producer_args ~consumer_prog ~consumer_args = + let open Core.Std in + let pipe_in, pipe_out = Unix.pipe () in + match Unix.fork () with + | `In_the_child -> + (* redirect producer's stdout to pipe_out *) + Unix.dup2 ~src:pipe_out ~dst:Unix.stdout ; + (* close producer's copy of pipe ends *) + Unix.close pipe_out ; + Unix.close pipe_in ; + (* exec producer *) + never_returns (Unix.exec ~prog:producer_prog ~args:producer_args ()) + | `In_the_parent producer_pid -> + match Unix.fork () with + | `In_the_child -> + (* redirect consumer's stdin to pipe_in *) + Unix.dup2 ~src:pipe_in ~dst:Unix.stdin ; + (* close consumer's copy of pipe ends *) + Unix.close pipe_out ; + Unix.close pipe_in ; + (* exec consumer *) + never_returns (Unix.exec ~prog:consumer_prog ~args:consumer_args ()) + | `In_the_parent consumer_pid -> + (* close parent's copy of pipe ends *) + Unix.close pipe_out ; + Unix.close pipe_in ; + (* wait for children *) + let producer_status = Unix.waitpid producer_pid in + let consumer_status = Unix.waitpid consumer_pid in + (producer_status, consumer_status) diff --git a/infer/src/base/Process.mli b/infer/src/base/Process.mli index 3a3ebff7f..0cecdb655 100644 --- a/infer/src/base/Process.mli +++ b/infer/src/base/Process.mli @@ -35,3 +35,7 @@ val print_unix_error : string -> exn -> unit printing information about the job's status. *) val run_jobs_in_parallel : 'a Stack.t -> ('a -> (string option * string * string array * string array)) -> ('a -> string) -> unit + +(** Pipeline producer program into consumer program *) +val pipeline : producer_prog:string -> producer_args:string list -> consumer_prog:string -> + consumer_args:string list -> Core.Std.Unix.Exit_or_signal.t * Core.Std.Unix.Exit_or_signal.t diff --git a/infer/src/integration/CaptureCompilationDatabase.ml b/infer/src/integration/CaptureCompilationDatabase.ml index 670871ef0..3ebac6bb5 100644 --- a/infer/src/integration/CaptureCompilationDatabase.ml +++ b/infer/src/integration/CaptureCompilationDatabase.ml @@ -155,6 +155,29 @@ let get_compilation_database_files_buck () = let cmd = String.concat " " cmd in Process.print_error_and_exit "Incorrect buck command: %s. Please use buck build " cmd +(** Compute the compilation database files. *) +let get_compilation_database_files_xcodebuild () = + let cmd_and_args = IList.rev Config.rest in + let temp_dir = Config.results_dir // "clang" in + create_dir temp_dir; + let tmp_file = Filename.temp_file ~temp_dir:temp_dir "cdb" ".json" in + let xcodebuild_cmd, xcodebuild_args = + match cmd_and_args with + | [] -> failwith("Build command cannot be empty") + | cmd :: _ -> cmd, cmd_and_args in + let xcpretty_cmd = "xcpretty" in + let xcpretty_cmd_args = + [xcpretty_cmd; "--report"; "json-compilation-database"; "--output"; tmp_file] in + let producer_status, consumer_status = + Process.pipeline ~producer_prog:xcodebuild_cmd ~producer_args:xcodebuild_args + ~consumer_prog:xcpretty_cmd ~consumer_args:xcpretty_cmd_args in + match producer_status, consumer_status with + | Ok (), Ok () -> [tmp_file] + | _ -> + Logging.stderr "There was an error executing the build command"; + exit 1 + + let capture_files_in_database db_json_files = let changed_files = read_files_to_compile () in let compilation_database = CompilationDatabase.empty () in diff --git a/infer/src/integration/CaptureCompilationDatabase.mli b/infer/src/integration/CaptureCompilationDatabase.mli index c57711010..eaaae7679 100644 --- a/infer/src/integration/CaptureCompilationDatabase.mli +++ b/infer/src/integration/CaptureCompilationDatabase.mli @@ -12,7 +12,11 @@ is passed, we only capture the files there *) val capture_files_in_database : string list -> unit -(** Gets the compilation database files that contain the compilation given by the +(** Get the compilation database files that contain the compilation given by the buck command. It will be the compilation of the passed targets only or also the dependencies according to the flag --use-compilation-database deps | no-deps *) val get_compilation_database_files_buck : unit -> string list + +(** Get the compilation database files that contain the compilation given by the + xcodebuild command, using xcpretty. *) +val get_compilation_database_files_xcodebuild : unit -> string list