[config] migrate help to use man pages

Summary:
Glorious, glorious man pages.

This changes a bunch of things that were hard to break up from the diff, so the
resulting diff is big.

Use `Cmdliner.Manpage` to format man pages (and also format a bit ourselves
since it is so stubborn).

As a bonus, introduce the following subcommands:

```
infer run ... # same as default mode with -- before
infer capture ... # -a capture
infer compile ... # -a compile
infer analyze ... # InferAnalyze
infer report ... # InferPrint
infer diff ... # this one is not new
infer clang ... # InferClang, not that you should use it
```

The man pages can still be improved a lot. Notable missing sections:
`ENVIRONMENT`, stuff about .inferconfig, some example usage, `DESCRIPTION`, ...

Reviewed By: jberdine

Differential Revision: D4921083

fbshipit-source-id: 9602230
master
Jules Villard 8 years ago committed by Facebook Github Bot
parent ebd5ec0013
commit 709376945f

@ -2,6 +2,7 @@ S src/**
B _build/infer/** B _build/infer/**
PKG ANSITerminal PKG ANSITerminal
PKG atdgen PKG atdgen
PKG cmdliner
PKG core PKG core
PKG javalib PKG javalib
PKG oUnit PKG oUnit

@ -40,7 +40,7 @@ OCAMLBUILD_OPTIONS = \
-tag-line "<*{clang/clang_ast_*,backend/jsonbug_*,checkers/stacktree_*}>: warn(-27-32-34-35-39)" \ -tag-line "<*{clang/clang_ast_*,backend/jsonbug_*,checkers/stacktree_*}>: warn(-27-32-34-35-39)" \
-tag-line "<*/{,*/}*.{ml,re}{,i}>: package(ppx_compare)" \ -tag-line "<*/{,*/}*.{ml,re}{,i}>: package(ppx_compare)" \
-tag thread \ -tag thread \
-pkgs ANSITerminal,atdgen,core,extlib,oUnit,str,unix,xmlm,yojson,zip -pkgs ANSITerminal,atdgen,cmdliner,core,extlib,oUnit,str,unix,xmlm,yojson,zip
ifeq ($(ENABLE_OCAML_BINANNOT),yes) ifeq ($(ENABLE_OCAML_BINANNOT),yes)
OCAMLBUILD_OPTIONS += -cflags -bin-annot OCAMLBUILD_OPTIONS += -cflags -bin-annot

