@ -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 = Infer Command) ? ( 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 = Infer Command) ? ( 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_s witch_parse_action
let mk_s ubcommand 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 ) ;
()