From ffd82c01a7ca42b0a96dd694a98a62e69b5ba9e0 Mon Sep 17 00:00:00 2001 From: Dulma Churchill Date: Wed, 1 Feb 2017 02:43:45 -0800 Subject: [PATCH] [clang] Fail when compilations fails in the capture, linters mode Reviewed By: jvillard Differential Revision: D4237708 fbshipit-source-id: 0e4c210 --- infer/src/base/Config.ml | 9 ++++++++- infer/src/base/Config.mli | 1 + infer/src/base/Process.ml | 19 ++++++++++++------- infer/src/base/Process.mli | 4 ++-- infer/src/clang/ClangCommand.re | 5 +++++ .../integration/CaptureCompilationDatabase.ml | 9 ++++++++- 6 files changed, 36 insertions(+), 11 deletions(-) diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index c2c13b3c6..8086624ed 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -600,7 +600,7 @@ and clang_compilation_db_files = and clang_frontend_action = CLOpt.mk_symbol_opt ~long:"clang-frontend-action" - ~exes:CLOpt.[Clang] + ~exes:CLOpt.[Clang;Driver] "Specify whether the clang frontend should capture or lint or both." ~symbols:clang_frontend_action_symbols @@ -633,6 +633,12 @@ and continue = "Continue the capture for the reactive analysis, increasing the changed files/procedures. (If \ a procedure was changed beforehand, keep the changed marking.)" +and linters_ignore_clang_failures = + CLOpt.mk_bool ~long:"linters-ignore-clang-failures" + ~exes:CLOpt.[Driver] + ~default:false + "Continue linting files even if some compilation fails." + and copy_propagation = CLOpt.mk_bool ~deprecated:["copy-propagation"] ~long:"copy-propagation" "Perform copy-propagation on the IR" @@ -1422,6 +1428,7 @@ and classpath = !classpath and cluster_cmdline = !cluster and compute_analytics = !compute_analytics and continue_capture = !continue +and linters_ignore_clang_failures = !linters_ignore_clang_failures and copy_propagation = !copy_propagation and crashcontext = !crashcontext and create_harness = !android_harness diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 5d5e3a7c6..bd994d901 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -174,6 +174,7 @@ val clang_include_to_override : string option val cluster_cmdline : string option val compute_analytics : bool val continue_capture : bool +val linters_ignore_clang_failures : bool val copy_propagation : bool val crashcontext : bool val create_harness : bool diff --git a/infer/src/base/Process.ml b/infer/src/base/Process.ml index ea31c5a3c..ba6e21d97 100644 --- a/infer/src/base/Process.ml +++ b/infer/src/base/Process.ml @@ -38,11 +38,15 @@ let create_process_and_wait ~prog ~args = (** Given a process id and a function that describes the command that the process id represents, prints a message explaining the command and its status, if in debug or stats mode. It also prints a dot to show progress of jobs being finished. *) -let print_status f pid status = +let print_status ~fail_on_failed_job f pid status = L.err "%a%s@." (fun fmt pid -> F.pp_print_string fmt (f pid)) pid (Unix.Exit_or_signal.to_string_hum status) ; - L.stdout ".%!" + L.stdout ".%!"; + match status with + | Error err when fail_on_failed_job -> + exit (match err with `Exit_non_zero i -> i | `Signal _ -> 1) + | _ -> () let start_current_jobs_count () = ref 0 @@ -53,14 +57,14 @@ module PidMap = Caml.Map.Make (Pid) (** [wait_for_son pid_child f jobs_count] wait for pid_child and all the other children and update the current jobs count. Use f to print the job status *) -let rec wait_for_child f current_jobs_count jobs_map = +let rec wait_for_child ~fail_on_failed_job f current_jobs_count jobs_map = let pid, status = Unix.wait `Any in Pervasives.decr current_jobs_count; Pervasives.incr waited_for_jobs; - print_status f pid status; + print_status ~fail_on_failed_job f pid status; jobs_map := PidMap.remove pid !jobs_map; if not (PidMap.is_empty !jobs_map) then - wait_for_child f current_jobs_count jobs_map + wait_for_child ~fail_on_failed_job f current_jobs_count jobs_map let pid_to_program jobsMap pid = try @@ -72,7 +76,7 @@ let pid_to_program jobsMap pid = and starts a new batch and so on. [gen_prog] should return a tuple [(dir_opt, command, args, env)] where [dir_opt] is an optional directory to chdir to before executing [command] with [args] in [env]. [prog_to_string] is used for printing information about the job's status. *) -let run_jobs_in_parallel jobs_stack gen_prog prog_to_string = +let run_jobs_in_parallel ?(fail_on_failed_job=false) jobs_stack gen_prog prog_to_string = let run_job () = let jobs_map = ref PidMap.empty in let current_jobs_count = start_current_jobs_count () in @@ -89,7 +93,8 @@ let run_jobs_in_parallel jobs_stack gen_prog prog_to_string = | `In_the_parent pid_child -> jobs_map := PidMap.add pid_child (prog_to_string job_prog) !jobs_map; if Int.equal (Stack.length jobs_stack) 0 || !current_jobs_count >= Config.jobs then - wait_for_child (pid_to_program !jobs_map) current_jobs_count jobs_map + wait_for_child ~fail_on_failed_job (pid_to_program !jobs_map) current_jobs_count + jobs_map done in run_job (); L.stdout ".@."; diff --git a/infer/src/base/Process.mli b/infer/src/base/Process.mli index 6446d3d5e..586be827e 100644 --- a/infer/src/base/Process.mli +++ b/infer/src/base/Process.mli @@ -25,8 +25,8 @@ val print_error_and_exit : env)] where [dir_opt] is an optional directory to chdir to before executing [command] with [args] in [env]. [prog_to_string] is used for printing information about the job's status. *) val run_jobs_in_parallel : - 'a Stack.t -> ('a -> (string option * string * string list * Unix.env)) -> ('a -> string) - -> unit + ?fail_on_failed_job:bool -> 'a Stack.t -> + ('a -> (string option * string * string list * Unix.env)) -> ('a -> string) -> unit (** Pipeline producer program into consumer program *) val pipeline : diff --git a/infer/src/clang/ClangCommand.re b/infer/src/clang/ClangCommand.re index a73b91c6f..f2f4ada9c 100644 --- a/infer/src/clang/ClangCommand.re +++ b/infer/src/clang/ClangCommand.re @@ -94,6 +94,11 @@ let clang_cc1_cmd_sanitizer cmd => { /* compilation-database-buck integration produces path to `dep.tmp` file that doesn't exist. Create it */ Unix.mkdir_p (Filename.dirname arg); arg + } else if ( + String.equal option "-dependency-file" && Option.is_some Config.use_compilation_database + /* In compilation database mode, dependency files are not assumed to exist */ + ) { + "/dev/null" } else if ( String.equal option "-isystem" ) { diff --git a/infer/src/integration/CaptureCompilationDatabase.ml b/infer/src/integration/CaptureCompilationDatabase.ml index 3f1be35ad..1f2979abd 100644 --- a/infer/src/integration/CaptureCompilationDatabase.ml +++ b/infer/src/integration/CaptureCompilationDatabase.ml @@ -86,7 +86,14 @@ let run_compilation_database compilation_database should_capture_file = let capture_text_upper = String.capitalize capture_text in let job_to_string = fun file -> Format.asprintf "%s %a" capture_text_upper SourceFile.pp file in - Process.run_jobs_in_parallel jobs_stack (run_compilation_file compilation_database) job_to_string + let fail_on_failed_job = + if Config.linters_ignore_clang_failures then false + else + match Config.use_compilation_database with + | Some `NoDeps -> Config.clang_frontend_do_lint + | _ -> false in + Process.run_jobs_in_parallel ~fail_on_failed_job jobs_stack + (run_compilation_file compilation_database) job_to_string (** Computes the compilation database files. *) let get_compilation_database_files_buck () =