@ -182,7 +182,7 @@ let resolve_infer_eradicate_conflict
let source_file = SourceFile.UNSAFE.from_string issue.file in let source_file = SourceFile.UNSAFE.from_string issue.file in
let filters = filters_of_analyzer Config.Eradicate in let filters = filters_of_analyzer Config.Eradicate in
filters.path_filter source_file in filters.path_filter source_file in
Config.equal_analyzer analyzer Config.Infer && Config.equal_analyzer analyzer Config.BiAbduction &&
String.equal issue.bug_type (Localise.to_issue_id Localise.null_dereference) && String.equal issue.bug_type (Localise.to_issue_id Localise.null_dereference) &&
file_is_whitelisted () in file_is_whitelisted () in
let filter issues = List.filter ~f:(Fn.non should_discard_issue) issues in let filter issues = List.filter ~f:(Fn.non should_discard_issue) issues in
@ -198,7 +198,7 @@ let do_filter
~(skip_duplicated_types: bool): Differential.t = ~(skip_duplicated_types: bool): Differential.t =
if Config.filtering then ( if Config.filtering then (
diff diff
|> (if Config.equal_analyzer Config.analyzer Config.Infer then |> (if Config.equal_analyzer Config.analyzer Config.BiAbduction then
skip_anonymous_class_renamings skip_anonymous_class_renamings
else Fn.id) else Fn.id)
|> (if skip_duplicated_types then |> (if skip_duplicated_types then

@ -28,7 +28,7 @@ let register_perf_stats_report () => {
let () = { let () = {
Logging.set_log_file_identifier Logging.set_log_file_identifier
CommandLineOption.(Infer Analysis) (Option.map f::Filename.basename Config.cluster_cmdline); CommandLineOption.Analyze (Option.map f::Filename.basename Config.cluster_cmdline);
if Config.print_builtins { if Config.print_builtins {
Builtin.print_and_exit () Builtin.print_and_exit ()
}; };

@ -265,11 +265,11 @@ let should_report (issue_kind: Exceptions.err_kind) issue_type error_desc eclass
switch Config.analyzer { switch Config.analyzer {
| Eradicate | Eradicate
| Tracing => true | Tracing => true
| Capture | BiAbduction
| CaptureOnly
| Checkers | Checkers
| Compile | CompileOnly
| Crashcontext | Crashcontext
| Infer
| Linters => false | Linters => false
}; };
if analyzer_is_whitelisted { if analyzer_is_whitelisted {
@ -787,9 +787,7 @@ module Report = {
module Summary = { module Summary = {
let pp_summary_out summary => { let pp_summary_out summary => {
let proc_name = Specs.get_proc_name summary; let proc_name = Specs.get_proc_name summary;
if Config.quiet { if (CLOpt.equal_command Config.command CLOpt.Report && not Config.quiet) {
()
} else {
L.stdout "Procedure: %a@\n%a@." Typ.Procname.pp proc_name Specs.pp_summary_text summary L.stdout "Procedure: %a@\n%a@." Typ.Procname.pp proc_name Specs.pp_summary_text summary
} }
}; };
@ -1313,3 +1311,5 @@ let main ::report_csv ::report_json => {
}; };
print_issues formats_by_report_kind print_issues formats_by_report_kind
}; };
let main_from_config () => main report_csv::Config.bugs_csv report_json::Config.bugs_json;

@ -9,3 +9,5 @@
open! IStd; open! IStd;
let main: report_csv::option string => report_json::option string => unit; let main: report_csv::option string => report_json::option string => unit;
let main_from_config: unit => unit;

@ -8,4 +8,4 @@
*/ */
open! IStd; open! IStd;
let () = InferPrint.main report_csv::Config.bugs_csv report_json::Config.bugs_json; let () = InferPrint.main_from_config ();

@ -24,8 +24,8 @@ let pp_prolog fmt clusters =
let compilation_dbs_cmd = let compilation_dbs_cmd =
List.map ~f:infer_flag_of_compilation_db !Config.clang_compilation_dbs List.map ~f:infer_flag_of_compilation_db !Config.clang_compilation_dbs
|> String.concat ~sep:" " |> escape in |> String.concat ~sep:" " |> escape in
F.fprintf fmt "INFERANALYZE= %s --results-dir '%s' %s \n@." F.fprintf fmt "INFERANALYZE = '%s' --results-dir '%s' %s@\n@\n"
(Config.bin_dir ^/ (Config.exe_name Analyze)) (Config.(bin_dir ^/ infer_analyze_exe_name))
(escape Config.results_dir) (escape Config.results_dir)
compilation_dbs_cmd; compilation_dbs_cmd;
F.fprintf fmt "CLUSTERS="; F.fprintf fmt "CLUSTERS=";
@ -35,8 +35,8 @@ let pp_prolog fmt clusters =
F.fprintf fmt "%a " Cluster.pp_cluster_name (i+1)) F.fprintf fmt "%a " Cluster.pp_cluster_name (i+1))
clusters; clusters;
F.fprintf fmt "@.@.default: test@.@.all: test@.@."; F.fprintf fmt "@\n@\ndefault: test@\n@\nall: test@\n@\n";
F.fprintf fmt "test: $(CLUSTERS)@."; F.fprintf fmt "test: $(CLUSTERS)@\n";
if Config.show_progress_bar then F.fprintf fmt "\t@@echo@\n@." if Config.show_progress_bar then F.fprintf fmt "\t@@echo@\n@."
let pp_epilog fmt () = let pp_epilog fmt () =

@ -373,9 +373,9 @@ let analyze driver_mode =
| _ when Config.maven -> | _ when Config.maven ->
(* Called from Maven, only do capture. *) (* Called from Maven, only do capture. *)
false, false false, false
| _, (Capture | Compile) -> | _, (CaptureOnly | CompileOnly) ->
false, false false, false
| _, (Infer | Eradicate | Checkers | Tracing | Crashcontext) -> | _, (BiAbduction | Checkers | Crashcontext | Eradicate | Tracing) ->
true, true true, true
| _, Linters -> | _, Linters ->
false, true in false, true in
@ -493,7 +493,7 @@ let infer_mode () =
remove_results_dir () ; remove_results_dir () ;
create_results_dir () ; create_results_dir () ;
(* re-set log files, as default files were in results_dir removed above *) (* re-set log files, as default files were in results_dir removed above *)
if not Config.buck_cache_mode then L.set_log_file_identifier CLOpt.(Infer Driver) None; if not Config.buck_cache_mode then L.set_log_file_identifier CLOpt.Run None;
if Config.print_builtins then Builtin.print_and_exit () ; if Config.print_builtins then Builtin.print_and_exit () ;
if CLOpt.is_originator then L.do_out "%s@\n" Config.version_string ; if CLOpt.is_originator then L.do_out "%s@\n" Config.version_string ;
if Config.debug_mode || Config.stats_mode then log_infer_args driver_mode ; if Config.debug_mode || Config.stats_mode then log_infer_args driver_mode ;
@ -545,6 +545,7 @@ let differential_mode () =
Differential.to_files diff out_path Differential.to_files diff out_path
let () = let () =
match Config.final_parse_action with match Config.command with
| Differential -> differential_mode () | Report -> InferPrint.main_from_config ()
| ReportDiff -> differential_mode ()
| _ -> infer_mode () | _ -> infer_mode ()

@ -0,0 +1,272 @@
(*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
module CLOpt = CommandLineOption
type data = { long: string; command_doc: CLOpt.command_doc }
let inferconfig_env_var = "INFERCONFIG"
(** Name of the infer configuration file *)
let inferconfig_file = ".inferconfig"
let command_to_long = CLOpt.[
Analyze, "analyze"; Capture, "capture"; Clang, "clang"; Compile, "compile"; Report, "report";
ReportDiff, "reportdiff"; Run, "run";
]
let long_of_command =
List.Assoc.find_exn ~equal:CLOpt.equal_command command_to_long
let exe_name_of_command cmd =
let long = long_of_command cmd in
(* InferClang is special because it's not really a user-facing executable *)
if CLOpt.equal_command cmd CLOpt.Clang then "InferClang"
else "infer-" ^ long
let mk_command_doc ~see_also:see_also_commands ?and_also ?environment:environment_opt
?files:files_opt ~synopsis =
let section = 1 in
let see_also =
let exe_names = List.map see_also_commands ~f:(fun cmd ->
let exe = exe_name_of_command cmd in
Printf.sprintf "$(b,%s)(%d)" (Cmdliner.Manpage.escape exe) section) in
let suffix = match and_also with None -> "" | Some msg -> " " ^ msg in
[`P (String.concat ~sep:", " exe_names ^ suffix)] in
let environment = Option.value environment_opt
~default:[`I (Printf.sprintf "$(b,%s), $(b,%s), $(b,%s)"
CLOpt.args_env_var inferconfig_env_var CLOpt.strict_mode_env_var,
Printf.sprintf "See the %s section in the manual of $(b,infer)(%d)."
Cmdliner.Manpage.s_environment section)] in
let files = Option.value files_opt
~default:[`I (Printf.sprintf "$(b,%s)" inferconfig_file,
Printf.sprintf "See the %s section in the manual of $(b,infer)(%d)."
Cmdliner.Manpage.s_files section)] in
CLOpt.mk_command_doc ~section ~version:Version.versionString
~date:Version.man_pages_last_modify_date ~synopsis:[`Pre synopsis] ~environment ~files ~see_also
let analyze =
mk_command_doc ~title:"Infer Analysis"
~short_description:"analyze the files captured by infer"
~synopsis:"$(b,infer) $(b,analyze) $(i,[options])\n\
$(b,infer) $(i,[options])"
~description:[`P "Analyze the files captured in the project results directory and report."]
~see_also:CLOpt.[Report; Run]
let capture =
mk_command_doc ~title:"Infer Compilation Capture"
~short_description:"capture source files for later analysis"
~synopsis:"$(b,infer) $(b,capture) $(i,[options]) $(b,--) $(b,buck) $(i,...)\n\
$(b,infer) $(b,capture) $(b,--flavors) $(i,[options]) $(b,--) $(b,buck) $(i,...)\n\
$(b,infer) $(b,capture) $(b,--buck-compilation-database) $(i,[no-]deps) \
$(i,[options]) $(b,--) $(b,buck) $(i,...)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--compilation-database) $(i,file)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--compilation-database-escaped) \
$(i,file)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--) $(b,gradle)/$(b,gradlew) \
$(i,...)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--) $(b,javac) $(i,...)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--) $(b,make)/$(b,clang)/$(b,gcc) \
$(i,...)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--) $(b,mvn)/$(b,mvnw) \
$(i,...)\n\
$(b,infer) $(b,capture) $(i,[options]) $(b,--) $(b,ndk-build) $(i,...)\n\
$(b,infer) $(b,capture) $(i,[--no-xcpretty]) $(i,[options]) $(b,--) \
$(b,xcodebuild) $(i,...)"
~description:[
`P "Capture the build command or compilation database specified on the command line: infer \
intercepts calls to the compiler to read source files, translate them into infer's \
intermediate representation, and store the result of the translation in the results \
directory.";
]
~see_also:CLOpt.[Analyze;Compile;Run]
let clang =
mk_command_doc ~title:"Infer Clang Capture"
~short_description:"internal tool to capture clang commands"
~synopsis:"$(b,InferClang) $(i,[clang options])"
~description:[`P "This is used internally by other infer commands. You shouldn't need to call \
this directly."]
~options:[`P "Accepts the same command line options as $(b,clang)(1) (but still reads infer \
options from the environment)."]
~see_also:CLOpt.[Capture]
let compile =
mk_command_doc ~title:"Infer Project Compilation"
~short_description:"compile project from within the infer environment"
~synopsis:"$(b,infer) $(b,compile) $(b,--) $(i,[compile command])"
~description:[
`P "Intercepts compilation commands similarly to $(b,infer-capture), but simply execute \
these compilation commands and do not perform any translation of the source files. This \
can be useful to configure build systems or for debugging purposes.";
]
~examples:[
`P "$(b,cmake)(1) hardcodes the absolute paths to the compiler inside the Makefiles it \
generates, which defeats the later capture of compilation commands by infer. Thus, to \
capture a CMake project, one should configure the project from within the infer build \
environment, for instance:";
`Pre " \
mkdir build && cd build\n \
infer compile -- cmake ..\n \
infer capture -- make";
`P "The same solution can be used for projects whose \"./configure\" script hardcodes the \
paths to the compilers, for instance:";
`Pre " \
infer compile -- ./configure\n \
infer capture -- make";
`P "Another solution for CMake projects is to use CMake's compilation databases, for \
instance:";
`Pre " \
mkdir build && cd build\n \
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=1 ..\n \
infer capture --compilation-database compile_commands.json";
]
~see_also:CLOpt.[Capture]
let infer = mk_command_doc ~title:"Infer Static Analyzer"
~short_description:"static analysis for Java and C/C++/Objective-C/Objective-C++"
~synopsis:"$(b,infer) $(b,analyze) $(i,[options])\n\
$(b,infer) $(b,capture) $(i,[options])\n\
$(b,infer) $(b,compile) $(i,[options])\n\
$(b,infer) $(b,report) $(i,[options])\n\
$(b,infer) $(b,reportdiff) $(i,[options])\n\
$(b,infer) $(b,run) $(i,[options])\n\
$(b,infer) $(b,--compilation-database[-escaped]) $(i,file) $(i,[options])\n\
$(b,infer) $(i,[options]) $(b,--) $(b,compile command)\n\
$(b,infer) $(i,[options])"
~description:[
`P "Infer is a static analyzer. Given a collection of source files written in Java or in \
languages of the C family, and a command to build them, infer produces a list of \
potential issues.";
`P "Infer consists of a collection of tools referenced in the $(i,SEE ALSO) section of this \
manual. See their respective manuals for more information about each.";
]
~options:[
`P "If a compilation command is specified via the $(b,--) option or one of the \
$(b,--clang-compilation-database[-escaped]) options, $(b,infer) behaves as \
$(b,infer-run)(1). Otherwise, $(b,infer) behaves as $(b,infer-analyze)(1).";
`P "See the manuals of individual infer commands for details about their supported options.";
`P "Every infer command accepts the arguments from all the other infer commands. The same \
option may affect and thus be list in the manual of several commands.";
`P (Printf.sprintf
"Options are read from the $(b,%s) file, then from the $(b,%s) environment variable, \
then from the command line. Options in $(b,%s) take precedence over options in \
$(b,%s), and options passed on the command line take precedence over options in \
$(b,%s). See the $(i,%s) and $(i,%s) sections of this manual for more information."
inferconfig_file CLOpt.args_env_var
CLOpt.args_env_var
inferconfig_file
CLOpt.args_env_var Cmdliner.Manpage.s_environment Cmdliner.Manpage.s_files
);
]
~environment:[
`P (Printf.sprintf
"Extra arguments may be passed to all infer commands using the $(b,%s) \
environment variable (see the $(i,%s) section). $(b,%s) is expected to contain a \
string of %c-separated options. For instance, calling `%s=--debug^--print-logs infer` \
is equivalent to calling `infer --debug --print-logs`."
CLOpt.args_env_var
Cmdliner.Manpage.s_options CLOpt.args_env_var
CLOpt.env_var_sep CLOpt.args_env_var
);
`P (Printf.sprintf
"$(b,%s): Tells infer where to find the %s file. (See the %s section)"
inferconfig_env_var inferconfig_file Cmdliner.Manpage.s_files
);
`P (Printf.sprintf
"If $(b,%s) is set to \"1\", then infer commands will exit with an error code in some \
cases when otherwise a simple warning would be emitted on stderr, for instance if a \
deprecated form of an option is used."
CLOpt.strict_mode_env_var
);
]
~files:[
`P (Printf.sprintf
"$(b,%s) can be used to store infer options. Its format is that of a JSON \
record, where fields are infer long-form options, without their leading \"--\", and \
values depend on the type of the option:" inferconfig_file);
`Noblank;
`P "- for switches options, the value is a JSON boolean (true or false, without quotes)";
`Noblank;
`P "- for integers, the value is a JSON integer (without quotes)";
`Noblank;
`P "- string options have string values";
`Noblank;
`P (Printf.sprintf "- path options have string values, and are interpreted relative to the \
location of the %s file" inferconfig_file);
`Noblank;
`P "- cumulative options are JSON arrays of the appropriate type";
`P (Printf.sprintf "Infer will look for an $(b,%s) file in the current directory, then its \
parent, etc., stopping at the first $(b,%s) file found."
inferconfig_file inferconfig_file);
`P "Example:";
`Pre " {\
\n \"cxx\": false,\
\n \"infer-blacklist-files-containing\": [\"@generated\",\"@Generated\"]\
\n }";
]
~see_also:(List.filter ~f:(function | CLOpt.Clang -> false | _ -> true) CLOpt.all_commands)
~and_also:", $(b,inferTraceBugs)"
"infer"
let report =
mk_command_doc ~title:"Infer Reporting"
~short_description:"compute and manipulate infer results"
~synopsis:"$(b,infer) $(b,report) $(i,[options]) [$(i,file.specs)...]"
~description:[
`P "Read, convert, and print .specs files in the results directory. Each spec is printed to \
standard output unless option -q is used.";
`P "If no specs file are passed on the command line, process all the .specs in the results \
directory.";
]
~see_also:CLOpt.[ReportDiff; Run]
let reportdiff =
mk_command_doc ~title:"Infer Report Difference"
~short_description:"compute the differences between two infer reports"
~synopsis:"$(b,infer) $(b,reportdiff) $(b,--report-current) $(i,file) \
$(b,--report-previous) $(i,file) $(i,[options])"
~description:[
`P "Given two infer reports $(i,previous) and $(i,current), compute the following three \
reports and store them inside the \"differential/\" subdirectory of the results \
directory:";
`Noblank; `P "- $(b,introduced.json) contains the issues found in $(i,current) but not \
$(i,previous);";
`Noblank; `P "- $(b,fixed.json) contains the issues found in $(i,previous) but not \
$(i,current);";
`Noblank; `P "- $(b,preexisting.json) contains the issues found in both $(i,previous) and \
$(i,current).";
`P "All three files follow the same format as normal infer reports.";
]
~see_also:CLOpt.[Report]
let run =
mk_command_doc ~title:"Infer Analysis of a Project"
~short_description:"capture source files, analyze, and report"
~synopsis:"$(b,infer) $(b,run) $(i,[options])\n\
$(b,infer) $(i,[options]) $(b,--) $(i,compile command)"
~description:[
`P "Calling \"$(b,infer) $(b,run) $(i,[options])\" is equivalent to performing the following \
sequence of commands:";
`Pre "$(b,infer) $(b,capture) $(i,[options])\n\
$(b,infer) $(b,analyze) $(i,[options])";
]
~see_also:CLOpt.[Analyze; Capture; Report]
let command_to_data =
let mk cmd mk_doc =
let long = long_of_command cmd in
let command_doc = mk_doc (exe_name_of_command cmd) in
cmd, { long; command_doc } in
CLOpt.[mk Analyze analyze; mk Capture capture; mk Clang clang; mk Compile compile;
mk Report report; mk ReportDiff reportdiff; mk Run run]
let data_of_command command =
List.Assoc.find_exn ~equal:CLOpt.equal_command command_to_data command

@ -0,0 +1,19 @@
(*
* Copyright (c) 2017 - present Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*)
open! IStd
module CLOpt = CommandLineOption
type data = { long: string; command_doc: CLOpt.command_doc }
val inferconfig_env_var : string
val inferconfig_file : string
val infer : CLOpt.command_doc
val data_of_command : CLOpt.command -> data

@ -16,6 +16,8 @@ module YBU = Yojson.Basic.Util
let (=) = String.equal let (=) = String.equal
let manpage_s_notes = "NOTES"
let is_env_var_set v = let is_env_var_set v =
Option.value (Option.map (Sys.getenv v) ~f:((=) "1")) ~default:false Option.value (Option.map (Sys.getenv v) ~f:((=) "1")) ~default:false
@ -30,7 +32,9 @@ let init_work_dir, is_originator =
Unix.putenv ~key:"INFER_CWD" ~data:real_cwd; Unix.putenv ~key:"INFER_CWD" ~data:real_cwd;
(real_cwd, true) (real_cwd, true)
let strict_mode = is_env_var_set "INFER_STRICT_MODE" let strict_mode_env_var = "INFER_STRICT_MODE"
let strict_mode = is_env_var_set strict_mode_env_var
let warnf = let warnf =
if strict_mode then failwithf if strict_mode then failwithf
@ -55,42 +59,33 @@ let to_arg_spec = function
let to_arg_spec_triple (x, spec, y) = (x, to_arg_spec spec, y) let to_arg_spec_triple (x, spec, y) = (x, to_arg_spec spec, y)
let to_arg_speclist = List.map ~f:to_arg_spec_triple let to_arg_speclist = List.map ~f:to_arg_spec_triple
type section = (* NOTE: All variants must be also added to `all_parse_modes` below *)
Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java | Print type parse_mode = InferCommand | Javac | NoParse [@@deriving compare]
[@@deriving compare] let equal_parse_mode = [%compare.equal : parse_mode]
let equal_section = [%compare.equal : section ]
let all_sections =
[ Analysis; BufferOverrun; Checkers; Clang; Crashcontext; Driver; Java; Print ]
(* NOTE: All variants must be also added to `all_parse_tags` below *) let all_parse_modes = [InferCommand; Javac; NoParse]
type 'a parse = Differential | Infer of 'a | Javac | NoParse [@@deriving compare]
type parse_mode = section list parse [@@deriving compare] let accept_unknown_args = function
| Javac | NoParse -> true
type parse_action = section parse [@@deriving compare] | InferCommand -> false
let equal_parse_action = [%compare.equal : parse_action ] (* NOTE: All variants must be also added to `all_commands` below *)
type command =
| Analyze | Capture | Clang | Compile | Report | ReportDiff | Run
[@@deriving compare]
(* NOTE: All variants must be also added to `all_parse_tags` below *) let equal_command = [%compare.equal : command]
type parse_tag = AllInferTags | OneTag of unit parse [@@deriving compare]
let equal_parse_tag = [%compare.equal : parse_tag ] let all_commands = [
let all_parse_tags = [ Analyze; Capture; Clang; Compile; Report; ReportDiff; Run
AllInferTags; OneTag Differential; OneTag (Infer ()); OneTag Javac; OneTag NoParse
] ]
let to_parse_tag parse = type command_doc = {
match parse with title : Cmdliner.Manpage.title;
| Differential -> OneTag Differential manual_pre_options : Cmdliner.Manpage.block list;
| Infer _ -> OneTag (Infer ()) manual_options : Cmdliner.Manpage.block list option;
| Javac -> OneTag Javac manual_post_options : Cmdliner.Manpage.block list;
| NoParse -> OneTag NoParse }
let accept_unknown_args = function
| Infer Print | Javac | NoParse -> true
| Infer (Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java)
| Differential -> false
type desc = { type desc = {
long: string; short: string; meta: string; doc: string; spec: spec; long: string; short: string; meta: string; doc: string; spec: spec;
@ -103,27 +98,7 @@ let dashdash long =
| "" | "--" -> long | "" | "--" -> long
| _ -> "--" ^ long | _ -> "--" ^ long
let short_meta {short; meta; spec} = let xdesc {long; short; spec} =
String.concat ~sep:" "
((if short = "" then [] else ["| -" ^ short]) @
(match spec with
| Symbol (symbols, _) ->
["{ " ^ (String.concat ~sep:" | " symbols) ^ " }" ^ meta]
| _ ->
if meta = "" then [] else ["<" ^ meta ^ ">"]))
let left_length long short_meta =
(String.length (dashdash long)) + (String.length short_meta)
let max_left_length limit current ({long; spec} as desc) =
let short_meta =
match spec with
| Symbol _ -> short_meta {desc with spec = Unit (fun () -> ())}
| _ -> short_meta desc in
let length = left_length long short_meta in
if length > limit then current else max current length
let xdesc {long; short; spec; doc} =
let key long short = let key long short =
match long, short with match long, short with
| "", "" -> "" | "", "" -> ""
@ -145,86 +120,8 @@ let xdesc {long; short; spec; doc} =
| _ -> | _ ->
spec spec
in in
(key long short, xspec long spec, doc) (* Arg doesn't need to know anything about documentation since we generate our own *)
(key long short, xspec long spec, "")
let wrap_line indent_string wrap_length line =
let indent_length = String.length indent_string in
let word_sep = ' ' in
let words = String.split ~on:word_sep line in
let word_sep_str = String.of_char word_sep in
let add_word_to_paragraph (rev_lines, non_empty, line, line_length) word =
let word_length = String.length word in
let new_length = line_length + (String.length word_sep_str) + word_length in
let new_non_empty = non_empty || word <> "" in
if new_length > wrap_length && non_empty then
(line::rev_lines, true, indent_string ^ word, indent_length + word_length)
else
let sep = if Int.equal line_length indent_length then "" else word_sep_str in
let new_line = line ^ sep ^ word in
if new_length > wrap_length && new_non_empty then
(new_line::rev_lines, false, indent_string, indent_length)
else
(rev_lines, new_non_empty, new_line, String.length new_line) in
let (rev_lines, _, line, _) =
List.fold ~f:add_word_to_paragraph ~init:([], false, "", 0) words in
List.rev (line::rev_lines)
let pad_and_xform doc_width left_width desc =
match desc with
| {doc = ""} ->
xdesc desc
| {long; doc} ->
let indent_doc doc =
(* 2 blank columns before option + 2 columns of gap between flag and doc *)
let left_indent = 4 + left_width in
let newline_padding = "\n" ^ String.make left_indent ' ' in
(* align every line after the first one of [doc] *)
let doc = String.concat_map doc ~f:(function
| '\n' -> newline_padding
| c -> String.of_char c) in
(* align the first line of [doc] *)
let short_meta = short_meta desc in
let gap = left_width - (left_length long short_meta) in
if gap < 0 then
short_meta ^ "\n" ^ (String.make left_indent ' ') ^ doc
else
short_meta ^ (String.make (gap + 1) ' ') ^ doc
in
let wrapped_lines =
let lines = String.split ~on:'\n' doc in
let wrap_line s =
if String.length s > doc_width then
wrap_line "" doc_width s
else [s] in
List.map ~f:wrap_line lines in
let doc = indent_doc (String.concat ~sep:"\n" (List.concat wrapped_lines)) in
xdesc {desc with doc}
let align desc_list =
let min_term_width = 80 in
let terminal_width = min_term_width in
(* 2 blank columns before option + 2 columns of gap between flag and doc *)
let extra_space = 4 in
let min_left_width = 15 in
let max_left_width = 49 in
let doc_width term_width left_width = term_width - extra_space - left_width in
let term_width doc_width left_width = left_width + extra_space + doc_width in
let max_doc_width = 100 in
let max_term_width = term_width max_left_width max_doc_width in
(* how many columns to reserve for the option names
NOTE: this doesn't take into account "--help | -h" nor "--help-full", but fortunately these
have short names *)
let left_width =
let opt_left_width =
List.fold ~f:(max_left_length max_left_width) ~init:0 desc_list in
let (--) a b = float_of_int a -. float_of_int b in
let multiplier = (max_left_width -- min_left_width) /. (max_term_width -- terminal_width) in
(* at 80 columns use min_left_width then use extra columns until opt_left_width *)
let cols_after_min_width = float_of_int (max 0 (terminal_width - min_term_width)) in
min (int_of_float (cols_after_min_width *. multiplier) + min_left_width) opt_left_width in
let doc_width = min max_doc_width (doc_width terminal_width left_width) in
(List.map ~f:(pad_and_xform doc_width left_width) desc_list, (doc_width, left_width))
let check_no_duplicates desc_list = let check_no_duplicates desc_list =
let rec check_for_duplicates_ = function let rec check_for_duplicates_ = function
@ -238,35 +135,51 @@ let check_no_duplicates desc_list =
check_for_duplicates_ (List.sort ~cmp:(fun (x, _, _) (y, _, _) -> String.compare x y) desc_list) check_for_duplicates_ (List.sort ~cmp:(fun (x, _, _) (y, _, _) -> String.compare x y) desc_list)
let parse_tag_desc_lists = List.map ~f:(fun parse_tag -> (parse_tag, ref [])) all_parse_tags let parse_mode_desc_lists = List.map ~f:(fun parse_mode -> (parse_mode, ref [])) all_parse_modes
module SectionMap = Caml.Map.Make (struct
type t = String.t
(* this must be the reverse of the order in which we want the sections to appear in the
manual *)
let compare s1 s2 =
if String.equal s1 s2 then
(* this simplifies the next two cases *)
0
else if String.equal s1 Cmdliner.Manpage.s_options then
(* ensure OPTIONS section is last (hence first in the manual) *)
1
else if String.equal s2 Cmdliner.Manpage.s_options then
(* same as above *)
-1
else
(* reverse order *)
String.compare s2 s1
end)
let infer_section_desc_lists = List.map ~f:(fun section -> (section, ref [])) all_sections let help_sections_desc_lists =
List.map all_commands ~f:(fun command -> (command, ref SectionMap.empty))
let hidden_descs_list = ref []
(** add [desc] to the one relevant parse_tag_desc_lists for the purposes of parsing, and, in the (** add [desc] to the one relevant parse_tag_desc_lists for the purposes of parsing, and, in the
case of Infer, include [desc] in --help only for the relevant sections. *) case of InferCommand, include [desc] in --help only for the relevant sections. *)
let add parse_mode desc = let add parse_mode sections desc =
let add_to_tag tag = let desc_list = List.Assoc.find_exn parse_mode_desc_lists parse_mode in
let desc_list = List.Assoc.find_exn parse_tag_desc_lists tag in desc_list := desc :: !desc_list;
desc_list := desc :: !desc_list in let add_to_section (command, section) =
(match parse_mode with let sections = List.Assoc.find_exn ~equal:equal_command help_sections_desc_lists command in
| Javac | NoParse -> () let prev_contents =
| Differential | Infer _ -> add_to_tag AllInferTags try SectionMap.find section !sections
); with Not_found -> [] in
add_to_tag (to_parse_tag parse_mode); sections := SectionMap.add section (desc::prev_contents) !sections in
match parse_mode with List.iter sections ~f:add_to_section;
| Differential | Javac | NoParse -> () if List.is_empty sections then
| Infer sections -> hidden_descs_list := desc :: !hidden_descs_list;
List.iter infer_section_desc_lists ~f:(fun (section, desc_list) -> ()
let desc = if List.mem ~equal:equal_section sections section then
desc
else
{desc with meta = ""; doc = ""} in
desc_list := desc :: !desc_list)
let deprecate_desc parse_mode ~long ~short ~deprecated desc = let deprecate_desc parse_mode ~long ~short ~deprecated desc =
let warn () = match parse_mode with let warn () = match parse_mode with
| Javac | NoParse -> () | Javac | NoParse -> ()
| Differential | Infer _ -> | InferCommand ->
warnf "WARNING: '-%s' is deprecated. Use '--%s'%s instead.@." warnf "WARNING: '-%s' is deprecated. Use '--%s'%s instead.@."
deprecated long (if short = "" then "" else Printf.sprintf " or '-%s'" short) in deprecated long (if short = "" then "" else Printf.sprintf " or '-%s'" short) in
let warn_then_f f x = warn (); f x in let warn_then_f f x = warn (); f x in
@ -281,7 +194,7 @@ let deprecate_desc parse_mode ~long ~short ~deprecated desc =
{ long = ""; short = deprecated; meta = ""; doc = ""; { long = ""; short = deprecated; meta = ""; doc = "";
spec = deprecated_spec; decode_json = deprecated_decode_json } spec = deprecated_spec; decode_json = deprecated_decode_json }
let mk ?(deprecated=[]) ?(parse_mode=Infer []) let mk ?(deprecated=[]) ?(parse_mode=InferCommand) ?(in_help=[])
~long ?short:short0 ~default ~meta doc ~default_to_string ~decode_json ~mk_setter ~mk_spec = ~long ?short:short0 ~default ~meta doc ~default_to_string ~decode_json ~mk_setter ~mk_spec =
let variable = ref default in let variable = ref default in
let closure = mk_setter variable in let closure = mk_setter variable in
@ -294,21 +207,20 @@ let mk ?(deprecated=[]) ?(parse_mode=Infer [])
let doc = let doc =
let default_string = default_to_string default in let default_string = default_to_string default in
if default_string = "" then doc if default_string = "" then doc
else doc ^ " (default: " ^ default_string ^ ")" in else
let doc_default_sep = if String.is_suffix ~suffix:"\n" doc then "" else " " in
doc ^ doc_default_sep ^ "(default: $(i," ^ Cmdliner.Manpage.escape default_string ^ "))" in
let short = match short0 with Some c -> String.of_char c | None -> "" in let short = match short0 with Some c -> String.of_char c | None -> "" in
let desc = {long; short=short; meta; doc; spec; decode_json} in let desc = {long; short=short; meta; doc; spec; decode_json} in
(* add desc for long option, with documentation (which includes any short option) for exes *) (* add desc for long option, with documentation (which includes any short option) for exes *)
if long <> "" then add parse_mode desc ; if long <> "" then add parse_mode in_help desc ;
(* add desc for short option only for parsing, without documentation *) (* add desc for short option only for parsing, without documentation *)
let parse_mode_no_sections = match parse_mode with
| Infer _ -> Infer []
| Differential | Javac | NoParse -> parse_mode in
if short <> "" then if short <> "" then
add parse_mode_no_sections {desc with long = ""; meta = ""; doc = ""} ; add parse_mode [] {desc with long = ""; meta = ""; doc = ""} ;
(* add desc for deprecated options only for parsing, without documentation *) (* add desc for deprecated options only for parsing, without documentation *)
List.iter deprecated ~f:(fun deprecated -> List.iter deprecated ~f:(fun deprecated ->
deprecate_desc parse_mode ~long ~short:short ~deprecated desc deprecate_desc parse_mode ~long ~short:short ~deprecated desc
|> add parse_mode_no_sections) ; |> add parse_mode []) ;
variable variable
(* begin parsing state *) (* begin parsing state *)
@ -322,20 +234,21 @@ let arg_being_parsed : int ref = ref 0
(* list of arg specifications currently being used by Arg.parse_argv_dynamic *) (* list of arg specifications currently being used by Arg.parse_argv_dynamic *)
let curr_speclist : (Arg.key * Arg.spec * Arg.doc) list ref = ref [] let curr_speclist : (Arg.key * Arg.spec * Arg.doc) list ref = ref []
let unknown_args_action = ref `Reject let unknown_args_action = ref `ParseCommands
let subcommands = ref []
let subcommand_actions = ref [] let subcommand_actions = ref []
let rev_anon_args = ref [] let rev_anon_args = ref []
(* keep track of the final parse action to drive the remainder of the program *) (* keep track of the current active command to drive the remainder of the program *)
let final_parse_action = ref (Infer Driver) let curr_command = ref None
(* end parsing state *) (* end parsing state *)
type 'a t = type 'a t =
?deprecated:string list -> long:Arg.key -> ?short:char -> ?deprecated:string list -> long:Arg.key -> ?short:char ->
?parse_mode:parse_mode -> ?meta:string -> Arg.doc -> ?parse_mode:parse_mode -> ?in_help:(command * string) list -> ?meta:string -> Arg.doc ->
'a 'a
let string_json_decoder ~long ~inferconfig_dir:_ json = let string_json_decoder ~long ~inferconfig_dir:_ json =
@ -351,25 +264,25 @@ let path_json_decoder ~long ~inferconfig_dir json =
let list_json_decoder json_decoder ~inferconfig_dir json = let list_json_decoder json_decoder ~inferconfig_dir json =
List.concat (YBU.convert_each (json_decoder ~inferconfig_dir) json) List.concat (YBU.convert_each (json_decoder ~inferconfig_dir) json)
let mk_set var value ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_set var value ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="") doc =
let setter () = var := value in let setter () = var := value in
ignore( ignore(
mk ~deprecated ~long ?short ~default:() ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default:() ?parse_mode ?in_help ~meta doc
~default_to_string:(fun () -> "") ~default_to_string:(fun () -> "")
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_setter:(fun _ _ -> setter ()) ~mk_setter:(fun _ _ -> setter ())
~mk_spec:(fun _ -> Unit setter) ) ~mk_spec:(fun _ -> Unit setter) )
let mk_option ?(default=None) ?(default_to_string=fun _ -> "") ~f let mk_option ?(default=None) ?(default_to_string=fun _ -> "") ~f
?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="string") doc =
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~default_to_string ~default_to_string
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_setter:(fun var str -> var := f str) ~mk_setter:(fun var str -> var := f str)
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b) let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b)
?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="") doc =
let nolong = let nolong =
let len = String.length long in let len = String.length long in
if len > 3 && String.sub long ~pos:0 ~len:3 = "no-" then if len > 3 && String.sub long ~pos:0 ~len:3 = "no-" then
@ -384,8 +297,8 @@ let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b)
in in
let doc long short = let doc long short =
match short with match short with
| Some short -> doc ^ " (Conversely: --" ^ long ^ " | -" ^ String.of_char short ^ ")" | Some short -> doc ^ " (Conversely: $(b,--" ^ long ^ ") | $(b,-" ^ String.of_char short ^ "))"
| None -> doc ^ " (Conversely: --" ^ long ^ ")" | None -> doc ^ " (Conversely: $(b,--" ^ long ^ "))"
in in
let doc, nodoc = let doc, nodoc =
if not default then if not default then
@ -395,13 +308,14 @@ let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b)
let default_to_string _ = "" in let default_to_string _ = "" in
let mk_spec set = Unit (fun () -> set "") in let mk_spec set = Unit (fun () -> set "") in
let var = let var =
mk ~long ?short ~deprecated ~default ?parse_mode mk ~long ?short ~deprecated ~default ?parse_mode ?in_help
~meta doc ~default_to_string ~mk_setter:(fun var _ -> var := f true) ~meta doc ~default_to_string ~mk_setter:(fun var _ -> var := f true)
~decode_json:(fun ~inferconfig_dir:_ json -> ~decode_json:(fun ~inferconfig_dir:_ json ->
[dashdash (if YBU.to_bool json then long else nolong)]) [dashdash (if YBU.to_bool json then long else nolong)])
~mk_spec in ~mk_spec in
ignore( ignore(
mk ~long:nolong ?short:noshort ~deprecated:deprecated_no ~default:(not default) ?parse_mode mk ~long:nolong ?short:noshort ~deprecated:deprecated_no ~default:(not default)
?parse_mode ?in_help
~meta nodoc ~default_to_string ~mk_setter:(fun _ _ -> var := f false) ~meta nodoc ~default_to_string ~mk_setter:(fun _ _ -> var := f false)
~decode_json:(fun ~inferconfig_dir:_ json -> ~decode_json:(fun ~inferconfig_dir:_ json ->
[dashdash (if YBU.to_bool json then nolong else long)]) [dashdash (if YBU.to_bool json then nolong else long)])
@ -409,61 +323,62 @@ let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b)
var var
let mk_bool_group ?(deprecated_no=[]) ?(default=false) let mk_bool_group ?(deprecated_no=[]) ?(default=false)
?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc children no_children = ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?meta doc children no_children =
let f b = let f b =
List.iter ~f:(fun child -> child := b) children ; List.iter ~f:(fun child -> child := b) children ;
List.iter ~f:(fun child -> child := not b) no_children ; List.iter ~f:(fun child -> child := not b) no_children ;
b b
in in
mk_bool ~deprecated ~deprecated_no ~default ~long ?short ~f ?parse_mode ~meta doc mk_bool ~deprecated ~deprecated_no ~default ~long ?short ~f ?parse_mode ?in_help ?meta doc
let mk_int ~default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_int ~default ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="int") doc =
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~default_to_string:string_of_int ~default_to_string:string_of_int
~mk_setter:(fun var str -> var := (int_of_string str)) ~mk_setter:(fun var str -> var := (int_of_string str))
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_int_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_int_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="int") doc =
let default_to_string = function Some f -> string_of_int f | None -> "" in let default_to_string = function Some f -> string_of_int f | None -> "" in
let f s = Some (int_of_string s) in let f s = Some (int_of_string s) in
mk_option ~deprecated ~long ?short ~default ~default_to_string ~f ?parse_mode ~meta doc mk_option ~deprecated ~long ?short ~default ~default_to_string ~f ?parse_mode ?in_help ~meta doc
let mk_float ~default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_float ~default ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="float") doc =
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~default_to_string:string_of_float ~default_to_string:string_of_float
~mk_setter:(fun var str -> var := (float_of_string str)) ~mk_setter:(fun var str -> var := (float_of_string str))
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_float_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_float_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="float") doc =
let default_to_string = function Some f -> string_of_float f | None -> "" in let default_to_string = function Some f -> string_of_float f | None -> "" in
let f s = Some (float_of_string s) in let f s = Some (float_of_string s) in
mk_option ~deprecated ~long ?short ~default ~default_to_string ~f ?parse_mode ~meta doc mk_option ~deprecated ~long ?short ~default ~default_to_string ~f ?parse_mode ?in_help ~meta doc
let mk_string ~default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_string ~default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?parse_mode ?in_help
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc ?(meta="string") doc =
mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~default_to_string:(fun s -> s) ~default_to_string:(fun s -> s)
~mk_setter:(fun var str -> var := f str) ~mk_setter:(fun var str -> var := f str)
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_string_opt ?default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?parse_mode let mk_string_opt ?default ?(f=fun s -> s) ?(deprecated=[]) ~long ?short ?parse_mode ?in_help
?(meta="") doc = ?(meta="string") doc =
let default_to_string = function Some s -> s | None -> "" in let default_to_string = function Some s -> s | None -> "" in
let f s = Some (f s) in let f s = Some (f s) in
mk_option ~deprecated ~long ?short ~default ~default_to_string ~f ?parse_mode ~meta doc mk_option ~deprecated ~long ?short ~default ~default_to_string ~f ?parse_mode ?in_help ~meta doc
let mk_string_list ?(default=[]) ?(f=fun s -> s) let mk_string_list ?(default=[]) ?(f=fun s -> s)
?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="+string") doc =
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~default_to_string:(String.concat ~sep:", ") ~default_to_string:(String.concat ~sep:", ")
~mk_setter:(fun var str -> var := (f str) :: !var) ~mk_setter:(fun var str -> var := (f str) :: !var)
~decode_json:(list_json_decoder (string_json_decoder ~long)) ~decode_json:(list_json_decoder (string_json_decoder ~long))
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_path_helper ~setter ~default_to_string let mk_path_helper ~setter ~default_to_string
~default ~deprecated ~long ~short ~parse_mode ~meta ~decode_json doc = ~default ~deprecated ~long ~short ~parse_mode ~in_help ~meta ~decode_json doc =
let normalize_path_in_args_being_parsed str = let normalize_path_in_args_being_parsed str =
if Filename.is_relative str then ( if Filename.is_relative str then (
(* Replace relative paths with absolute ones on the fly in the args being parsed. This assumes (* Replace relative paths with absolute ones on the fly in the args being parsed. This assumes
@ -476,60 +391,68 @@ let mk_path_helper ~setter ~default_to_string
abs_path abs_path
) else ) else
str in str in
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~decode_json ~default_to_string ~decode_json ~default_to_string
~mk_setter:(fun var str -> ~mk_setter:(fun var str ->
let abs_path = normalize_path_in_args_being_parsed str in let abs_path = normalize_path_in_args_being_parsed str in
setter var abs_path) setter var abs_path)
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_path ~default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") = let mk_path ~default ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="path") =
mk_path_helper mk_path_helper
~setter:(fun var x -> var := x) ~setter:(fun var x -> var := x)
~decode_json:(path_json_decoder ~long) ~decode_json:(path_json_decoder ~long)
~default_to_string:(fun s -> s) ~default_to_string:(fun s -> s)
~default ~deprecated ~long ~short ~parse_mode ~meta ~default ~deprecated ~long ~short ~parse_mode ~in_help ~meta
let mk_path_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") = let mk_path_opt ?default ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="path") =
mk_path_helper mk_path_helper
~setter:(fun var x -> var := Some x) ~setter:(fun var x -> var := Some x)
~decode_json:(path_json_decoder ~long) ~decode_json:(path_json_decoder ~long)
~default_to_string:(function Some s -> s | None -> "") ~default_to_string:(function Some s -> s | None -> "")
~default ~deprecated ~long ~short ~parse_mode ~meta ~default ~deprecated ~long ~short ~parse_mode ~in_help ~meta
let mk_path_list ?(default=[]) ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="path") = let mk_path_list ?(default=[]) ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="+path") =
mk_path_helper mk_path_helper
~setter:(fun var x -> var := x :: !var) ~setter:(fun var x -> var := x :: !var)
~decode_json:(list_json_decoder (path_json_decoder ~long)) ~decode_json:(list_json_decoder (path_json_decoder ~long))
~default_to_string:(String.concat ~sep:", ") ~default_to_string:(String.concat ~sep:", ")
~default ~deprecated ~long ~short ~parse_mode ~meta ~default ~deprecated ~long ~short ~parse_mode ~in_help ~meta
let mk_symbols_meta symbols =
let strings = List.map ~f:fst symbols in
Printf.sprintf "{ %s }" (String.concat ~sep:" | " strings)
let mk_symbol ~default ~symbols ~eq ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_symbol ~default ~symbols ~eq ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?meta doc =
let strings = List.map ~f:fst symbols in let strings = List.map ~f:fst symbols in
let sym_to_str = List.map ~f:(fun (x,y) -> (y,x)) symbols in let sym_to_str = List.map ~f:(fun (x,y) -> (y,x)) symbols in
let of_string str = List.Assoc.find_exn ~equal:String.equal symbols str in let of_string str = List.Assoc.find_exn ~equal:String.equal symbols str in
let to_string sym = List.Assoc.find_exn ~equal:eq sym_to_str sym in let to_string sym = List.Assoc.find_exn ~equal:eq sym_to_str sym in
mk ~deprecated ~long ?short ~default ?parse_mode ~meta doc let meta = Option.value meta ~default:(mk_symbols_meta symbols) in
mk ~deprecated ~long ?short ~default ?parse_mode ?in_help ~meta doc
~default_to_string:(fun s -> to_string s) ~default_to_string:(fun s -> to_string s)
~mk_setter:(fun var str -> var := of_string str) ~mk_setter:(fun var str -> var := of_string str)
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> Symbol (strings, set)) ~mk_spec:(fun set -> Symbol (strings, set))
let mk_symbol_opt ~symbols ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="") doc = let mk_symbol_opt ~symbols ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?meta doc =
let strings = List.map ~f:fst symbols in let strings = List.map ~f:fst symbols in
let of_string str = List.Assoc.find_exn ~equal:String.equal symbols str in let of_string str = List.Assoc.find_exn ~equal:String.equal symbols str in
mk ~deprecated ~long ?short ~default:None ?parse_mode ~meta doc let meta = Option.value meta ~default:(mk_symbols_meta symbols) in
mk ~deprecated ~long ?short ~default:None ?parse_mode ?in_help ~meta doc
~default_to_string:(fun _ -> "") ~default_to_string:(fun _ -> "")
~mk_setter:(fun var str -> var := Some (of_string str)) ~mk_setter:(fun var str -> var := Some (of_string str))
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_spec:(fun set -> Symbol (strings, set)) ~mk_spec:(fun set -> Symbol (strings, set))
let mk_symbol_seq ?(default=[]) ~symbols ~eq ?(deprecated=[]) ~long ?short ?parse_mode let mk_symbol_seq ?(default=[]) ~symbols ~eq ?(deprecated=[]) ~long ?short ?parse_mode ?in_help
?(meta="") doc = ?meta doc =
let sym_to_str = List.map ~f:(fun (x,y) -> (y,x)) symbols in let sym_to_str = List.map ~f:(fun (x,y) -> (y,x)) symbols in
let of_string str = List.Assoc.find_exn ~equal:String.equal symbols str in let of_string str = List.Assoc.find_exn ~equal:String.equal symbols str in
let to_string sym = List.Assoc.find_exn ~equal:eq sym_to_str sym in let to_string sym = List.Assoc.find_exn ~equal:eq sym_to_str sym in
mk ~deprecated ~long ?short ~default ?parse_mode ~meta:(",-separated sequence" ^ meta) doc let meta = Option.value meta ~default:(",-separated sequence of " ^ mk_symbols_meta symbols) in
mk ~deprecated ~long ?short ~default ?parse_mode ?in_help
~meta doc
~default_to_string:(fun syms -> String.concat ~sep:" " (List.map ~f:to_string syms)) ~default_to_string:(fun syms -> String.concat ~sep:" " (List.map ~f:to_string syms))
~mk_setter:(fun var str_seq -> ~mk_setter:(fun var str_seq ->
var := List.map ~f:of_string (String.split ~on:',' str_seq)) var := List.map ~f:of_string (String.split ~on:',' str_seq))
@ -539,15 +462,15 @@ let mk_symbol_seq ?(default=[]) ~symbols ~eq ?(deprecated=[]) ~long ?short ?pars
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_set_from_json ~default ~default_to_string ~f let mk_set_from_json ~default ~default_to_string ~f
?(deprecated=[]) ~long ?short ?parse_mode ?(meta="json") doc = ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="json") doc =
mk ~deprecated ~long ?short ?parse_mode ~meta doc mk ~deprecated ~long ?short ?parse_mode ?in_help ~meta doc
~default ~default_to_string ~default ~default_to_string
~mk_setter:(fun var json -> var := f (Yojson.Basic.from_string json)) ~mk_setter:(fun var json -> var := f (Yojson.Basic.from_string json))
~decode_json:(fun ~inferconfig_dir:_ json -> [dashdash long; Yojson.Basic.to_string json]) ~decode_json:(fun ~inferconfig_dir:_ json -> [dashdash long; Yojson.Basic.to_string json])
~mk_spec:(fun set -> String set) ~mk_spec:(fun set -> String set)
let mk_json ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="json") doc = let mk_json ?(deprecated=[]) ~long ?short ?parse_mode ?in_help ?(meta="json") doc =
mk ~deprecated ~long ?short ?parse_mode ~meta doc mk ~deprecated ~long ?short ?parse_mode ?in_help ~meta doc
~default:(`List []) ~default_to_string:Yojson.Basic.to_string ~default:(`List []) ~default_to_string:Yojson.Basic.to_string
~mk_setter:(fun var json -> var := Yojson.Basic.from_string json) ~mk_setter:(fun var json -> var := Yojson.Basic.from_string json)
~decode_json:(fun ~inferconfig_dir:_ json -> [dashdash long; Yojson.Basic.to_string json]) ~decode_json:(fun ~inferconfig_dir:_ json -> [dashdash long; Yojson.Basic.to_string json])
@ -557,45 +480,14 @@ let mk_json ?(deprecated=[]) ~long ?short ?parse_mode ?(meta="json") doc =
[parse_action_accept_unknown_args] is true. *) [parse_action_accept_unknown_args] is true. *)
let mk_anon () = rev_anon_args let mk_anon () = rev_anon_args
let mk_rest ?(parse_mode=Infer []) doc = let mk_rest ?(parse_mode=InferCommand) ?(in_help=[]) doc =
let rest = ref [] in let rest = ref [] in
let spec = Rest (fun arg -> rest := arg :: !rest) in let spec = Rest (fun arg -> rest := arg :: !rest) in
add parse_mode {long = "--"; short = ""; meta = ""; doc; spec; add parse_mode in_help {long = "--"; short = ""; meta = ""; doc; spec;
decode_json = fun ~inferconfig_dir:_ _ -> []} ; decode_json = fun ~inferconfig_dir:_ _ -> []} ;
rest rest
let set_curr_speclist_for_parse_action ~usage ?(parse_all=false) parse_action = let normalize_desc_list speclist =
let full_speclist = ref [] in
let curr_usage status =
prerr_endline (String.concat_array ~sep:" " !args_to_parse) ;
Arg.usage !curr_speclist usage ;
exit status
and full_usage status =
Arg.usage (to_arg_speclist !full_speclist) usage ;
exit status
in
(* "-help" and "--help" are automatically recognized by Arg.parse, so we have to give them special
treatment *)
let add_or_suppress_help (speclist, (doc_width,left_width)) =
let unknown opt =
(opt, Unit (fun () -> raise (Arg.Bad ("unknown option '" ^ opt ^ "'"))), "") in
let mk_spec ~long ?(short="") spec doc =
pad_and_xform doc_width left_width {
long; short; meta=""; spec; doc;
decode_json=fun ~inferconfig_dir:_ _ -> raise (Arg.Bad long);
} in
speclist @ [
mk_spec ~long:"help" ~short:"h"
(Unit (fun () -> curr_usage 0))
"Display this list of options";
mk_spec ~long:"help-full"
(Unit (fun () -> full_usage 0))
"Display the full list of options, including internal and experimental options";
(unknown "-help")
]
in
let normalize speclist =
let norm k = let norm k =
let remove_no s = let remove_no s =
let len = String.length k in let len = String.length k in
@ -616,105 +508,115 @@ let set_curr_speclist_for_parse_action ~usage ?(parse_all=false) parse_action =
let lower_norm s = String.lowercase @@ norm s in let lower_norm s = String.lowercase @@ norm s in
String.compare (lower_norm x) (lower_norm y) in String.compare (lower_norm x) (lower_norm y) in
let sort speclist = List.sort ~cmp:compare_specs speclist in let sort speclist = List.sort ~cmp:compare_specs speclist in
align (sort speclist) sort speclist
let mk_command_doc ~title ~section ~version ~date ~short_description ~synopsis ~description
?options ?exit_status ?environment ?files ?notes ?bugs ?examples ~see_also
command_str =
let add_if section blocks = match blocks with
| None -> `Blocks []
| Some bs -> `Blocks (`S section :: bs) in
let manual_pre_options = [
`S Cmdliner.Manpage.s_name;
(* the format of the following line is mandated by man(7) *)
`Pre (Printf.sprintf "%s - %s" command_str short_description);
`S Cmdliner.Manpage.s_synopsis;
`Blocks synopsis;
`S Cmdliner.Manpage.s_description;
`Blocks description;
] in
let manual_post_options = [
add_if Cmdliner.Manpage.s_exit_status exit_status;
add_if Cmdliner.Manpage.s_environment environment;
add_if Cmdliner.Manpage.s_files files;
add_if manpage_s_notes notes;
add_if Cmdliner.Manpage.s_bugs bugs;
add_if Cmdliner.Manpage.s_examples examples;
`S Cmdliner.Manpage.s_see_also;
`Blocks see_also;
] in
let command_doc = {
title = command_str, section, date, version, title;
manual_pre_options; manual_options = options; manual_post_options;
} in
command_doc
let set_curr_speclist_for_parse_mode ~usage parse_mode =
let curr_usage status =
prerr_endline (String.concat_array ~sep:" " !args_to_parse) ;
prerr_endline usage ;
exit status
in in
let add_to_curr_speclist ?(add_help=false) ?header parse_action = (* "-help" and "--help" are automatically recognized by Arg.parse, so we have to give them special
let mk_header_spec heading = treatment *)
("", Unit (fun () -> ()), "\n## " ^ heading ^ "\n") in let add_or_suppress_help speclist =
let exe_descs = match parse_all, parse_action with let unknown opt =
| true, _ -> (opt, Unit (fun () -> raise (Arg.Bad ("unknown option '" ^ opt ^ "'"))), "") in
List.Assoc.find_exn ~equal:equal_parse_tag parse_tag_desc_lists AllInferTags let has_opt opt = List.exists ~f:(fun (o, _, _) -> String.equal opt o) speclist in
| false, Infer section -> let add_unknown opt = if not (has_opt opt) then List.cons (unknown opt) else Fn.id in
List.Assoc.find_exn ~equal:equal_section infer_section_desc_lists section add_unknown "-help" @@ add_unknown "--help" @@ speclist
| false, (Differential | Javac | NoParse) ->
to_parse_tag parse_action
|> List.Assoc.find_exn ~equal:equal_parse_tag parse_tag_desc_lists in
let (exe_speclist, widths) = normalize !exe_descs in
let exe_speclist = if add_help
then add_or_suppress_help (exe_speclist, widths)
else exe_speclist in
let exe_speclist = to_arg_speclist exe_speclist in
(* Return false if the same option appears in [speclist], unless [doc] is non-empty and the
documentation in [speclist] is empty. The goal is to keep only one instance of each option,
and that instance is the one that has a non-empty docstring if there is one. *)
let is_not_dup_with_doc speclist (opt, _, doc) =
opt = "" ||
List.for_all ~f:(fun (opt', _, doc') ->
(doc <> "" && doc' = "") || (not (String.equal opt opt'))) speclist in
let unique_exe_speclist = List.filter ~f:(is_not_dup_with_doc !curr_speclist) exe_speclist in
curr_speclist := List.filter ~f:(is_not_dup_with_doc unique_exe_speclist) !curr_speclist @
(match header with
| Some s -> (to_arg_spec_triple (mk_header_spec s)):: unique_exe_speclist
| None -> unique_exe_speclist)
in in
(* speclist includes args for current exe with docs, and all other args without docs, so
that all args can be parsed, but --help and parse failures only show external args for
current exe *)
(* reset the speclist between calls to this function *)
curr_speclist := [];
if equal_parse_action parse_action (Infer Driver) then (
add_to_curr_speclist ~add_help:true ~header:"Driver options" (Infer Driver);
add_to_curr_speclist ~header:"Checkers options" (Infer Checkers);
add_to_curr_speclist ~header:"Clang-specific options" (Infer Clang);
add_to_curr_speclist ~header:"Java-specific options" (Infer Java);
) else
add_to_curr_speclist ~add_help:true parse_action
;
assert( check_no_duplicates !curr_speclist )
;
let full_desc_list = let full_desc_list =
let parse_tag = if parse_all then AllInferTags else to_parse_tag parse_action in List.Assoc.find_exn ~equal:equal_parse_mode parse_mode_desc_lists parse_mode in
List.Assoc.find_exn ~equal:equal_parse_tag parse_tag_desc_lists parse_tag in curr_speclist := normalize_desc_list !full_desc_list
full_speclist := add_or_suppress_help (normalize !full_desc_list) |> List.map ~f:xdesc
; |> add_or_suppress_help
|> to_arg_speclist;
assert( check_no_duplicates !curr_speclist );
curr_usage curr_usage
let select_parse_action ~usage ?parse_all action = let select_parse_mode ~usage action =
let usage = set_curr_speclist_for_parse_action ~usage ?parse_all action in let usage = set_curr_speclist_for_parse_mode ~usage action in
unknown_args_action := if accept_unknown_args action then `Add else `Reject; unknown_args_action := if accept_unknown_args action then `Add else `ParseCommands;
final_parse_action := action;
usage usage
let string_of_command command =
let (_, s, _) = List.Assoc.find_exn !subcommands ~equal:equal_command command in
s
let anon_fun arg = let anon_fun arg =
match List.Assoc.find !subcommand_actions ~equal:String.equal arg with
| Some switch ->
switch ()
| None ->
match !unknown_args_action with match !unknown_args_action with
| `ParseCommands -> (
match !curr_command, List.Assoc.find !subcommand_actions ~equal:String.equal arg with
| None, Some switch -> switch ()
| Some command, Some _ ->
raise (Arg.Bad
("More than one subcommand specified: " ^ string_of_command command ^ ", " ^
arg))
| _, None ->
raise (Arg.Bad ("unexpected anonymous argument: " ^ arg))
)
| `Skip -> | `Skip ->
() ()
| `Add -> | `Add ->
rev_anon_args := arg::!rev_anon_args rev_anon_args := arg::!rev_anon_args
| `Reject ->
raise (Arg.Bad ("unexpected anonymous argument: " ^ arg))
let mk_rest_actions ?(parse_mode=Infer []) doc ~usage decode_action = let mk_rest_actions ?(parse_mode=InferCommand) ?(in_help=[]) doc ~usage decode_action =
let rest = ref [] in let rest = ref [] in
let spec = String (fun arg -> let spec = String (fun arg ->
rest := List.rev (Array.to_list (Array.slice !args_to_parse (!arg_being_parsed + 1) 0)) ; rest := List.rev (Array.to_list (Array.slice !args_to_parse (!arg_being_parsed + 1) 0)) ;
select_parse_action ~usage (decode_action arg) |> ignore; select_parse_mode ~usage (decode_action arg) |> ignore;
(* stop accepting new anonymous arguments *) (* stop accepting new anonymous arguments *)
unknown_args_action := `Skip) in unknown_args_action := `Skip) in
add parse_mode {long = "--"; short = ""; meta = ""; doc; spec; add parse_mode in_help {long = "--"; short = ""; meta = ""; doc; spec;
decode_json = fun ~inferconfig_dir:_ _ -> []} ; decode_json = fun ~inferconfig_dir:_ _ -> []} ;
rest rest
let mk_switch_parse_action let mk_subcommand command ?(accept_unknown_args=false) ?deprecated ~long ?(name=long)
parse_action ~usage ?(deprecated=[]) ~long ?(name=long) ?parse_mode ?(meta="") doc = ?parse_mode ?in_help command_doc =
let switch () = let switch () =
select_parse_action ~usage parse_action |> ignore in curr_command := Some command;
unknown_args_action := if accept_unknown_args then `Add else `ParseCommands in
ignore( ignore(
mk ~deprecated ~long ~default:() ?parse_mode ~meta doc mk ?deprecated ~long ~default:() ?parse_mode ?in_help ~meta:""
(Printf.sprintf "activates the %s subcommand (see $(i,`infer %s --help`))" long long)
~default_to_string:(fun () -> "") ~default_to_string:(fun () -> "")
~decode_json:(string_json_decoder ~long) ~decode_json:(string_json_decoder ~long)
~mk_setter:(fun _ _ -> switch ()) ~mk_setter:(fun _ _ -> switch ())
~mk_spec:(fun _ -> Unit switch)); ~mk_spec:(fun _ -> Unit switch));
let add_action opt = subcommands := (command, (command_doc, name, in_help))::!subcommands;
let sub = (opt, switch) in subcommand_actions := (name, switch)::!subcommand_actions
subcommand_actions := sub::!subcommand_actions in
add_action name
let decode_inferconfig_to_argv path = let decode_inferconfig_to_argv path =
let json = match Utils.read_json_file path with let json = match Utils.read_json_file path with
@ -723,7 +625,7 @@ let decode_inferconfig_to_argv path =
| Error msg -> | Error msg ->
warnf "WARNING: Could not read or parse Infer config in %s:@\n%s@." path msg ; warnf "WARNING: Could not read or parse Infer config in %s:@\n%s@." path msg ;
`Assoc [] in `Assoc [] in
let desc_list = List.Assoc.find_exn ~equal:equal_parse_tag parse_tag_desc_lists AllInferTags in let desc_list = List.Assoc.find_exn ~equal:equal_parse_mode parse_mode_desc_lists InferCommand in
let json_config = YBU.to_assoc json in let json_config = YBU.to_assoc json in
let inferconfig_dir = Filename.dirname path in let inferconfig_dir = Filename.dirname path in
let one_config_item result (key, json_val) = let one_config_item result (key, json_val) =
@ -779,7 +681,9 @@ let extra_env_args = ref []
let extend_env_args args = let extend_env_args args =
extra_env_args := List.rev_append args !extra_env_args extra_env_args := List.rev_append args !extra_env_args
let parse_args ~usage ?parse_all action args0 = (* TODO(t18057447) [should_expand_args] is a bogus hack to side-step a bug with expansion of
@argfiles *)
let parse_args ~usage initial_action ?(should_expand_args=true) ?initial_command args0 =
(* look inside argfiles so we can move select arguments into the top line CLI and parse them into (* look inside argfiles so we can move select arguments into the top line CLI and parse them into
Config vars. note that we don't actually delete the arguments to the file, we just duplicate Config vars. note that we don't actually delete the arguments to the file, we just duplicate
them on the CLI. javac is ok with this. *) them on the CLI. javac is ok with this. *)
@ -811,15 +715,19 @@ let parse_args ~usage ?parse_all action args0 =
acc acc
else else
arg :: acc in arg :: acc in
let args = if should_expand_args then
List.fold ~f:expand_argfiles ~init:[] (List.rev args0)
else
args0 in
let args =
if Option.is_none parse_all
then List.fold ~f:expand_argfiles ~init:[] (List.rev args0)
else args0 in
let exe_name = Sys.executable_name in let exe_name = Sys.executable_name in
args_to_parse := Array.of_list (exe_name :: args); args_to_parse := Array.of_list (exe_name :: args);
arg_being_parsed := 0; arg_being_parsed := 0;
let curr_usage = select_parse_action ~usage ?parse_all action in let curr_usage = select_parse_mode ~usage initial_action in
Option.iter initial_command ~f:(fun command ->
let switch = List.Assoc.find_exn !subcommand_actions ~equal:String.equal
(string_of_command command) in
switch ());
(* tests if msg indicates an unknown option, as opposed to a known option with bad argument *) (* tests if msg indicates an unknown option, as opposed to a known option with bad argument *)
let is_unknown msg = String.is_substring msg ~substring:": unknown option" in let is_unknown msg = String.is_substring msg ~substring:": unknown option" in
let rec parse_loop () = let rec parse_loop () =
@ -828,19 +736,22 @@ let parse_args ~usage ?parse_all action args0 =
anon_fun usage anon_fun usage
with with
| Arg.Bad usage_msg -> | Arg.Bad usage_msg ->
if !unknown_args_action <> `Reject && is_unknown usage_msg then ( if !unknown_args_action <> `ParseCommands && is_unknown usage_msg then (
anon_fun !args_to_parse.(!arg_being_parsed); anon_fun !args_to_parse.(!arg_being_parsed);
parse_loop () parse_loop ()
) else ( ) else (
Pervasives.prerr_string usage_msg; Pervasives.prerr_string usage_msg;
exit 2 exit 2
) )
| Arg.Help usage_msg -> Pervasives.print_string usage_msg; exit 0 | Arg.Help _ ->
(* we handle --help by ourselves and error on -help, so Arg has no way to raise Help
anymore *)
assert false
in in
parse_loop (); parse_loop ();
curr_usage curr_usage
let parse ?config_file ~usage action = let parse ?config_file ~usage action initial_command =
let env_args = decode_env_to_argv (Option.value (Sys.getenv args_env_var) ~default:"") in let env_args = decode_env_to_argv (Option.value (Sys.getenv args_env_var) ~default:"") in
let inferconfig_args = let inferconfig_args =
Option.map ~f:decode_inferconfig_to_argv config_file |> Option.value ~default:[] in Option.map ~f:decode_inferconfig_to_argv config_file |> Option.value ~default:[] in
@ -859,15 +770,100 @@ let parse ?config_file ~usage action =
else !args_to_export ^ String.of_char env_var_sep ^ encode_argv_to_env args in else !args_to_export ^ String.of_char env_var_sep ^ encode_argv_to_env args in
args_to_export := arg_string in args_to_export := arg_string in
(* read .inferconfig first, then env vars, then command-line options *) (* read .inferconfig first, then env vars, then command-line options *)
parse_args ~usage ~parse_all:true (Infer Driver) inferconfig_args |> ignore; (* TODO(t18057447) [should_expand_args] is a bogus hack to side-step a bug with expansion of
@argfiles *)
parse_args ~usage ~should_expand_args:false InferCommand inferconfig_args |> ignore;
(* NOTE: do not add the contents of .inferconfig to INFER_ARGS. This helps avoid hitting the (* NOTE: do not add the contents of .inferconfig to INFER_ARGS. This helps avoid hitting the
command line size limit. *) command line size limit. *)
parse_args ~usage ~parse_all:true (Infer Driver) env_args |> ignore; (* TODO(t18057447) [should_expand_args] is a bogus hack to side-step a bug with expansion of
@argfiles *)
parse_args ~usage ~should_expand_args:false InferCommand env_args |> ignore;
add_parsed_args_to_args_to_export (); add_parsed_args_to_args_to_export ();
let curr_usage = let curr_usage =
let cl_args = match Array.to_list Sys.argv with _ :: tl -> tl | [] -> [] in let cl_args = match Array.to_list Sys.argv with _ :: tl -> tl | [] -> [] in
let curr_usage = parse_args ~usage action cl_args in let curr_usage = parse_args ~usage action ?initial_command cl_args in
add_parsed_args_to_args_to_export (); add_parsed_args_to_args_to_export ();
curr_usage in curr_usage in
Unix.putenv ~key:args_env_var ~data:!args_to_export; Unix.putenv ~key:args_env_var ~data:!args_to_export;
!final_parse_action, curr_usage !curr_command, curr_usage
let wrap_line indent_string wrap_length line0 =
let line = indent_string ^ line0 in
let indent_length = String.length indent_string in
let word_sep = ' ' in
let words = String.split ~on:word_sep line in
let word_sep_str = String.of_char word_sep in
let add_word_to_paragraph (rev_lines, non_empty, line, line_length) word =
let word_length =
let len = String.length word in
if String.is_prefix ~prefix:"$(b," word || String.is_prefix ~prefix:"$(i," word then
len - 4 (* length of formatting tag prefix *)
- 1 (* APPROXIMATION: closing parenthesis that will come after the word, or maybe later *)
else
len in
let new_length = line_length + (String.length word_sep_str) + word_length in
let new_non_empty = non_empty || word <> "" in
if new_length > wrap_length && non_empty then
(line::rev_lines, true, indent_string ^ word, indent_length + word_length)
else
let sep = if Int.equal line_length indent_length then "" else word_sep_str in
let new_line = line ^ sep ^ word in
if new_length > wrap_length && new_non_empty then
(new_line::rev_lines, false, indent_string, indent_length)
else
(rev_lines, new_non_empty, new_line, new_length) in
let (rev_lines, _, line, _) =
List.fold ~f:add_word_to_paragraph ~init:([], false, "", 0) words in
List.rev (line::rev_lines)
let show_manual ?internal_section default_doc command_opt =
let command_doc = match command_opt with
| None ->
default_doc
| Some command ->
let (command_doc, _, _) = List.Assoc.find_exn !subcommands command in
command_doc in
let pp_meta f meta = match meta with
| "" -> ()
| meta -> F.fprintf f " $(i,%s)" (Cmdliner.Manpage.escape meta) in
let pp_short f = function
| "" -> ()
| s -> Format.fprintf f ",$(b,-%s)" s in
let block_of_desc { long; meta; short; doc } =
if String.equal doc "" then
[]
else
let doc_first_line, doc_other_lines = match String.split ~on:'\n' doc with
| first::other -> first, other
| [] -> "", [] in
(* Cmdline.Manpage does not format multi-paragraph documentation strings correctly for `I
blocks, so we do a bit of formatting by hand *)
let indent_string = " " in
let width = 77 (* Cmdliner.Manpage width limit it seems *)
- 7 (* base indentation of documentation strings *) in
`I (Format.asprintf "$(b,%s)%a%a" (dashdash long) pp_short short pp_meta meta,
doc_first_line)
:: List.concat_map (List.concat_map ~f:(wrap_line indent_string width) doc_other_lines)
~f:(fun s -> [`Noblank; `Pre s]) in
let option_blocks = match command_doc.manual_options, command_opt with
| None, None ->
failwithf "Cannot create %s section" Cmdliner.Manpage.s_options
| Some blocks, _ ->
`S Cmdliner.Manpage.s_options :: blocks
| None, Some command ->
let sections = List.Assoc.find_exn help_sections_desc_lists command in
let hidden =
match internal_section with
| Some section ->
`S section :: `P "Use at your own risk."
:: List.concat_map ~f:block_of_desc (normalize_desc_list !hidden_descs_list)
| None ->
[] in
SectionMap.fold (fun section descs result ->
`S section ::
List.concat_map ~f:block_of_desc (normalize_desc_list descs) @ result)
!sections hidden in
let blocks = [`Blocks command_doc.manual_pre_options; `Blocks option_blocks;
`Blocks command_doc.manual_post_options] in
Cmdliner.Manpage.print `Auto Format.std_formatter (command_doc.title, blocks);
()

@ -14,18 +14,30 @@ open! IStd
(** Print to stderr in case of error, fails in strict mode *) (** Print to stderr in case of error, fails in strict mode *)
val warnf : ('a, Format.formatter, unit) format -> 'a val warnf : ('a, Format.formatter, unit) format -> 'a
(** a section is a part of infer that can be affected by an infer option *) type parse_mode =
type section = | InferCommand (** parse arguments as arguments for infer *)
Analysis | BufferOverrun | Checkers | Clang | Crashcontext | Driver | Java | Print | Javac (** parse arguments passed to the Java compiler *)
| NoParse (** all arguments are anonymous arguments, no parsing is attempted *)
[@@deriving compare] [@@deriving compare]
val all_sections : section list (** Main modes of operation for infer *)
type command =
type 'a parse = Differential | Infer of 'a | Javac | NoParse | Analyze (** analyze previously captured source files *)
| 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 *)
| Report (** post-process infer results and reports *)
| ReportDiff (** compute the difference of two infer reports *)
| Run (** orchestrate the capture, analysis, and reporting of a compilation command *)
[@@deriving compare]
type parse_mode = section list parse [@@deriving compare] val equal_command : command -> command -> bool
type parse_action = section parse [@@deriving compare] val all_commands : command list
val is_originator : bool val is_originator : bool
@ -43,15 +55,15 @@ val init_work_dir : string
- [f] specifies a transformation to be performed on the parsed value before setting the config - [f] specifies a transformation to be performed on the parsed value before setting the config
variable variable
- [symbols] is an association list sometimes used in place of [f] - [symbols] is an association list sometimes used in place of [f]
- [parse_mode] declares which parse mode the option is for. In the case of Infer, that includes - [parse_mode] declares which parse mode the option is for
the sections for which the option should be included in the external documentation (--help), - [in_help] indicates the man pages in which the command should be documented, as generated by
otherwise it appears only in --help-full calling infer with --help. Otherwise it appears only in --help-full.
- [meta] is a meta-variable naming the parsed value for documentation purposes - [meta] is a meta-variable naming the parsed value for documentation purposes
- a documentation string - a documentation string
*) *)
type 'a t = type 'a t =
?deprecated:string list -> long:string -> ?short:char -> ?deprecated:string list -> long:string -> ?short:char ->
?parse_mode:parse_mode -> ?meta:string -> string -> ?parse_mode:parse_mode -> ?in_help:(command * string) list -> ?meta:string -> string ->
'a 'a
(** [mk_set variable value] defines a command line option which sets [variable] to [value]. *) (** [mk_set variable value] defines a command line option which sets [variable] to [value]. *)
@ -128,43 +140,80 @@ val mk_anon : unit -> string list ref
reverse order they appeared on the command line. For example, calling [mk_rest] and parsing reverse order they appeared on the command line. For example, calling [mk_rest] and parsing
[exe -opt1 -opt2 -- arg1 arg2] will result in the returned ref containing [arg2; arg1]. *) [exe -opt1 -opt2 -- arg1 arg2] will result in the returned ref containing [arg2; arg1]. *)
val mk_rest : val mk_rest :
?parse_mode:parse_mode -> string -> ?parse_mode:parse_mode-> ?in_help:(command * string) list -> string ->
string list ref string list ref
(** [mk_rest_actions doc ~usage command_to_parse_action] defines a [string list ref] of the command (** [mk_rest_actions doc ~usage command_to_parse_mode] defines a [string list ref] of the command
line arguments following ["--"], in the reverse order they appeared on the command line. [usage] line arguments following ["--"], in the reverse order they appeared on the command line. [usage]
is the usage message in case of parse errors or if --help is passed. For example, calling is the usage message in case of parse errors or if --help is passed. For example, calling
[mk_action] and parsing [exe -opt1 -opt2 -- arg1 arg2] will result in the returned ref [mk_action] and parsing [exe -opt1 -opt2 -- arg1 arg2] will result in the returned ref
containing [arg2; arg1]. Additionally, the first arg following ["--"] is passed to containing [arg2; arg1]. Additionally, the first arg following ["--"] is passed to
[command_to_parse_action] to obtain the parse action that will be used to parse the remaining [command_to_parse_mode] to obtain the parse action that will be used to parse the remaining
arguments. *) arguments. *)
val mk_rest_actions : val mk_rest_actions :
?parse_mode:parse_mode -> string -> ?parse_mode:parse_mode -> ?in_help:(command * string) list -> string ->
usage:string -> (string -> parse_action) usage:string -> (string -> parse_mode)
-> string list ref -> string list ref
type command_doc
(** When the option is found on the command line, either as [--long] or [name], the current parse
action is discarded and the following arguments are parsed using [parse_action]. [name] defaults (** [mk_command_doc ~title ~section ~version ~short_description ~synopsis ~description ~see_also
to [long]. *) command_exe] records information about a command that is used to create its man page. A lot of
val mk_switch_parse_action : parse_action -> usage:string -> ?deprecated:string list -> the concepts are taken from man-pages(7).
long:string -> ?name:string -> ?parse_mode:section list parse -> ?meta:string -> string -> unit
- [command_exe] is the name of the command, preferably an executable that selects the command
- [title] will be the title of the manual
- [section] will be the section of the manual (the number 7 in man-pages(7))
- [version] is the version string of the command
- [date] is the date of the last modification of the manual
- [short_description] is a one-line description of the command
- [options] if specified, will populate the OPTIONS section; otherwise, the options are taken
from the command's
- All the other [section_name] options correspond to the contents of the section [section_name].
Some are mandatory and some are not.
*)
val mk_command_doc : title:string -> section:int -> version:string -> date:string ->
short_description:string -> synopsis:Cmdliner.Manpage.block list ->
description:Cmdliner.Manpage.block list ->
?options:Cmdliner.Manpage.block list ->
?exit_status:Cmdliner.Manpage.block list ->
?environment:Cmdliner.Manpage.block list ->
?files:Cmdliner.Manpage.block list ->
?notes:Cmdliner.Manpage.block list ->
?bugs:Cmdliner.Manpage.block list ->
?examples:Cmdliner.Manpage.block list ->
see_also:Cmdliner.Manpage.block list ->
string -> command_doc
(** [mk_subcommand command ~long command_doc] defines the subcommand [command]. A subcommand is
activated by passing [--long], [name], or any [-key] for [key] in [deprecated] on the command
line. [name] defaults to [long]. A man page is automatically generated for [command] based on
the information in [command_doc]. *)
val mk_subcommand : command -> ?accept_unknown_args:bool ->
?deprecated:string list -> long:string -> ?name:string ->
?parse_mode:parse_mode -> ?in_help:(command * string) list -> command_doc -> unit
(** environment variable use to pass arguments from parent to child processes *) (** environment variable use to pass arguments from parent to child processes *)
val args_env_var : string val args_env_var : string
val strict_mode_env_var : string
(** separator of argv elements when encoded into environment variables *) (** separator of argv elements when encoded into environment variables *)
val env_var_sep : char val env_var_sep : char
(** [extend_env_args args] appends [args] to those passed via [args_env_var] *) (** [extend_env_args args] appends [args] to those passed via [args_env_var] *)
val extend_env_args : string list -> unit val extend_env_args : string list -> unit
(** [parse ~usage parse_action] parses command line arguments as specified by preceding calls to the (** [parse ~usage parse_mode command] parses command line arguments as specified by preceding calls
[mk_*] functions, and returns a function that prints the usage message and help text then exits. to the [mk_*] functions, and returns:
- the command selected by the user on the command line, except if [command] is not None in which
case it is considered "pre-selected" for the user;
- a function that prints the usage message and help text then exits with the code passed as
argument.
The decoded values of the inferconfig file [config_file], if provided, are parsed, followed by The decoded values of the inferconfig file [config_file], if provided, are parsed, followed by
the decoded values of the environment variable [args_env_var], followed by [Sys.argv] if the decoded values of the environment variable [args_env_var], followed by [Sys.argv] if
[parse_action] is one that should parse command line arguments (this is defined in the [parse_mode] is one that should parse command line arguments (this is defined in the
implementation of this module). Therefore arguments passed on the command line supersede those implementation of this module). Therefore arguments passed on the command line supersede those
specified in the environment variable, which themselves supersede those passed via the config specified in the environment variable, which themselves supersede those passed via the config
file. file.
@ -172,7 +221,12 @@ val extend_env_args : string list -> unit
WARNING: An argument will be interpreted as many times as it appears in all of the config file, WARNING: An argument will be interpreted as many times as it appears in all of the config file,
the environment variable, and the command line. The [args_env_var] is set to the set of options the environment variable, and the command line. The [args_env_var] is set to the set of options
parsed in [args_env_var] and on the command line. *) parsed in [args_env_var] and on the command line. *)
val parse : ?config_file:string -> usage:Arg.usage_msg -> parse_action -> parse_action * (int -> 'a) val parse : ?config_file:string -> usage:Arg.usage_msg -> parse_mode -> command option
-> command option * (int -> 'a)
(** [is_env_var_set var] is true if $[var]=1 *) (** [is_env_var_set var] is true if $[var]=1 *)
val is_env_var_set : string -> bool val is_env_var_set : string -> bool
(** Display the manual of [command] to the user, or [command_doc] if [command] is None. If
[internal_section] is true, add a section about internal (hidden) options. *)
val show_manual : ?internal_section:string -> command_doc -> command option -> unit

File diff suppressed because it is too large Load Diff

@ -10,16 +10,14 @@
open! IStd open! IStd
module CLOpt = CommandLineOption
(** Configuration values: either constant, determined at compile time, or set at startup (** Configuration values: either constant, determined at compile time, or set at startup
time by system calls, environment variables, or command line options *) time by system calls, environment variables, or command line options *)
type exe = Analyze | Clang | Driver | Print [@@deriving compare]
val exe_name : exe -> string
(** Various kind of analyzers *)
type analyzer = type analyzer =
| Capture | Compile | Infer | Eradicate | Checkers | Tracing | Crashcontext | Linters | BiAbduction | CaptureOnly | CompileOnly | Eradicate | Checkers | Tracing | Crashcontext
| Linters
[@@deriving compare] [@@deriving compare]
val equal_analyzer : analyzer -> analyzer -> bool val equal_analyzer : analyzer -> analyzer -> bool
@ -101,6 +99,8 @@ val frontend_stats_dir_name : string
val global_tenv_filename : string val global_tenv_filename : string
val idempotent_getters : bool val idempotent_getters : bool
val incremental_procs : bool val incremental_procs : bool
val infer_exe_name : string
val infer_analyze_exe_name : string
val infer_py_argparse_error_exit_code : int val infer_py_argparse_error_exit_code : int
val initial_analysis_time : float val initial_analysis_time : float
val ivar_attributes : string val ivar_attributes : string
@ -200,6 +200,7 @@ val clang_frontend_do_lint : bool
val clang_ignore_regex : string option val clang_ignore_regex : string option
val clang_include_to_override_regex : string option val clang_include_to_override_regex : string option
val cluster_cmdline : string option val cluster_cmdline : string option
val command : CLOpt.command
val compute_analytics : bool val compute_analytics : bool
val continue_capture : bool val continue_capture : bool
val default_linters : bool val default_linters : bool
@ -235,7 +236,6 @@ val file_renamings : string option
val filter_paths : bool val filter_paths : bool
val filter_report_paths : string option val filter_report_paths : string option
val filtering : bool val filtering : bool
val final_parse_action : CommandLineOption.parse_action
val flavors : bool val flavors : bool
val from_json_report : string option val from_json_report : string option
val frontend_debug : bool val frontend_debug : bool
@ -288,7 +288,6 @@ val no_translate_libs : bool
val objc_memory_model_on : bool val objc_memory_model_on : bool
val only_footprint : bool val only_footprint : bool
val out_file_cmdline : string val out_file_cmdline : string
val parse_action : CommandLineOption.parse_action
val pmd_xml : bool val pmd_xml : bool
val precondition_stats : bool val precondition_stats : bool
val print_logs : bool val print_logs : bool

@ -29,15 +29,12 @@ let dup_formatter fmt1 fmt2 =
Format.pp_set_formatter_output_functions fmt1 out_string flush Format.pp_set_formatter_output_functions fmt1 out_string flush
(** Name of dir for logging the output in the specific executable *) (** Name of dir for logging the output in the specific executable *)
let log_dir_of_action (action : CLOpt.parse_action) = match action with let log_dir_of_command (command : CLOpt.command) = match command with
| Infer (Analysis | BufferOverrun | Checkers | Crashcontext) -> "analyze" | Analyze -> "analyze"
| Differential -> "differential" | Capture | Clang | Compile -> "capture"
| Infer Driver -> "driver" | Report -> "report"
| Infer Clang | ReportDiff -> "reportdiff"
| Infer Java | Run -> "driver"
| NoParse
| Javac -> "capture"
| Infer Print -> "print"
let stdout_err_log_files = let stdout_err_log_files =
(((lazy F.std_formatter), (lazy Pervasives.stdout), (((lazy F.std_formatter), (lazy Pervasives.stdout),
@ -54,8 +51,8 @@ let close_log_file fmt chan file =
Out_channel.close c Out_channel.close c
) )
let create_log_file action name_prefix outerr = let create_log_file command name_prefix outerr =
let log_dir = Config.results_dir ^/ Config.log_dir_name ^/ log_dir_of_action action in let log_dir = Config.results_dir ^/ Config.log_dir_name ^/ log_dir_of_command command in
let config_name = match outerr with let config_name = match outerr with
| `Out -> Config.out_file_cmdline | `Out -> Config.out_file_cmdline
| `Err -> Config.err_file_cmdline in | `Err -> Config.err_file_cmdline in
@ -83,34 +80,36 @@ let create_log_file action name_prefix outerr =
"log files flushing"; "log files flushing";
(file_fmt, chan, file) (file_fmt, chan, file)
let should_setup_log_files (action : CLOpt.parse_action) = match action with let should_setup_log_files (command : CLOpt.command) = match command with
| Infer Analysis | Analyze | Capture | Clang | Compile ->
| Infer Clang -> Config.debug_mode || Config.stats_mode Config.debug_mode || Config.stats_mode
| Infer Driver -> true | Run ->
| _ -> false true
| Report | ReportDiff ->
false
let create_outerr_log_files exe prefix_opt = let create_outerr_log_files command prefix_opt =
let lazy3 x = (lazy (fst3 (Lazy.force x)), let lazy3 x = (lazy (fst3 (Lazy.force x)),
lazy (snd3 (Lazy.force x)), lazy (snd3 (Lazy.force x)),
lazy (trd3 (Lazy.force x))) in lazy (trd3 (Lazy.force x))) in
if should_setup_log_files exe then if should_setup_log_files command then
let name_prefix = match prefix_opt with let name_prefix = match prefix_opt with
| Some name -> name ^ "-" | Some name -> name ^ "-"
| None -> "" in | None -> "" in
(lazy (create_log_file exe name_prefix `Out) |> lazy3, (lazy (create_log_file command name_prefix `Out) |> lazy3,
lazy (create_log_file exe name_prefix `Err) |> lazy3) lazy (create_log_file command name_prefix `Err) |> lazy3)
else else
stdout_err_log_files stdout_err_log_files
let ((out_formatter, out_chan, out_file), let ((out_formatter, out_chan, out_file),
(err_formatter, err_chan, err_file)) = (err_formatter, err_chan, err_file)) =
let (o_fmt, o_c, o_f), (e_fmt, e_c, e_f) = let (o_fmt, o_c, o_f), (e_fmt, e_c, e_f) =
create_outerr_log_files Config.parse_action None in create_outerr_log_files Config.command None in
((ref o_fmt, ref o_c, ref o_f), ((ref o_fmt, ref o_c, ref o_f),
(ref e_fmt, ref e_c, ref e_f)) (ref e_fmt, ref e_c, ref e_f))
let set_log_file_identifier exe prefix_opt = let set_log_file_identifier command prefix_opt =
let (o_fmt, o_c, o_f), (e_fmt, e_c, e_f) = create_outerr_log_files exe prefix_opt in let (o_fmt, o_c, o_f), (e_fmt, e_c, e_f) = create_outerr_log_files command prefix_opt in
(* close previous log files *) (* close previous log files *)
close_log_file !out_formatter !out_chan !out_file; close_log_file !out_formatter !out_chan !out_file;
close_log_file !err_formatter !err_chan !err_file; close_log_file !err_formatter !err_chan !err_file;

@ -73,7 +73,7 @@ val set_delayed_prints : print_action list -> unit
val reset_delayed_prints : unit -> unit val reset_delayed_prints : unit -> unit
(** Set a custom identifier to be part of the filename of the current logfiles. *) (** Set a custom identifier to be part of the filename of the current logfiles. *)
val set_log_file_identifier : CommandLineOption.parse_action -> string option -> unit val set_log_file_identifier : CommandLineOption.command -> string option -> unit
(** print to the current out stream, as specified in set_log_file_identifier (** print to the current out stream, as specified in set_log_file_identifier
(note: only prints in debug or in stats mode) *) (note: only prints in debug or in stats mode) *)

@ -49,7 +49,7 @@ let register_perf_stats_report source_file => {
let init_global_state_for_capture_and_linters source_file => { let init_global_state_for_capture_and_linters source_file => {
Logging.set_log_file_identifier Logging.set_log_file_identifier
CLOpt.(Infer Clang) (Some (Filename.basename (SourceFile.to_abs_path source_file))); CLOpt.Clang (Some (Filename.basename (SourceFile.to_abs_path source_file)));
register_perf_stats_report source_file; register_perf_stats_report source_file;
Config.curr_language := Config.Clang; Config.curr_language := Config.Clang;
DB.Results_dir.init source_file; DB.Results_dir.init source_file;
@ -165,7 +165,7 @@ let cc1_capture clang_cmd => {
}; };
Logging.out "@\n*** Beginning capture of file %s ***@\n" source_path; Logging.out "@\n*** Beginning capture of file %s ***@\n" source_path;
if ( if (
Config.equal_analyzer Config.analyzer Config.Compile || Config.equal_analyzer Config.analyzer Config.CompileOnly ||
CLocation.is_file_blacklisted source_path CLocation.is_file_blacklisted source_path
) { ) {
Logging.out "@\n Skip the analysis of source file %s@\n@\n" source_path; Logging.out "@\n Skip the analysis of source file %s@\n@\n" source_path;
@ -179,7 +179,7 @@ let cc1_capture clang_cmd => {
source_path (fun chan_in => run_and_validate_clang_frontend (`Pipe chan_in)) clang_cmd source_path (fun chan_in => run_and_validate_clang_frontend (`Pipe chan_in)) clang_cmd
}; };
/* reset logging to stop capturing log output into the source file's log */ /* reset logging to stop capturing log output into the source file's log */
Logging.set_log_file_identifier CLOpt.(Infer Clang) None; Logging.set_log_file_identifier CLOpt.Capture None;
() ()
} }
}; };

@ -47,11 +47,11 @@ let add_flavor_to_target target =
add "uber-compilation-database" add "uber-compilation-database"
| Some `NoDeps, _ -> | Some `NoDeps, _ ->
add "compilation-database" add "compilation-database"
| None, Compile -> | None, CompileOnly ->
target target
| None, (Linters | Capture) -> | None, (Linters | CaptureOnly) ->
add "infer-capture-all" add "infer-capture-all"
| None, (Checkers | Infer) -> | None, (BiAbduction | Checkers) ->
add "infer" add "infer"
| None, (Eradicate | Tracing | Crashcontext) -> | None, (Eradicate | Tracing | Crashcontext) ->
failwithf "Analyzer %s is Java-only; not supported with Buck flavors" failwithf "Analyzer %s is Java-only; not supported with Buck flavors"

@ -81,6 +81,6 @@ let compile compiler build_prog build_args =
let capture compiler ~prog ~args = let capture compiler ~prog ~args =
let verbose_out_file = compile compiler prog args in let verbose_out_file = compile compiler prog args in
if Config.analyzer <> Config.Compile then if Config.analyzer <> Config.CompileOnly then
JMain.from_verbose_out verbose_out_file; JMain.from_verbose_out verbose_out_file;
if not (Config.debug_mode || Config.stats_mode) then Unix.unlink verbose_out_file if not (Config.debug_mode || Config.stats_mode) then Unix.unlink verbose_out_file

@ -33,7 +33,7 @@ let infer_profile = lazy
\n </plugins>\ \n </plugins>\
\n </build>\ \n </build>\
\n </profile>\ \n </profile>\
" infer_profile_name (Config.(bin_dir ^/ string_of_analyzer Infer))) " infer_profile_name (Config.(bin_dir ^/ infer_exe_name)))
let pom_worklist = ref [CLOpt.init_work_dir] let pom_worklist = ref [CLOpt.init_work_dir]

@ -502,7 +502,7 @@ let test_resolve_infer_eradicate_conflict =
(* [(test_name, analyzer, expected_hashes); ...] *) (* [(test_name, analyzer, expected_hashes); ...] *)
[ [
("test_resolve_infer_eradicate_conflict_runs_with_infer_analyzer", ("test_resolve_infer_eradicate_conflict_runs_with_infer_analyzer",
Config.Infer, Config.BiAbduction,
([1], [11], [4])); ([1], [11], [4]));
("test_resolve_infer_eradicate_conflict_skips_with_checkers_analyzer", ("test_resolve_infer_eradicate_conflict_skips_with_checkers_analyzer",
Config.Checkers, Config.Checkers,

@ -28,6 +28,7 @@ ocaml-version: [ >= "4.04.0" ]
depends: [ depends: [
"ANSITerminal" {>="0.7"} "ANSITerminal" {>="0.7"}
"atdgen" {>="1.6.0"} "atdgen" {>="1.6.0"}
"cmdliner" {>="1.0.0"}
"core" {<"v0.9"} "core" {<"v0.9"}
"conf-autoconf" "conf-autoconf"
"ctypes" {>="0.9.2"} "ctypes" {>="0.9.2"}

@ -6,6 +6,7 @@ biniou = 1.0.12
camlp4 = 4.04+1 camlp4 = 4.04+1
camlzip = 1.07 camlzip = 1.07
camomile = 0.8.5 camomile = 0.8.5
cmdliner = 1.0.0
conf-autoconf = 0.1 conf-autoconf = 0.1
conf-m4 = 1 conf-m4 = 1
conf-pkg-config = 1.0 conf-pkg-config = 1.0

@ -1,6 +1,7 @@
(* load dependencies *) (* load dependencies *)
#use "topfind";; #use "topfind";;
#thread;; #thread;;
#require "cmdliner";;
#require "core.top";; #require "core.top";;
#require "ctypes";; #require "ctypes";;
#require "ctypes.stubs";; #require "ctypes.stubs";;

Loading…
Cancel
Save