From 0cc371d0a2ef6e43e78757e506b1c2af64f32048 Mon Sep 17 00:00:00 2001 From: Jules Villard Date: Wed, 13 Sep 2017 03:52:09 -0700 Subject: [PATCH] [driver] remove infer-clang fake command Summary: "Running as clang" was its own infer subcommand. That's not terribly good because it makes it hard to specify another subcommand, and in particular it broke the `compile` subcommand in some integrations because "running as clang" would always do capture. The cmake tests required to run with `--keep-going` because of this. Instead of having its own fake subcommand, simply add a new boolean in the config for "infer runs as clang", as we do for javac already (used in the mvn integration). Also make logging of the environment better. Reviewed By: jberdine Differential Revision: D5813986 fbshipit-source-id: 72b96cd --- infer/src/backend/infer.ml | 22 ++++++---- infer/src/base/CommandDoc.ml | 4 +- infer/src/base/CommandLineOption.ml | 23 +++++----- infer/src/base/CommandLineOption.mli | 4 -- infer/src/base/Config.ml | 47 +++++++------------- infer/src/base/Config.mli | 6 ++- infer/src/base/Pp.ml | 19 ++++++++- infer/src/base/Pp.mli | 5 +++ infer/src/integration/Driver.ml | 64 ++++++++++------------------ 9 files changed, 90 insertions(+), 104 deletions(-) diff --git a/infer/src/backend/infer.ml b/infer/src/backend/infer.ml index 41fb15d0e..af3e62b27 100644 --- a/infer/src/backend/infer.ml +++ b/infer/src/backend/infer.ml @@ -62,7 +62,7 @@ let setup_results_dir () = match Config.command with | Analyze -> assert_results_dir "have you run capture before?" - | Clang | Report | ReportDiff + | Report | ReportDiff -> create_results_dir () | Diff -> remove_results_dir () ; create_results_dir () @@ -70,12 +70,23 @@ let setup_results_dir () = -> let driver_mode = Lazy.force Driver.mode_from_command_line in if not ( Driver.(equal_mode driver_mode Analyze) - || Config.(buck || continue_capture || maven || reactive_mode) ) + || + Config.(buck || continue_capture || infer_is_clang || infer_is_javac || reactive_mode) ) then remove_results_dir () ; create_results_dir () | Explore -> assert_results_dir "please run an infer analysis first" +let log_environment_info () = + L.environment_info "CWD = %s@\n" (Sys.getcwd ()) ; + L.environment_info "Project root = %s@\n" Config.project_root ; + let infer_args = + Sys.getenv CLOpt.args_env_var |> Option.map ~f:(String.split ~on:CLOpt.env_var_sep) + |> Option.value ~default:[""] + in + L.environment_info "INFER_ARGS = %a" Pp.cli_args infer_args ; + L.environment_info "command line arguments: %a" Pp.cli_args (Array.to_list Sys.argv) + let () = ( if Config.linters_validate_syntax_only then match CTLParserHelper.validate_al_files () with @@ -85,6 +96,7 @@ let () = -> print_endline e ; L.exit 3 ) ; if Config.print_builtins then Builtin.print_and_exit () ; setup_results_dir () ; + log_environment_info () ; if Config.debug_mode then L.progress "Logs in %s@." (Config.results_dir ^/ Config.log_file) ; match Config.command with | Analyze @@ -97,12 +109,6 @@ let () = L.environment_info "Starting analysis %a" pp_cluster_opt Config.cluster_cmdline ; InferAnalyze.register_perf_stats_report () ; Driver.analyze_and_report Analyze ~changed_files:(Driver.read_config_changed_files ()) - | Clang - -> let prog, args = - match Array.to_list Sys.argv with prog :: args -> (prog, args) | [] -> assert false - (* Sys.argv is never empty *) - in - ClangWrapper.exe ~prog ~args | Report -> let report_json = match Config.from_json_report with diff --git a/infer/src/base/CommandDoc.ml b/infer/src/base/CommandDoc.ml index 16ea5a457..765565335 100644 --- a/infer/src/base/CommandDoc.ml +++ b/infer/src/base/CommandDoc.ml @@ -197,9 +197,7 @@ $(b,infer) $(i,[options])|} "cxx": false, "infer-blacklist-files-containing": ["@generated","@Generated"] }|} - ] - ~see_also:(List.filter ~f:(function CLOpt.Clang -> false | _ -> true) CLOpt.all_commands) - "infer" + ] ~see_also:CLOpt.all_commands "infer" let report = mk_command_doc ~title:"Infer Reporting" ~short_description:"compute and manipulate infer results" diff --git a/infer/src/base/CommandLineOption.ml b/infer/src/base/CommandLineOption.ml index e942b77d0..ac0494768 100644 --- a/infer/src/base/CommandLineOption.ml +++ b/infer/src/base/CommandLineOption.ml @@ -88,7 +88,6 @@ let anon_arg_action_of_parse_mode parse_mode = type command = | Analyze | Capture - | Clang | Compile | Diff | Explore @@ -104,7 +103,6 @@ let infer_exe_name = "infer" let command_to_name = [ (Analyze, "analyze") ; (Capture, "capture") - ; (Clang, "clang") ; (Compile, "compile") ; (Diff, "diff") ; (Explore, "explore") @@ -925,17 +923,16 @@ let parse ?config_file ~usage action initial_command = add_parsed_args_to_args_to_export () ; curr_usage in let to_export = - (* We have to be careful not to add too much data to the environment because the size of the - environment contributes to the length of the command to be run. If the environment + CLI is - too big, running any command will fail with a cryptic "exit code 127" error. Use an argfile - to prevent this from happening *) - let file = Filename.temp_file "args_" "" in - let quoted_file_args = - List.map (decode_env_to_argv !args_to_export) ~f:(fun arg -> - if String.contains arg '\'' then arg else F.sprintf "'%s'" arg ) - in - Out_channel.with_file file ~f:(fun oc -> Out_channel.output_lines oc quoted_file_args) ; - "@" ^ file + let argv_to_export = decode_env_to_argv !args_to_export in + if argv_to_export <> [] then + (* We have to be careful not to add too much data to the environment because the size of the + environment contributes to the length of the command to be run. If the environment + CLI is + too big, running any command will fail with a cryptic "exit code 127" error. Use an argfile + to prevent this from happening *) + let file = Filename.temp_file "args_" "" in + Out_channel.with_file file ~f:(fun oc -> Out_channel.output_lines oc argv_to_export) ; + "@" ^ file + else "" in Unix.putenv ~key:args_env_var ~data:to_export ; (!curr_command, curr_usage) diff --git a/infer/src/base/CommandLineOption.mli b/infer/src/base/CommandLineOption.mli index 9f3df02eb..a150dfca8 100644 --- a/infer/src/base/CommandLineOption.mli +++ b/infer/src/base/CommandLineOption.mli @@ -26,10 +26,6 @@ type command = | Capture (** capture compilation commands and translate source files into infer's intermediate language *) - | Clang - (** run and accept the same arguments as the clang compiler, may also capture the source - files compiled, and may also not actually compile the files depending on other options - *) | Compile (** set up the infer environment then run the compilation commands without capturing the source files *) diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 999505512..2cdc88afb 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -355,17 +355,14 @@ let clang_exe_aliases = ; "g++" ; "gcc" ] -let initial_command = +let exe_basename = (* Sys.executable_name tries to do clever things which we must avoid, use argv[0] instead *) - let exe_basename = Filename.basename Sys.argv.(0) in - let is_clang = List.mem ~equal:String.equal clang_exe_aliases in - match CLOpt.command_of_exe_name exe_basename with - | Some _ as command - -> command - | None when is_clang exe_basename - -> Some CLOpt.Clang - | None - -> None + Filename.basename Sys.argv.(0) + +let infer_is_clang = List.mem ~equal:String.equal clang_exe_aliases exe_basename + +let initial_command = + match CLOpt.command_of_exe_name exe_basename with Some _ as command -> command | None -> None let bin_dir = (* Resolve symlinks to get to the real executable, which is located in [bin_dir]. *) @@ -426,18 +423,12 @@ let startup_action = let open CLOpt in if infer_is_javac then Javac else if !Sys.interactive then NoParse - else - match initial_command with - | Some Clang - -> NoParse - | None | Some (Analyze | Capture | Compile | Diff | Explore | Report | ReportDiff | Run) - -> InferCommand + else if infer_is_clang then NoParse + else InferCommand let exe_usage = let exe_command_name = match initial_command with - | Some CLOpt.Clang - -> None (* users cannot see this *) | Some command -> Some (CLOpt.name_of_command command) | None @@ -480,24 +471,17 @@ let anon_args = CLOpt.mk_anon () let () = let on_unknown_arg_from_command (cmd: CLOpt.command) = match cmd with - | Clang - -> assert false (* filtered out *) | Report -> `Add | Analyze | Capture | Compile | Diff | Explore | ReportDiff | Run -> `Reject in (* make sure we generate doc for all the commands we know about *) - List.filter ~f:(Fn.non CLOpt.(equal_command Clang)) CLOpt.all_commands - |> List.iter ~f:(fun cmd -> - let {CommandDoc.name; command_doc} = CommandDoc.data_of_command cmd in - let on_unknown_arg = on_unknown_arg_from_command cmd in - let deprecated_long = - if CLOpt.(equal_command ReportDiff) cmd then Some "diff" else None - in - CLOpt.mk_subcommand cmd ~name ?deprecated_long ~on_unknown_arg (Some command_doc) ) - -let () = CLOpt.mk_subcommand CLOpt.Clang ~name:"clang" ~on_unknown_arg:`Skip None + List.iter CLOpt.all_commands ~f:(fun cmd -> + let {CommandDoc.name; command_doc} = CommandDoc.data_of_command cmd in + let on_unknown_arg = on_unknown_arg_from_command cmd in + let deprecated_long = if CLOpt.(equal_command ReportDiff) cmd then Some "diff" else None in + CLOpt.mk_subcommand cmd ~name ?deprecated_long ~on_unknown_arg (Some command_doc) ) let abs_struct = CLOpt.mk_int ~deprecated:["absstruct"] ~long:"abs-struct" ~default:1 ~meta:"int" @@ -877,8 +861,7 @@ and ( bo_debug , write_dotty ) = let all_generic_manuals = List.filter_map CLOpt.all_commands ~f:(fun cmd -> - if List.mem ~equal:CLOpt.equal_command CLOpt.([Clang; Explore]) cmd then None - else Some (cmd, manual_generic) ) + if CLOpt.equal_command Explore cmd then None else Some (cmd, manual_generic) ) in let bo_debug = CLOpt.mk_int ~default:0 ~long:"bo-debug" diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 8beb356a2..be1eb74c3 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -424,6 +424,10 @@ val immutable_cast : bool val infer_cache : string option +val infer_is_clang : bool + +val infer_is_javac : bool + val iphoneos_target_sdk_version : string option type iphoneos_target_sdk_version_path_regex = {path: Str.regexp; version: string} @@ -491,8 +495,6 @@ val log_file : string val makefile_cmdline : string -val maven : bool - val max_nesting : int option val merge : bool diff --git a/infer/src/base/Pp.ml b/infer/src/base/Pp.ml index 834d6f136..a5418f6b6 100644 --- a/infer/src/base/Pp.ml +++ b/infer/src/base/Pp.ml @@ -12,6 +12,8 @@ module F = Format (** Pretty Printing} *) +module CLOpt = CommandLineOption + (** Kind of simple printing: default or with full types *) type simple_kind = SIM_DEFAULT | SIM_WITH_TYP @@ -134,4 +136,19 @@ let elapsed_time fmt () = let elapsed = Unix.gettimeofday () -. Utils.initial_timeofday in F.fprintf fmt "%f" elapsed -let to_string ~f fmt x = F.fprintf fmt "%s" (f x) +let string fmt s = F.fprintf fmt "%s" s + +let to_string ~f fmt x = string fmt (f x) + +let pp_argfile fmt fname = + try + F.fprintf fmt " Contents of '%s'@\n" fname ; + In_channel.iter_lines ~f:(F.fprintf fmt " %s@\n") (In_channel.create fname) ; + F.fprintf fmt " /Contents of '%s'@\n" fname + with exn -> F.fprintf fmt " Error reading file '%s':@\n %a@\n" fname Exn.pp exn + +let cli_args fmt args = + F.fprintf fmt "%a@\n%a" + (seq ~sep:(String.of_char CLOpt.env_var_sep) string) + args (seq ~sep:"\n" pp_argfile) + (List.filter_map ~f:(String.chop_prefix ~prefix:"@") args) diff --git a/infer/src/base/Pp.mli b/infer/src/base/Pp.mli index a2c115b6d..74be752b5 100644 --- a/infer/src/base/Pp.mli +++ b/infer/src/base/Pp.mli @@ -68,6 +68,11 @@ val latex : color -> env val color_string : color -> string (** string representation of colors *) +val string : F.formatter -> string -> unit + +val cli_args : F.formatter -> string list -> unit +(** pretty print command line arguments, expanding argument files to print their contents *) + val seq : ?print_env:env -> ?sep:string -> ?sep_html:string -> ?sep_latex:string -> (F.formatter -> 'a -> unit) -> F.formatter -> 'a list -> unit diff --git a/infer/src/integration/Driver.ml b/infer/src/integration/Driver.ml index 5434aac91..31e54cda6 100644 --- a/infer/src/integration/Driver.ml +++ b/infer/src/integration/Driver.ml @@ -83,13 +83,6 @@ type mode = let equal_mode = [%compare.equal : mode] let pp_mode fmt mode = - let log_argfile_arg fname = - try - F.fprintf fmt "-- Contents of '%s'@\n" fname ; - In_channel.iter_lines ~f:(F.fprintf fmt "%s@\n") (In_channel.create fname) ; - F.fprintf fmt "-- /Contents of '%s'@." fname - with exn -> F.fprintf fmt " Error reading file '%s':@\n %a@." fname Exn.pp exn - in match mode with | Analyze | BuckGenrule _ @@ -101,23 +94,11 @@ let pp_mode fmt mode = -> (* these are pretty boring, do not log anything *) () | Javac (_, prog, args) - -> F.fprintf fmt "Javac driver mode:@\nprog = %s@\n" prog ; - let log_arg arg = - F.fprintf fmt "Arg: %s@\n" arg ; - (* "@fname" means that fname is an arg file containing additional arguments to pass to - javac. *) - String.chop_prefix ~prefix:"@" arg - |> (* Sometimes these argfiles go away at the end of the build and we cannot inspect them after - the fact, so log them now. *) - Option.iter ~f:log_argfile_arg - in - List.iter ~f:log_arg args + -> F.fprintf fmt "Javac driver mode:@\nprog = %s@\nargs = %a" prog Pp.cli_args args | Maven (prog, args) - -> F.fprintf fmt "Maven driver mode:@\nprog = %s@\n" prog ; - List.iter ~f:(F.fprintf fmt "Arg: %s@\n") args + -> F.fprintf fmt "Maven driver mode:@\nprog = %s@\nargs = %a" prog Pp.cli_args args | Clang (_, prog, args) - -> F.fprintf fmt "Clang driver mode:@\nprog = %s@\n" prog ; - List.iter ~f:(F.fprintf fmt "Arg: %s@\n") args + -> F.fprintf fmt "Clang driver mode:@\nprog = %s@\nargs = %a" prog Pp.cli_args args (* A clean command for each driver mode to be suggested to the user in case nothing got captured. *) @@ -254,7 +235,8 @@ let capture_with_compilation_database db_files = let compilation_database = CompilationDatabase.from_json_files db_files in CaptureCompilationDatabase.capture_files_in_database compilation_database -let capture ~changed_files = function +let capture ~changed_files mode = + match mode with | Analyze -> () | BuckCompilationDB (prog, args) @@ -264,12 +246,14 @@ let capture ~changed_files = function | BuckGenrule path -> L.progress "Capturing for Buck genrule compatibility...@." ; JMain.from_arguments path | Clang (compiler, prog, args) - -> L.progress "Capturing in make/cc mode...@." ; Clang.capture compiler ~prog ~args + -> if CLOpt.is_originator then L.progress "Capturing in make/cc mode...@." ; + Clang.capture compiler ~prog ~args | ClangCompilationDB db_files -> L.progress "Capturing using compilation database...@." ; capture_with_compilation_database ~changed_files db_files | Javac (compiler, prog, args) - -> L.progress "Capturing in javac mode...@." ; Javac.capture compiler ~prog ~args + -> if CLOpt.is_originator then L.progress "Capturing in javac mode...@." ; + Javac.capture compiler ~prog ~args | Maven (prog, args) -> L.progress "Capturing in maven mode...@." ; Maven.capture ~prog ~args | Python args @@ -424,8 +408,8 @@ let analyze_and_report ?suppress_console_report ~changed_files mode = | PythonCapture (BBuck, _), _ when not Config.flavors -> (* In Buck mode when compilation db is not used, analysis is invoked from capture if buck flavors are not used *) (false, false) - | _ when Config.maven - -> (* Called from Maven, only do capture. *) + | _ when Config.infer_is_clang || Config.infer_is_javac + -> (* Called from another integration to do capture only. *) (false, false) | _, (CaptureOnly | CompileOnly) -> (false, false) @@ -461,15 +445,6 @@ let fail_on_issue_epilogue () = | Error error -> L.internal_error "Failed to read report file '%s': %s@." issues_json error ; () -let log_infer_args mode = - L.environment_info "INFER_ARGS = %s@\n" - (Option.value (Sys.getenv CLOpt.args_env_var) ~default:"") ; - List.iter ~f:(L.environment_info "anon arg: %s@\n") Config.anon_args ; - List.iter ~f:(L.environment_info "rest arg: %s@\n") Config.rest ; - L.environment_info "Project root = %s@\n" Config.project_root ; - L.environment_info "CWD = %s@\n" (Sys.getcwd ()) ; - L.environment_info "Driver mode:@\n%a@." pp_mode mode - let assert_supported_mode required_analyzer requested_mode_string = let analyzer_enabled = match required_analyzer with @@ -560,9 +535,14 @@ let mode_of_build_command build_cmd = let mode_from_command_line = ( lazy ( match Config.generated_classes with - | _ when Config.maven - -> (* infer is pretending to be javac in the Maven integration *) - let build_args = match Array.to_list Sys.argv with _ :: args -> args | [] -> [] in + | _ when Config.infer_is_clang + -> let prog, args = + match Array.to_list Sys.argv with prog :: args -> (prog, args) | [] -> assert false + (* Sys.argv is never empty *) + in + Clang (Clang.Clang, prog, args) + | _ when Config.infer_is_javac + -> let build_args = match Array.to_list Sys.argv with _ :: args -> args | [] -> [] in Javac (Javac.Javac, "javac", build_args) | Some path -> assert_supported_mode `Java "Buck genrule" ; @@ -572,7 +552,8 @@ let mode_from_command_line = let run_prologue mode = if CLOpt.is_originator then L.environment_info "%a@\n" Config.pp_version () ; - if Config.debug_mode || Config.stats_mode then log_infer_args mode ; + if Config.debug_mode || Config.stats_mode then + L.environment_info "Driver mode:@\n%a@." pp_mode mode ; if Config.dump_duplicate_symbols then reset_duplicates_file () ; (* infer might be called from a Makefile and itself uses `make` to run the analysis in parallel, but cannot communicate with the parent make command. Since infer won't interfere with them @@ -580,7 +561,8 @@ let run_prologue mode = mono-threaded execution. *) Unix.unsetenv "MAKEFLAGS" ; register_perf_stats_report () ; - if not Config.buck_cache_mode then touch_start_file_unless_continue () ; + if not Config.buck_cache_mode && not Config.infer_is_clang && not Config.infer_is_javac then + touch_start_file_unless_continue () ; () let run_epilogue mode =