@ -12,6 +12,7 @@
open ! Utils
module F = Format
module YBU = Yojson . Basic . Util
(* Each command line option may appear in the --help list of any executable, these tags are used to
@ -28,7 +29,11 @@ let current_exe =
| _ -> T
type desc = { long : string ; short : string ; meta : string ; doc : string ; spec : Arg . spec }
type desc = {
long : string ; short : string ; meta : string ; doc : string ; spec : Arg . spec ;
(* * how to go from an option in the json config file to a list of command-line options *)
decode_json : Yojson . Basic . json -> string list ;
}
let dashdash long =
match long with
@ -125,7 +130,7 @@ let add exes desc =
) exe_desc_lists
let mk ? ( deprecated = [] ) ? ( exes = [] )
~ long ? ( short = " " ) ~ default ~ meta doc ~ default_to_string ~ mk_setter ~ mk_spec =
~ long ? ( short = " " ) ~ default ~ meta doc ~ default_to_string ~ decode_json ~ mk_setter ~ mk_spec =
let variable = ref default in
let closure = mk_setter variable in
let setter str =
@ -136,7 +141,7 @@ let mk ?(deprecated=[]) ?(exes=[])
let default_string = default_to_string default in
if default_string = " " then doc
else doc ^ " (default: " ^ default_string ^ " ) " in
let desc = { long ; short ; meta ; doc ; spec } in
let desc = { long ; short ; meta ; doc ; spec ; decode_json } in
(* add desc for long option, with documentation ( which includes any short option ) for exes *)
add exes desc ;
(* add desc for short option only for parsing, without documentation *)
@ -153,11 +158,16 @@ type 'a t =
? exes : exe list -> ? meta : string -> Arg . doc ->
' a
let string_json_decoder ~ long json = [ dashdash long ; YBU . to_string json ]
let list_json_decoder json_decoder json = IList . flatten ( YBU . convert_each json_decoder json )
let mk_set var value ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
let setter () = var := value in
ignore (
mk ~ deprecated ~ long ? short ~ default : () ? exes ~ meta doc
~ default_to_string : ( fun () -> " " )
~ decode_json : ( string_json_decoder ~ long )
~ mk_setter : ( fun _ _ -> setter () )
~ mk_spec : ( fun _ -> Arg . Unit setter ) )
@ -165,6 +175,7 @@ let mk_option ?(default=None) ?(default_to_string=fun _ -> "") ~f
? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
mk ~ deprecated ~ long ? short ~ default ? exes ~ meta doc
~ default_to_string
~ decode_json : ( string_json_decoder ~ long )
~ mk_setter : ( fun var str -> var := f str )
~ mk_spec : ( fun set -> Arg . String set )
@ -199,10 +210,16 @@ let mk_bool ?(deprecated_no=[]) ?(default=false) ?(f=fun b -> b)
let mk_spec set = Arg . Unit ( fun () -> set " " ) in
let var =
mk ~ long ? short ~ deprecated ~ default ? exes
~ meta doc ~ default_to_string ~ mk_setter : ( fun var _ -> var := f true ) ~ mk_spec in
~ meta doc ~ default_to_string ~ mk_setter : ( fun var _ -> var := f true )
~ decode_json : ( fun json ->
[ dashdash ( if YBU . to_bool json then long else nolong ) ] )
~ mk_spec in
ignore (
mk ~ long : nolong ? short : noshort ~ deprecated : deprecated_no ~ default : ( not default ) ? exes
~ meta nodoc ~ default_to_string ~ mk_setter : ( fun _ _ -> var := f false ) ~ mk_spec ) ;
~ meta nodoc ~ default_to_string ~ mk_setter : ( fun _ _ -> var := f false )
~ decode_json : ( fun json ->
[ dashdash ( if YBU . to_bool json then nolong else long ) ] )
~ mk_spec ) ;
var
let mk_bool_group ? ( deprecated_no = [] ) ? ( default = false )
@ -217,18 +234,21 @@ let mk_int ~default ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc =
mk ~ deprecated ~ long ? short ~ default ? exes ~ meta doc
~ default_to_string : string_of_int
~ mk_setter : ( fun var str -> var := ( int_of_string str ) )
~ decode_json : ( string_json_decoder ~ long )
~ mk_spec : ( fun set -> Arg . String set )
let mk_float ~ default ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
mk ~ deprecated ~ long ? short ~ default ? exes ~ meta doc
~ default_to_string : string_of_float
~ mk_setter : ( fun var str -> var := ( float_of_string str ) )
~ decode_json : ( string_json_decoder ~ long )
~ mk_spec : ( fun set -> Arg . String set )
let mk_string ~ default ? ( f = fun s -> s ) ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
mk ~ deprecated ~ long ? short ~ default ? exes ~ meta doc
~ default_to_string : ( fun s -> s )
~ mk_setter : ( fun var str -> var := f str )
~ decode_json : ( string_json_decoder ~ long )
~ mk_spec : ( fun set -> Arg . String set )
let mk_string_opt ? default ? ( f = fun s -> s ) ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
@ -241,6 +261,7 @@ let mk_string_list ?(default=[]) ?(f=fun s -> s)
mk ~ deprecated ~ long ? short ~ default ? exes ~ meta doc
~ default_to_string : ( String . concat " , " )
~ mk_setter : ( fun var str -> var := ( f str ) :: ! var )
~ decode_json : ( list_json_decoder ( string_json_decoder ~ long ) )
~ mk_spec : ( fun set -> Arg . String set )
let mk_symbol ~ default ~ symbols ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
@ -251,6 +272,7 @@ let mk_symbol ~default ~symbols ?(deprecated=[]) ~long ?short ?exes ?(meta="") d
mk ~ deprecated ~ long ? short ~ default ? exes ~ meta doc
~ default_to_string : ( fun s -> to_string s )
~ mk_setter : ( fun var str -> var := of_string str )
~ decode_json : ( string_json_decoder ~ long )
~ mk_spec : ( fun set -> Arg . Symbol ( strings , set ) )
let mk_symbol_opt ~ symbols ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
@ -259,6 +281,7 @@ let mk_symbol_opt ~symbols ?(deprecated=[]) ~long ?short ?exes ?(meta="") doc =
mk ~ deprecated ~ long ? short ~ default : None ? exes ~ meta doc
~ default_to_string : ( fun _ -> " " )
~ mk_setter : ( fun var str -> var := Some ( of_string str ) )
~ decode_json : ( string_json_decoder ~ long )
~ mk_spec : ( fun set -> Arg . Symbol ( strings , set ) )
let mk_symbol_seq ? ( default = [] ) ~ symbols ? ( deprecated = [] ) ~ long ? short ? exes ? ( meta = " " ) doc =
@ -269,6 +292,9 @@ let mk_symbol_seq ?(default=[]) ~symbols ?(deprecated=[]) ~long ?short ?exes ?(m
~ default_to_string : ( fun syms -> String . concat " " ( IList . map to_string syms ) )
~ mk_setter : ( fun var str_seq ->
var := IList . map of_string ( Str . split ( Str . regexp_string " , " ) str_seq ) )
~ decode_json : ( fun json ->
[ dashdash long ;
String . concat " , " ( YBU . convert_each YBU . to_string json ) ] )
~ mk_spec : ( fun set -> Arg . String set )
let anon_fun = ref ( fun arg -> raise ( Arg . Bad ( " unexpected anonymous argument: " ^ arg ) ) )
@ -279,6 +305,38 @@ let mk_anon () =
anon
(* begin temporarily export inferconfig_json while .inferconfig-specific options still exist *)
let inferconfig_json = ref ( ` Assoc [] )
(* end temporarily export inferconfig_json *)
let decode_inferconfig_to_argv path =
let json = match read_optional_json_file path with
| Ok json ->
(* begin temporarily export inferconfig_json while
. inferconfig - specific options still exist * )
inferconfig_json := json ;
(* end temporarily export inferconfig_json *)
json
| Error msg ->
F . eprintf " WARNING: Could not read or parse Infer config in %s:@ \n %s@. " path msg ;
` Assoc [] in
let desc_list = ! ( IList . assoc ( = ) current_exe exe_desc_lists ) in
let json_config = YBU . to_assoc json in
let one_config_item result ( key , json_val ) =
try
let { decode_json } = IList . find ( fun { long } -> string_equal key long ) desc_list in
decode_json json_val @ result
with
| Not_found ->
(* TODO: have all json options be regular options as well. When this is done, we can show a
warning if a json key is not a valid option . * )
result
| YBU . Type_error ( msg , json ) ->
F . eprintf " WARNING: while reading config file %s:@ \n Ill-formed value %s for option %s: %s@. "
path ( Yojson . Basic . to_string json ) key msg ;
result in
IList . fold_left one_config_item [] json_config
(* * [sep_char] is used to separate elements of argv when encoded into environment variables *)
let sep_char = '^'
@ -308,7 +366,7 @@ let prefix_before_rest args =
prefix_before_rest_ [] args
let parse ? ( incomplete = false ) env_var exe_usage =
let parse ? ( incomplete = false ) ? config_file env_var exe_usage =
let curr_speclist = ref []
and full_speclist = ref []
in
@ -322,12 +380,15 @@ let parse ?(incomplete=false) env_var exe_usage =
exit status
in
let help_desc_list =
[ { long = " help " ; short = " " ; meta = " " ; spec = Arg . Unit ( fun () -> curr_usage 0 ) ;
[ { long = " help " ; short = " " ; meta = " " ; decode_json = ( fun _ -> [] ) ;
spec = Arg . Unit ( fun () -> curr_usage 0 ) ;
doc = " Display this list of options " }
; { long = " help-full " ; short = " " ; meta = " " ; spec = Arg . Unit ( fun () -> full_usage 0 ) ;
; { long = " help-full " ; short = " " ; meta = " " ; decode_json = ( fun _ -> [] ) ;
spec = Arg . Unit ( fun () -> full_usage 0 ) ;
doc = " Display the full list of options, including internal and experimental options " }
] in
let section heading speclist =
let normalize speclist =
let speclist = help_desc_list @ speclist in
let norm k =
let len = String . length k in
if len > 3 && String . sub k 0 3 = " no- " then String . sub k 3 ( len - 3 ) else k in
@ -338,27 +399,20 @@ let parse ?(incomplete=false) env_var exe_usage =
| _ , " -- " -> - 1
| _ -> String . compare ( norm x ) ( norm y ) in
let sort speclist = IList . sort compare_specs speclist in
let add_heading speclist =
match heading with
| Some heading ->
let doc = " \n " ^ heading ^ " \n " in
{ doc ; long = " " ; short = " " ; meta = " " ; spec = Arg . Unit ( fun () -> () ) } :: speclist
| None ->
speclist in
let suppress_help speclist =
( " -help " , Arg . Unit ( fun () -> raise ( Arg . Bad " unknown option '-help' " ) ) , " " ) :: speclist in
suppress_help ( align ~ limit : 32 ( add_heading ( sort speclist ) ) )
suppress_help ( align ~ limit : 32 ( sort speclist ) )
in
let curr_desc_list = IList . assoc ( = ) current_exe exe_desc_lists
in
(* curr_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 * )
curr_speclist := ( section None ( help_desc_list @ ! curr_desc_list ) )
curr_speclist := normalize ! curr_desc_list
;
assert ( check_no_duplicates ! curr_speclist )
;
full_speclist := ( section None ( help_desc_list @ ! full_desc_list ) )
full_speclist := normalize ! full_desc_list
;
let env_args = decode_env_to_argv ( try Unix . getenv env_var with Not_found -> " " ) in
(* begin transitional support for INFERCLANG_ARGS *)
@ -368,10 +422,16 @@ let parse ?(incomplete=false) env_var exe_usage =
let env_args = c_args @ env_args in
(* end transitional support for INFERCLANG_ARGS *)
let exe_name , env_cl_args = prepend_to_argv env_args in
let all_args = match config_file with
| None -> env_cl_args
| Some path ->
let json_args = decode_inferconfig_to_argv path in
(* read .inferconfig first, as both env vars and command-line options overwrite it *)
json_args @ env_cl_args in
let current = ref 0 in
let rec parse_loop () =
try
Arg . parse_argv_dynamic ~ current ( Array . of_list ( exe_name :: env_cl_args ) )
Arg . parse_argv_dynamic ~ current ( Array . of_list ( exe_name :: al l_args) )
curr_speclist ! anon_fun usage_msg
with
| Arg . Bad _ when incomplete -> parse_loop ()
@ -380,5 +440,5 @@ let parse ?(incomplete=false) env_var exe_usage =
in
parse_loop () ;
if not incomplete then
Unix . putenv env_var ( encode_argv_to_env ( prefix_before_rest env_c l_args) ) ;
Unix . putenv env_var ( encode_argv_to_env ( prefix_before_rest al l_args) ) ;
curr_usage