Add OCaml toplevel driver executable

Summary:
This diff changes the toplevel 'infer' executable from the current
python script to an OCaml binary.  Currently this executable only parses
command line arguments, sets up environment variables, and invokes the
existing python script.  This improves infer's command-line and
configuration interface, since passing arguments to the frontends or
backend no longer requires manually setting environment variables, and
arguments for the toplevel can now also be specified in .inferconfig.
Simplification and migration of functionality from the python script is
left for the future.

Reviewed By: martinoluca, jvillard

Differential Revision: D3450662

fbshipit-source-id: 1b52302
master
Josh Berdine 9 years ago committed by Facebook Github Bot 9
parent 7f3e99c3d4
commit a2a7e07708

1
.gitignore vendored

@ -71,6 +71,7 @@ buck-out/
/infer/bin/InferUnit /infer/bin/InferUnit
/infer/bin/Typeprop /infer/bin/Typeprop
/infer/bin/infer /infer/bin/infer
/infer/lib/infer
/infer/bin/inferTraceBugs /infer/bin/inferTraceBugs
/infer/src/backend/version.ml /infer/src/backend/version.ml
/infer/models/java/models/ /infer/models/java/models/

@ -2,7 +2,7 @@
## Top-level commands ## Top-level commands
*infer* : Main command to run Infer. It's a python script. Check out the docs for instructions on how to use it. *infer* : Main command to run Infer. Check out the docs for instructions on how to use it.
*inferTest* : Shell script for running Infer's tests. Uses Buck for running the tests. *inferTest* : Shell script for running Infer's tests. Uses Buck for running the tests.
Usage: inferTest {c, objc, java} for the tests about the analysis of C, Objective-C, or Java files. Usage: inferTest {c, objc, java} for the tests about the analysis of C, Objective-C, or Java files.

@ -22,7 +22,12 @@ TARGETS_TO_TEST := $(shell echo $(TARGETS_TO_TEST))
all: infer inferTraceBugs all: infer inferTraceBugs
$(INFER_BIN_RELPATH) $(INFERTRACEBUGS_BIN_RELPATH): $(INFER_BIN_SYMLINK):
($(REMOVE) $@ && \
cd $(@D) && \
$(LN_S) ../lib/$(@F) $(@F))
$(INFERTRACEBUGS_BIN_RELPATH):
($(REMOVE) $@ && \ ($(REMOVE) $@ && \
cd $(@D) && \ cd $(@D) && \
$(LN_S) ../lib/python/$(@F) $(@F)) $(LN_S) ../lib/python/$(@F) $(@F))
@ -36,7 +41,7 @@ ifeq ($(BUILD_C_ANALYZERS),yes)
src_build: clang_plugin src_build: clang_plugin
endif endif
infer: $(INFER_BIN_RELPATH) src_build infer: $(INFER_BIN_SYMLINK) src_build
ifeq ($(BUILD_JAVA_ANALYZERS),yes) ifeq ($(BUILD_JAVA_ANALYZERS),yes)
$(MAKE) -C $(ANNOTATIONS_DIR) $(MAKE) -C $(ANNOTATIONS_DIR)
endif endif
@ -213,19 +218,20 @@ endif
@for i in $$(find infer/lib/python/inferlib/* -type f); do \ @for i in $$(find infer/lib/python/inferlib/* -type f); do \
$(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \ $(INSTALL_DATA) -C $$i $(DESTDIR)$(libdir)/infer/$$i; \
done done
$(INSTALL_PROGRAM) -C infer/lib/python/infer \ $(INSTALL_PROGRAM) -C infer/lib/python/infer.py \
$(DESTDIR)$(libdir)/infer/infer/lib/python/infer $(DESTDIR)$(libdir)/infer/infer/lib/python/infer.py
$(INSTALL_PROGRAM) -C infer/lib/python/inferTraceBugs \ $(INSTALL_PROGRAM) -C infer/lib/python/inferTraceBugs \
$(DESTDIR)$(libdir)/infer/infer/lib/python/inferTraceBugs $(DESTDIR)$(libdir)/infer/infer/lib/python/inferTraceBugs
$(INSTALL_PROGRAM) -C $(INFER_BIN) $(DESTDIR)$(libdir)/infer/infer/lib/
$(INSTALL_PROGRAM) -C $(INFERANALYZE_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/ $(INSTALL_PROGRAM) -C $(INFERANALYZE_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/
$(INSTALL_PROGRAM) -C $(INFERPRINT_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/ $(INSTALL_PROGRAM) -C $(INFERPRINT_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/
$(INSTALL_PROGRAM) -C $(INFERSTATS_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/ $(INSTALL_PROGRAM) -C $(INFERSTATS_BIN) $(DESTDIR)$(libdir)/infer/infer/bin/
(cd $(DESTDIR)$(libdir)/infer/infer/bin/ && \ (cd $(DESTDIR)$(libdir)/infer/infer/bin/ && \
$(REMOVE) infer && \ $(REMOVE) infer && \
$(LN_S) $(libdir)/infer/infer/lib/python/infer infer) $(LN_S) $(libdir)/infer/infer/lib/infer infer)
(cd $(DESTDIR)$(bindir)/ && \ (cd $(DESTDIR)$(bindir)/ && \
$(REMOVE) infer && \ $(REMOVE) infer && \
$(LN_S) $(libdir)/infer/infer/lib/python/infer infer) $(LN_S) $(libdir)/infer/infer/lib/infer infer)
(cd $(DESTDIR)$(bindir)/ && \ (cd $(DESTDIR)$(bindir)/ && \
$(REMOVE) inferTraceBugs && \ $(REMOVE) inferTraceBugs && \
$(LN_S) $(libdir)/infer/infer/lib/python/inferTraceBugs inferTraceBugs) $(LN_S) $(libdir)/infer/infer/lib/python/inferTraceBugs inferTraceBugs)
@ -245,7 +251,7 @@ endif
$(MAKE) -C $(SRC_DIR) clean $(MAKE) -C $(SRC_DIR) clean
$(MAKE) -C $(ANNOTATIONS_DIR) clean $(MAKE) -C $(ANNOTATIONS_DIR) clean
$(MAKE) -C $(MODELS_DIR) clean $(MAKE) -C $(MODELS_DIR) clean
$(REMOVE) $(INFER_BIN_RELPATH) $(INFERTRACEBUGS_BIN_RELPATH) $(REMOVE) $(INFER_BIN_SYMLINK) $(INFERTRACEBUGS_BIN_RELPATH)
ifeq ($(IS_FACEBOOK_TREE),yes) ifeq ($(IS_FACEBOOK_TREE),yes)
$(MAKE) -C facebook clean $(MAKE) -C facebook clean
endif endif

@ -98,10 +98,10 @@ INFERJAVA_BIN = $(BIN_DIR)/InferJava
INFERSTATS_BIN = $(BIN_DIR)/InferStatsAggregator INFERSTATS_BIN = $(BIN_DIR)/InferStatsAggregator
INFERPRINT_BIN = $(BIN_DIR)/InferPrint INFERPRINT_BIN = $(BIN_DIR)/InferPrint
INFERUNIT_BIN = $(BIN_DIR)/InferUnit INFERUNIT_BIN = $(BIN_DIR)/InferUnit
INFER_BIN = $(BIN_DIR)/infer INFER_BIN = $(LIB_DIR)/infer
INFERTRACEBUGS_BIN = $(BIN_DIR)/inferTraceBugs INFERTRACEBUGS_BIN = $(BIN_DIR)/inferTraceBugs
# paths relative to $(ROOT_DIR) # paths relative to $(ROOT_DIR)
INFER_BIN_RELPATH = infer/bin/infer INFER_BIN_SYMLINK = infer/bin/infer
INFERTRACEBUGS_BIN_RELPATH = infer/bin/inferTraceBugs INFERTRACEBUGS_BIN_RELPATH = infer/bin/inferTraceBugs
ifeq ($(BUILD_JAVA_ANALYZERS),yes) ifeq ($(BUILD_JAVA_ANALYZERS),yes)

@ -153,7 +153,6 @@ infer_group.add_argument('--infer_cache', metavar='<directory>',
infer_group.add_argument('-pr', '--project_root', infer_group.add_argument('-pr', '--project_root',
dest='project_root', dest='project_root',
default=get_pwd(), default=get_pwd(),
type=utils.decode,
help='Location of the project root ' help='Location of the project root '
'(default is current directory)') '(default is current directory)')

@ -49,7 +49,7 @@ INFER_SCRIPT = """\
import subprocess import subprocess
import sys import sys
cmd = ['{0}'] + {1} + ['--', 'javac'] + sys.argv[1:] cmd = {1} + ['--', 'javac'] + sys.argv[1:]
subprocess.check_call(cmd) subprocess.check_call(cmd)
""" """

@ -45,6 +45,7 @@ endif
#### Backend declarations #### #### Backend declarations ####
INFER_MAIN = backend/infer
INFERANALYZE_MAIN = backend/inferanalyze INFERANALYZE_MAIN = backend/inferanalyze
#### InferPrint declarations #### #### InferPrint declarations ####
@ -130,6 +131,7 @@ OCAMLBUILD_ALL = $(OCAMLBUILD_BASE) $(JAVA_OCAMLBUILD_OPTIONS)
# list of ocamlbuild targets common to all build targets -- native version # list of ocamlbuild targets common to all build targets -- native version
INFER_BASE_TARGETS = \ INFER_BASE_TARGETS = \
$(INFER_MAIN).native \
$(INFERANALYZE_MAIN).native \ $(INFERANALYZE_MAIN).native \
$(INFERPRINT_MAIN).native \ $(INFERPRINT_MAIN).native \
$(INFERUNIT_MAIN).native \ $(INFERUNIT_MAIN).native \
@ -166,6 +168,7 @@ all: infer
infer: init $(INFERPRINT_ATDGEN_STUBS) infer: init $(INFERPRINT_ATDGEN_STUBS)
$(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) $(INFER_CONFIG_TARGETS) $(OCAMLBUILD_CONFIG) -build-dir $(INFER_BUILD_DIR) $(INFER_CONFIG_TARGETS)
$(COPY) $(INFER_BUILD_DIR)/$(INFER_MAIN).native $(INFER_BIN)
$(COPY) $(INFER_BUILD_DIR)/$(INFERANALYZE_MAIN).native $(INFERANALYZE_BIN) $(COPY) $(INFER_BUILD_DIR)/$(INFERANALYZE_MAIN).native $(INFERANALYZE_BIN)
$(COPY) $(INFER_BUILD_DIR)/$(INFERPRINT_MAIN).native $(INFERPRINT_BIN) $(COPY) $(INFER_BUILD_DIR)/$(INFERPRINT_MAIN).native $(INFERPRINT_BIN)
$(COPY) $(INFER_BUILD_DIR)/$(CHECKCOPYRIGHT_MAIN).native $(CHECKCOPYRIGHT_BIN) $(COPY) $(INFER_BUILD_DIR)/$(CHECKCOPYRIGHT_MAIN).native $(CHECKCOPYRIGHT_BIN)
@ -226,7 +229,7 @@ test_build: init $(INFERPRINT_ATDGEN_STUBS) $(CLANG_ATDGEN_STUBS) $(INFER_CLANG_
$(DEPENDENCIES_DIR)/ocamldot/ocamldot: $(DEPENDENCIES_DIR)/ocamldot/ocamldot:
$(MAKE) -C $(DEPENDENCIES_DIR)/ocamldot $(MAKE) -C $(DEPENDENCIES_DIR)/ocamldot
roots:=Inferanalyze CMain JMain Inferprint roots:=Infer Inferanalyze CMain JMain Inferprint
src_dirs:=$(shell find * -type d) src_dirs:=$(shell find * -type d)
src_files:=$(shell find $(src_dirs) -regex '.*\.ml\(i\)*' -not -path facebook/scripts/eradicate_stats.ml) src_files:=$(shell find $(src_dirs) -regex '.*\.ml\(i\)*' -not -path facebook/scripts/eradicate_stats.ml)
inc_flags:=$(foreach dir,$(src_dirs),-I $(dir)) inc_flags:=$(foreach dir,$(src_dirs),-I $(dir))
@ -317,8 +320,9 @@ endif
$(REMOVE) backend/version.ml $(REMOVE) backend/version.ml
$(REMOVE) backend/version.ml.tmp.* $(REMOVE) backend/version.ml.tmp.*
$(REMOVE) backend/jsonbug_{j,t}.ml{,i} $(REMOVE) backend/jsonbug_{j,t}.ml{,i}
$(REMOVE) $(INFERJAVA_BIN) $(INFERCLANG_BIN) $(INFERLLVM_BIN) $(INFERUNIT_BIN) $(REMOVE) $(INFER_BIN) $(INFERANALYZE_BIN) $(INFERPRINT_BIN) $(STATSAGGREGATOR_BIN)
$(REMOVE) $(INFERANALYZE_BIN) $(INFERPRINT_BIN) $(CHECKCOPYRIGHT_BIN) $(STATSAGGREGATOR_BIN) $(REMOVE) $(INFERJAVA_BIN) $(INFERCLANG_BIN) $(INFERLLVM_BIN)
$(REMOVE) $(INFERUNIT_BIN) $(CHECKCOPYRIGHT_BIN)
$(REMOVE) $(CLANG_ATDGEN_STUBS) $(REMOVE) $(CLANG_ATDGEN_STUBS)
$(REMOVE) $(INFER_CLANG_FCP_MIRRORED_FILES) $(REMOVE) $(INFER_CLANG_FCP_MIRRORED_FILES)
$(REMOVE) mod_dep.dot $(REMOVE) mod_dep.dot

@ -144,7 +144,9 @@ let mk ?(deprecated=[]) ?(exes=[])
let closure = mk_setter variable in let closure = mk_setter variable in
let setter str = let setter str =
try closure str try closure str
with _ -> raise (Arg.Bad ("bad value " ^ str ^ " for flag " ^ long)) in with exc ->
raise (Arg.Bad ("bad value " ^ str ^ " for flag " ^ long
^ " (" ^ (Printexc.to_string exc) ^ ")")) in
let spec = mk_spec setter in let spec = mk_spec setter in
let doc = let doc =
let default_string = default_to_string default in let default_string = default_to_string default in
@ -297,7 +299,7 @@ let mk_symbol_seq ?(default=[]) ~symbols ?(deprecated=[]) ~long ?short ?exes ?(m
let sym_to_str = IList.map (fun (x,y) -> (y,x)) symbols in let sym_to_str = IList.map (fun (x,y) -> (y,x)) symbols in
let of_string str = IList.assoc string_equal str symbols in let of_string str = IList.assoc string_equal str symbols in
let to_string sym = IList.assoc ( = ) sym sym_to_str in let to_string sym = IList.assoc ( = ) sym sym_to_str in
mk ~deprecated ~long ?short ~default ?exes ~meta:(" ,-separated sequence" ^ meta) doc mk ~deprecated ~long ?short ~default ?exes ~meta:(",-separated sequence" ^ meta) doc
~default_to_string:(fun syms -> String.concat " " (IList.map to_string syms)) ~default_to_string:(fun syms -> String.concat " " (IList.map to_string syms))
~mk_setter:(fun var str_seq -> ~mk_setter:(fun var str_seq ->
var := IList.map of_string (Str.split (Str.regexp_string ",") str_seq)) var := IList.map of_string (Str.split (Str.regexp_string ",") str_seq))
@ -314,13 +316,22 @@ let mk_set_from_json ~default ~default_to_string ~f
~decode_json:(fun json -> [dashdash long; Yojson.Basic.to_string json]) ~decode_json:(fun json -> [dashdash long; Yojson.Basic.to_string json])
~mk_spec:(fun set -> Arg.String set) ~mk_spec:(fun set -> Arg.String set)
(* A ref to a function used during argument parsing to process anonymous arguments. By default,
anonymous arguments are rejected. *)
let anon_fun = ref (fun arg -> raise (Arg.Bad ("unexpected anonymous argument: " ^ arg))) let anon_fun = ref (fun arg -> raise (Arg.Bad ("unexpected anonymous argument: " ^ arg)))
(* Clients declare that anonymous arguments are acceptable by calling [mk_anon], which returns a ref
storing the anonymous arguments. *)
let mk_anon () = let mk_anon () =
let anon = ref [] in let anon = ref [] in
anon_fun := (fun arg -> anon := arg :: !anon) ; anon_fun := (fun arg -> anon := arg :: !anon) ;
anon anon
let mk_rest ?(exes=[]) doc =
let rest = ref [] in
let spec = Arg.Rest (fun arg -> rest := arg :: !rest) in
add exes {long = "--"; short = ""; meta = ""; doc; spec; decode_json = fun _ -> []} ;
rest
let decode_inferconfig_to_argv path = let decode_inferconfig_to_argv path =
let json = match read_optional_json_file path with let json = match read_optional_json_file path with
@ -378,7 +389,7 @@ let prefix_before_rest args =
prefix_before_rest_ [] args prefix_before_rest_ [] args
let parse ?(incomplete=false) ?config_file env_var exe_usage = let parse ?(incomplete=false) ?(accept_unknown=false) ?config_file env_var exe_usage =
let curr_speclist = ref [] let curr_speclist = ref []
and full_speclist = ref [] and full_speclist = ref []
in in
@ -446,13 +457,18 @@ let parse ?(incomplete=false) ?config_file env_var exe_usage =
let json_args = decode_inferconfig_to_argv path in let json_args = decode_inferconfig_to_argv path in
(* read .inferconfig first, as both env vars and command-line options overwrite it *) (* read .inferconfig first, as both env vars and command-line options overwrite it *)
json_args @ env_cl_args in json_args @ env_cl_args in
let argv = Array.of_list (exe_name :: all_args) in
let current = ref 0 in let current = ref 0 in
(* tests if msg indicates an unknown option, as opposed to a known option with bad argument *)
let is_unknown msg =
let prefix = exe_name ^ ": unknown option" in
prefix = (String.sub msg 0 (String.length prefix)) in
let rec parse_loop () = let rec parse_loop () =
try try
Arg.parse_argv_dynamic ~current (Array.of_list (exe_name :: all_args)) Arg.parse_argv_dynamic ~current argv curr_speclist !anon_fun usage_msg
curr_speclist !anon_fun usage_msg
with with
| Arg.Bad _ when incomplete -> parse_loop () | Arg.Bad _ when incomplete -> parse_loop ()
| Arg.Bad msg when accept_unknown && is_unknown msg -> !anon_fun argv.(!current) ; parse_loop ()
| Arg.Bad usage_msg -> Pervasives.prerr_string usage_msg; exit 2 | Arg.Bad usage_msg -> Pervasives.prerr_string usage_msg; exit 2
| Arg.Help usage_msg -> Pervasives.print_string usage_msg; exit 0 | Arg.Help usage_msg -> Pervasives.print_string usage_msg; exit 0
in in

@ -90,6 +90,13 @@ val mk_anon :
unit -> unit ->
string list ref string list ref
(** [mk_rest doc] defines a [string list ref] of the command line arguments following ["--"], in the
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]. *)
val mk_rest :
?exes:exe list -> string ->
string list ref
(** [parse env_var exe_usage] parses command line arguments as specified by preceding calls to the (** [parse env_var exe_usage] parses command line arguments as specified by preceding calls to the
[mk_*] functions, and returns a function that prints the usage message and help text then exits. [mk_*] functions, and returns a function that prints the usage message and help text then exits.
The decoded values of the inferconfig file [config_file], if provided, and of the environment The decoded values of the inferconfig file [config_file], if provided, and of the environment
@ -97,6 +104,8 @@ val mk_anon :
the command line supersede those specified in the environment variable, which themselves the command line supersede those specified in the environment variable, which themselves
supersede those passed via the config file. WARNING: An argument will be interpreted as many supersede those passed via 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 times as it appears in all of the config file, the environment variable, and the command
line. *) line. The [env_var] is set to the full set of options parsed. If [incomplete] is set, unknown
val parse : ?incomplete:bool -> ?config_file:string -> options are ignored, and [env_var] is not set. If [accept_unknown] is set, unknown options are
treated the same as anonymous arguments. *)
val parse : ?incomplete:bool -> ?accept_unknown:bool -> ?config_file:string ->
string -> (exe -> Arg.usage_msg) -> (int -> 'a) string -> (exe -> Arg.usage_msg) -> (int -> 'a)

@ -35,6 +35,15 @@ let string_of_language = function
type clang_lang = C | CPP | OBJC | OBJCPP type clang_lang = C | CPP | OBJC | OBJCPP
let ml_bucket_symbols = [
("all", `MLeak_all);
("cf", `MLeak_cf);
("arc", `MLeak_arc);
("narc", `MLeak_no_arc);
("cpp", `MLeak_cpp);
("unknown_origin", `MLeak_unknown);
]
type os_type = Unix | Win32 | Cygwin type os_type = Unix | Win32 | Cygwin
type zip_library = { type zip_library = {
@ -43,6 +52,17 @@ type zip_library = {
models: bool; models: bool;
} }
let whitelisted_cpp_methods = [
["std"; "move"];
["google"; "CheckNotNull"];
["google"; "GetReferenceableValue"];
["google"; "Check_NEImpl"];
["google"; "Check_LEImpl"];
["google"; "Check_GTImpl"];
["google"; "Check_GEImpl"];
["google"; "Check_EQImpl"]
]
(** Constant configuration values *) (** Constant configuration values *)
@ -183,7 +203,7 @@ let buck_generated_folder = "buck-out/gen"
let version_string = let version_string =
"Infer version " "Infer version "
^ Version.versionString ^ Version.versionString
^ "\nCopyright 2009 - present Facebook. All Rights Reserved.\n" ^ "\nCopyright 2009 - present Facebook. All Rights Reserved."
(** System call configuration values *) (** System call configuration values *)
@ -195,27 +215,21 @@ let initial_analysis_time = Unix.time ()
(** Path to lib/specs to retrieve the default models *) (** Path to lib/specs to retrieve the default models *)
let models_dir = let models_dir =
let bin_dir = Filename.dirname Sys.executable_name in let bin_dir = Filename.dirname Sys.executable_name in
let lib_dir = Filename.concat (Filename.concat bin_dir Filename.parent_dir_name) "lib" in let lib_dir = bin_dir // Filename.parent_dir_name // "lib" in
let lib_specs_dir = Filename.concat lib_dir specs_dir_name in let lib_specs_dir = lib_dir // specs_dir_name in
lib_specs_dir lib_specs_dir
let cpp_models_dir = let cpp_models_dir =
let bin_dir = Filename.dirname Sys.executable_name in let bin_dir = Filename.dirname Sys.executable_name in
let cpp_models_dir = bin_dir // Filename.parent_dir_name // "models" // "cpp" // "include"
Filename.concat (Filename.concat bin_dir Filename.parent_dir_name)
"models/cpp/include" in
cpp_models_dir
let whitelisted_cpp_methods = [ let ncpu =
["std"; "move"]; try
["google"; "CheckNotNull"]; with_process_in
["google"; "GetReferenceableValue"]; "getconf _NPROCESSORS_ONLN 2>/dev/null"
["google"; "Check_NEImpl"]; (fun chan -> Scanf.fscanf chan "%d" (fun n -> n))
["google"; "Check_LEImpl"]; with _ ->
["google"; "Check_GTImpl"]; 1
["google"; "Check_GEImpl"];
["google"; "Check_EQImpl"]
]
let os_type = match Sys.os_type with let os_type = match Sys.os_type with
| "Win32" -> Win32 | "Win32" -> Win32
@ -321,6 +335,38 @@ let patterns_of_json_with_key json_key json =
(** Command Line options *) (** Command Line options *)
(* The working directory of the initial invocation of infer, to which paths passed as command line
options are relative. *)
let init_work_dir =
try
Sys.getenv "INFER_CWD"
with Not_found ->
let cwd =
(* Use PWD if it denotes the same inode as ., to try to avoid paths with symlinks resolved *)
(* Approach is borrowed from llvm implementation of *)
(* llvm::sys::fs::current_path (implemented in Path.inc file) *)
try
let pwd = Unix.getenv "PWD" in
let pwd_stat = Unix.stat pwd in
let dot_stat = Unix.stat "." in
if pwd_stat.st_dev = dot_stat.st_dev && pwd_stat.st_ino = dot_stat.st_ino then
pwd
else
Sys.getcwd ()
with _ ->
Sys.getcwd ()
in
Unix.putenv "INFER_CWD" cwd ;
cwd
(* Resolve relative paths passed as command line options, i.e., with respect to the working
directory of the initial invocation of infer. *)
let resolve path =
if Filename.is_relative path then
init_work_dir // path
else
path
(* Declare the phase 1 options *) (* Declare the phase 1 options *)
let inferconfig_home = let inferconfig_home =
@ -328,10 +374,10 @@ let inferconfig_home =
~exes:CLOpt.[Analyze] ~meta:"dir" "Path to the .inferconfig file" ~exes:CLOpt.[Analyze] ~meta:"dir" "Path to the .inferconfig file"
and project_root = and project_root =
CLOpt.mk_string_opt ~deprecated:["project_root"] ~long:"project-root" ~short:"pr" CLOpt.mk_string_opt ~deprecated:["project_root"; "-project_root"] ~long:"project-root" ~short:"pr"
?default:(if CLOpt.(current_exe = Print) then Some (Sys.getcwd ()) else None) ?default:CLOpt.(match current_exe with Print | Toplevel -> Some (Sys.getcwd ()) | _ -> None)
~f:filename_to_absolute ~f:filename_to_absolute
~exes:CLOpt.[Analyze;Clang;Java;Llvm;Print] ~exes:CLOpt.[Analyze;Clang;Java;Llvm;Print;Toplevel]
~meta:"dir" "Specify the root directory of the project" ~meta:"dir" "Specify the root directory of the project"
(* Parse the phase 1 options, ignoring the rest *) (* Parse the phase 1 options, ignoring the rest *)
@ -345,7 +391,7 @@ and project_root = !project_root
let inferconfig_path = let inferconfig_path =
match inferconfig_home, project_root with match inferconfig_home, project_root with
| Some dir, _ | _, Some dir -> Filename.concat dir inferconfig_file | Some dir, _ | _, Some dir -> dir // inferconfig_file
| None, None -> inferconfig_file | None, None -> inferconfig_file
(* Proceed to declare and parse the remaining options *) (* Proceed to declare and parse the remaining options *)
@ -361,6 +407,14 @@ let inferconfig_path =
let anon_args = let anon_args =
CLOpt.mk_anon () CLOpt.mk_anon ()
and rest =
CLOpt.mk_rest
~exes:CLOpt.[Toplevel] "Stop argument processing, use remaining arguments as a build command"
and absolute_paths =
CLOpt.mk_bool ~long:"absolute-paths"
~exes:CLOpt.[Toplevel] "Report errors using absolute paths"
(** Flag for abstracting fields of structs (** Flag for abstracting fields of structs
0 = no 0 = no
1 = forget some fields during matching (and so lseg abstraction) *) 1 = forget some fields during matching (and so lseg abstraction) *)
@ -432,10 +486,14 @@ and analysis_stops =
(** Setup the analyzer in order to filter out errors for this analyzer only *) (** Setup the analyzer in order to filter out errors for this analyzer only *)
and analyzer = and analyzer =
CLOpt.mk_symbol_opt ~deprecated:["analyzer"] ~long:"analyzer" CLOpt.mk_symbol_opt ~deprecated:["analyzer"] ~long:"analyzer" ~short:"a"
"Specify the analyzer for the path filtering" "Specify the analyzer for the path filtering"
~symbols:Utils.string_to_analyzer ~symbols:Utils.string_to_analyzer
and android_harness =
CLOpt.mk_bool ~deprecated:["harness"] ~long:"android-harness"
"Create harness to detect bugs involving the Android lifecycle"
(** if true, completely ignore the possibility that errors can be caused by unknown procedures (** if true, completely ignore the possibility that errors can be caused by unknown procedures
during the symbolic execution phase *) during the symbolic execution phase *)
and angelic_execution = and angelic_execution =
@ -455,6 +513,14 @@ and ast_file =
CLOpt.mk_string_opt ~long:"ast-file" ~short:"ast" CLOpt.mk_string_opt ~long:"ast-file" ~short:"ast"
~meta:"file" "AST file for the translation" ~meta:"file" "AST file for the translation"
and blacklist =
CLOpt.mk_string_opt ~deprecated:["-blacklist-regex"] ~long:"blacklist"
~meta:"regex" "Skip analysis of files matched by the specified regular expression"
and buck =
CLOpt.mk_bool ~long:"buck"
"To use when run with buck"
and buck_out = and buck_out =
CLOpt.mk_string_opt ~long:"buck-out" CLOpt.mk_string_opt ~long:"buck-out"
~exes:CLOpt.[StatsAggregator] ~meta:"dir" "Specify the root directory of buck-out" ~exes:CLOpt.[StatsAggregator] ~meta:"dir" "Specify the root directory of buck-out"
@ -526,7 +592,7 @@ and curr_language =
var var
and cxx_experimental = and cxx_experimental =
CLOpt.mk_bool ~deprecated:["cxx-experimental"] ~long:"cxx-experimental" CLOpt.mk_bool ~deprecated:["cxx-experimental"] ~long:"cxx"
"Analyze C++ methods, still experimental" "Analyze C++ methods, still experimental"
and debug, print_types, write_dotty = and debug, print_types, write_dotty =
@ -547,6 +613,10 @@ and debug, print_types, write_dotty =
in in
(debug, print_types, write_dotty) (debug, print_types, write_dotty)
and debug_exceptions =
CLOpt.mk_bool ~long:"debug-exceptions"
"Generate lightweight debugging information: just print the internal exceptions during analysis"
(* The classes in the given jar file will be translated. No sources needed *) (* The classes in the given jar file will be translated. No sources needed *)
and dependencies = and dependencies =
CLOpt.mk_bool ~deprecated:["dependencies"] ~long:"dependencies" CLOpt.mk_bool ~deprecated:["dependencies"] ~long:"dependencies"
@ -589,17 +659,29 @@ and err_file =
CLOpt.mk_string ~deprecated:["err_file"] ~long:"err-file" ~default:"" CLOpt.mk_string ~deprecated:["err_file"] ~long:"err-file" ~default:""
~exes:CLOpt.[Analyze] ~meta:"file" "use file for the err channel" ~exes:CLOpt.[Analyze] ~meta:"file" "use file for the err channel"
(* Generate harness for Android code *) and failures_allowed =
and harness = CLOpt.mk_bool ~deprecated_no:["-no_failures_allowed"] ~long:"failures-allowed" ~default:true
CLOpt.mk_bool ~deprecated:["harness"] ~long:"harness" "Fail if at least one of the translations fails"
"Create Android lifecycle harness"
and filtering =
CLOpt.mk_bool ~long:"filtering" ~short:"f" ~default:true
"Also show the results from the experimental checks. Warning: some checks may contain many \
false alarms."
and flavors =
CLOpt.mk_bool ~deprecated:["-use-flavors"] ~long:"flavors"
"Buck integration using flavors."
and frontend_stats =
CLOpt.mk_bool ~long:"frontend-stats" ~short:"fs"
"Output statistics about the capture phase to *.o.astlog"
and headers = and headers =
CLOpt.mk_bool ~deprecated:["headers"] ~deprecated_no:["no_headers"] ~long:"headers" CLOpt.mk_bool ~deprecated:["headers"] ~deprecated_no:["no_headers"] ~long:"headers" ~short:"hd"
"Translate code in header files" "Analyze code in header files"
and infer_cache = and infer_cache =
CLOpt.mk_string_opt ~deprecated:["infer_cache"] ~long:"infer-cache" CLOpt.mk_string_opt ~deprecated:["infer_cache"; "-infer_cache"] ~long:"infer-cache"
~f:filename_to_absolute ~f:filename_to_absolute
~meta:"dir" "Select a directory to contain the infer cache" ~meta:"dir" "Select a directory to contain the infer cache"
@ -611,6 +693,10 @@ and iterations =
"Specify the maximum number of operations for each function, expressed as a multiple \ "Specify the maximum number of operations for each function, expressed as a multiple \
of symbolic operations" of symbolic operations"
and jobs =
CLOpt.mk_int ~deprecated:["-multicore"] ~long:"jobs" ~short:"j" ~default:ncpu
~exes:CLOpt.[Toplevel] ~meta:"int" "Run the specified number of analysis jobs simultaneously"
(** Flag to tune the final information-loss check used by the join (** Flag to tune the final information-loss check used by the join
0 = use the most aggressive join for preconditions 0 = use the most aggressive join for preconditions
1 = use the least aggressive join for preconditions 1 = use the least aggressive join for preconditions
@ -624,11 +710,19 @@ and latex =
CLOpt.mk_option ~deprecated:["latex"] ~long:"latex" ~f:create_outfile CLOpt.mk_option ~deprecated:["latex"] ~long:"latex" ~f:create_outfile
~meta:"file.tex" "Print latex report to file.tex" ~meta:"file.tex" "Print latex report to file.tex"
and load_average =
CLOpt.mk_option ~long:"load-average" ~short:"l" ~f:(fun s -> Some (float_of_string s))
~meta:"float"
"Do not start new parallel jobs if the load average is greater than that specified"
(** name of the file to load analysis results from *) (** name of the file to load analysis results from *)
and load_results = and load_results =
CLOpt.mk_string_opt ~deprecated:["load_results"] ~long:"load-results" CLOpt.mk_string_opt ~deprecated:["load_results"] ~long:"load-results"
~meta:"file.iar" "Load analysis results from Infer Analysis Results file file.iar" ~meta:"file.iar" "Load analysis results from Infer Analysis Results file file.iar"
and llvm =
CLOpt.mk_bool ~long:"llvm" "Analyze C or C++ using the experimental LLVM frontend"
(** name of the makefile to create with clusters and dependencies *) (** name of the makefile to create with clusters and dependencies *)
and makefile = and makefile =
CLOpt.mk_string ~deprecated:["makefile"] ~long:"makefile" ~default:"" CLOpt.mk_string ~deprecated:["makefile"] ~long:"makefile" ~default:""
@ -641,26 +735,22 @@ and merge =
(** List of obj memory leak buckets to be checked in Objective-C/C++ *) (** List of obj memory leak buckets to be checked in Objective-C/C++ *)
and ml_buckets = and ml_buckets =
CLOpt.mk_symbol_seq ~deprecated:["ml_buckets"] ~long:"ml-buckets" ~default:[`MLeak_cf] CLOpt.mk_symbol_seq ~deprecated:["ml_buckets"; "-ml_buckets"] ~long:"ml-buckets"
~default:[`MLeak_cf]
~exes:CLOpt.[Toplevel]
"Specify the memory leak buckets to be checked: \ "Specify the memory leak buckets to be checked: \
'cf' checks leaks from Core Foundation, \ 'cf' checks leaks from Core Foundation, \
'arc' from code compiled in ARC mode, \ 'arc' from code compiled in ARC mode, \
'narc' from code not compiled in ARC mode, \ 'narc' from code not compiled in ARC mode, \
'cpp' from C++ code" 'cpp' from C++ code"
~symbols:[ ~symbols:ml_bucket_symbols
("all", `MLeak_all);
("cf", `MLeak_cf);
("arc", `MLeak_arc);
("narc", `MLeak_no_arc);
("cpp", `MLeak_cpp);
("unknown_origin", `MLeak_unknown)]
and models_file = and models_file =
CLOpt.mk_string_opt ~deprecated:["models"] ~long:"models" CLOpt.mk_string_opt ~deprecated:["models"] ~long:"models"
~exes:CLOpt.[Analyze;Java] ~meta:"zip file" "add a zip file containing the models" ~exes:CLOpt.[Analyze;Java] ~meta:"zip file" "add a zip file containing the models"
and models_mode = and models_mode =
CLOpt.mk_bool ~deprecated:["models_mode"] ~long:"models-mode" CLOpt.mk_bool ~deprecated:["models_mode"; "-models_mode"] ~long:"models-mode"
"Mode for computing the models" "Mode for computing the models"
and modified_targets = and modified_targets =
@ -759,10 +849,10 @@ and reports_include_ml_loc =
"Include the location (in the Infer source code) from where reports are generated" "Include the location (in the Infer source code) from where reports are generated"
and results_dir = and results_dir =
CLOpt.mk_string ~deprecated:["results_dir"] ~long:"results-dir" CLOpt.mk_string ~deprecated:["results_dir"; "-out"] ~long:"results-dir" ~short:"o"
~default:(Filename.concat (Sys.getcwd ()) "infer-out") ~default:(Sys.getcwd () // "infer-out")
~exes:CLOpt.[Analyze;Clang;Java;Llvm;Print;StatsAggregator] ~exes:CLOpt.[Analyze;Clang;Java;Llvm;Print;StatsAggregator]
~meta:"dir" "Specify the project results directory" ~meta:"dir" "Write results in the specified directory"
(** name of the file to load save results to *) (** name of the file to load save results to *)
and save_results = and save_results =
@ -813,13 +903,14 @@ and specs_library =
let validate_path path = let validate_path path =
if Filename.is_relative path then if Filename.is_relative path then
failwith ("Failing because path " ^ path ^ " is not absolute") in failwith ("Failing because path " ^ path ^ " is not absolute") in
match read_file fname with match read_file (resolve fname) with
| Some pathlist -> | Some pathlist ->
IList.iter validate_path pathlist; IList.iter validate_path pathlist;
pathlist pathlist
| None -> failwith ("cannot read file " ^ fname) | None -> failwith ("cannot read file " ^ fname ^ " from cwd " ^ (Sys.getcwd ()))
in in
CLOpt.mk_string ~deprecated:["specs-dir-list-file"] ~long:"specs-library-index" CLOpt.mk_string ~deprecated:["specs-dir-list-file"; "-specs-dir-list-file"]
~long:"specs-library-index"
~default:"" ~default:""
~f:(fun file -> specs_library := (read_specs_dir_list_file file) @ !specs_library; "") ~f:(fun file -> specs_library := (read_specs_dir_list_file file) @ !specs_library; "")
~exes:CLOpt.[Analyze] ~meta:"file" ~exes:CLOpt.[Analyze] ~meta:"file"
@ -870,7 +961,7 @@ and test_filtering =
"List all the files Infer can report on (should be call at the root of the project)" "List all the files Infer can report on (should be call at the root of the project)"
and testing_mode = and testing_mode =
CLOpt.mk_bool ~deprecated:["testing_mode"] ~long:"testing-mode" CLOpt.mk_bool ~deprecated:["testing_mode"; "-testing_mode"] ~long:"testing-mode" ~short:"tm"
"Mode for testing, where no headers are translated, and dot files are created" "Mode for testing, where no headers are translated, and dot files are created"
(** Flag set to enable detailed tracing informatin during error explanation *) (** Flag set to enable detailed tracing informatin during error explanation *)
@ -1046,7 +1137,7 @@ let use_jar_cache = true
let exe_usage (exe : CLOpt.exe) = let exe_usage (exe : CLOpt.exe) =
match exe with match exe with
| Analyze -> | Analyze ->
version_string ^ "\ version_string ^ "\n\
Usage: InferAnalyze [options]\n\ Usage: InferAnalyze [options]\n\
Analyze the files captured in the project results directory, \ Analyze the files captured in the project results directory, \
which can be specified with the --results-dir option." which can be specified with the --results-dir option."
@ -1063,7 +1154,7 @@ let exe_usage (exe : CLOpt.exe) =
To process all the .specs in the results directory, use option --results-dir \ To process all the .specs in the results directory, use option --results-dir \
Each spec is printed to standard output unless option -q is used." Each spec is printed to standard output unless option -q is used."
| StatsAggregator -> | StatsAggregator ->
"Usage: InferStatsAggregator --results-dir <dir> --buck-out <dir>\n \ "Usage: InferStatsAggregator --results-dir <dir> --buck-out <dir>\n\
Aggregates all the perf stats generated by Buck on each target" Aggregates all the perf stats generated by Buck on each target"
| Toplevel -> | Toplevel ->
version_string version_string
@ -1072,7 +1163,8 @@ let post_parsing_initialization () =
F.set_margin !margin ; F.set_margin !margin ;
if !version then ( if !version then (
F.fprintf F.std_formatter "%s@." version_string ; (* TODO(11791235) change back to stdout once buck integration is fixed *)
F.fprintf F.err_formatter "%s@." version_string ;
exit 0 exit 0
); );
if !version_json then ( if !version_json then (
@ -1115,7 +1207,7 @@ let post_parsing_initialization () =
let zip_channel = Zip.open_in zip_filename in let zip_channel = Zip.open_in zip_filename in
let entries = Zip.entries zip_channel in let entries = Zip.entries zip_channel in
let extract_entry entry = let extract_entry entry =
let dest_file = Filename.concat dest_dir (Filename.basename entry.Zip.filename) in let dest_file = dest_dir // (Filename.basename entry.Zip.filename) in
if Filename.check_suffix entry.Zip.filename specs_files_suffix if Filename.check_suffix entry.Zip.filename specs_files_suffix
then Zip.copy_entry_to_file zip_channel entry dest_file in then Zip.copy_entry_to_file zip_channel entry dest_file in
IList.iter extract_entry entries; IList.iter extract_entry entries;
@ -1123,7 +1215,7 @@ let post_parsing_initialization () =
in in
let basename = Filename.basename zip_filename in let basename = Filename.basename zip_filename in
let key = basename ^ string_crc_hex32 zip_filename in let key = basename ^ string_crc_hex32 zip_filename in
let key_dir = Filename.concat cache_dir key in let key_dir = cache_dir // key in
if (mkdir key_dir) if (mkdir key_dir)
then extract_specs key_dir zip_filename; then extract_specs key_dir zip_filename;
specs_library := !specs_library @ [key_dir] specs_library := !specs_library @ [key_dir]
@ -1156,7 +1248,8 @@ let post_parsing_initialization () =
let parse_args_and_return_usage_exit = let parse_args_and_return_usage_exit =
let usage_exit = CLOpt.parse ~config_file:inferconfig_path "INFER_ARGS" exe_usage in let usage_exit =
CLOpt.parse ~accept_unknown:true ~config_file:inferconfig_path "INFER_ARGS" exe_usage in
if !debug || (!developer_mode && not (CLOpt.current_exe = CLOpt.Print)) then if !debug || (!developer_mode && not (CLOpt.current_exe = CLOpt.Print)) then
prerr_endline prerr_endline
((Filename.basename Sys.executable_name) ^ " got args " ((Filename.basename Sys.executable_name) ^ " got args "
@ -1170,8 +1263,10 @@ let print_usage_exit () =
(** Freeze initialized configuration values *) (** Freeze initialized configuration values *)
let anon_args = !anon_args let anon_args = IList.rev !anon_args
and rest = !rest
and abs_struct = !abs_struct and abs_struct = !abs_struct
and absolute_paths = !absolute_paths
and allow_specs_cleanup = !allow_specs_cleanup and allow_specs_cleanup = !allow_specs_cleanup
and analysis_path_regex_whitelist_options = and analysis_path_regex_whitelist_options =
IList.map (fun (a, b) -> (a, !b)) analysis_path_regex_whitelist_options IList.map (fun (a, b) -> (a, !b)) analysis_path_regex_whitelist_options
@ -1187,6 +1282,8 @@ and angelic_execution = !angelic_execution
and arc_mode = objc_arc and arc_mode = objc_arc
and array_level = !array_level and array_level = !array_level
and ast_file = !ast_file and ast_file = !ast_file
and blacklist = !blacklist
and buck = !buck
and buck_out = !buck_out and buck_out = !buck_out
and bugs_csv = !bugs_csv and bugs_csv = !bugs_csv
and bugs_json = !bugs_json and bugs_json = !bugs_json
@ -1201,9 +1298,10 @@ and clang_lang = !clang_lang
and cluster_cmdline = !cluster and cluster_cmdline = !cluster
and code_query = !code_query and code_query = !code_query
and continue_capture = !continue and continue_capture = !continue
and create_harness = !harness and create_harness = !android_harness
and cxx_experimental = !cxx_experimental and cxx_experimental = !cxx_experimental
and debug_mode = !debug and debug_mode = !debug
and debug_exceptions = !debug_exceptions
and dependency_mode = !dependencies and dependency_mode = !dependencies
and developer_mode = !developer_mode and developer_mode = !developer_mode
and disable_checks = !disable_checks and disable_checks = !disable_checks
@ -1211,12 +1309,20 @@ and dotty_cfg_libs = !dotty_cfg_libs
and enable_checks = !enable_checks and enable_checks = !enable_checks
and eradicate = !eradicate and eradicate = !eradicate
and err_file_cmdline = !err_file and err_file_cmdline = !err_file
and failures_allowed = !failures_allowed
and filtering = !filtering
and flavors = !flavors
and frontend_stats = !frontend_stats
and headers = !headers
and infer_cache = !infer_cache and infer_cache = !infer_cache
and iterations = !iterations and iterations = !iterations
and javac_verbose_out = !verbose_out and javac_verbose_out = !verbose_out
and jobs = !jobs
and join_cond = !join_cond and join_cond = !join_cond
and latex = !latex and latex = !latex
and load_average = !load_average
and load_analysis_results = !load_results and load_analysis_results = !load_results
and llvm = !llvm
and makefile_cmdline = !makefile and makefile_cmdline = !makefile
and merge = !merge and merge = !merge
and ml_buckets = !ml_buckets and ml_buckets = !ml_buckets

@ -30,6 +30,10 @@ type pattern =
type clang_lang = C | CPP | OBJC | OBJCPP type clang_lang = C | CPP | OBJC | OBJCPP
val ml_bucket_symbols :
(string * [ `MLeak_all | `MLeak_arc | `MLeak_cf | `MLeak_cpp | `MLeak_no_arc | `MLeak_unknown ])
list
type os_type = Unix | Win32 | Cygwin type os_type = Unix | Win32 | Cygwin
type zip_library = { type zip_library = {
@ -48,9 +52,12 @@ val assign : string
val attributes_dir_name : string val attributes_dir_name : string
val backend_stats_dir_name : string val backend_stats_dir_name : string
val bound_error_allowed_in_procedure_call : bool val bound_error_allowed_in_procedure_call : bool
val buck_generated_folder : string
val buck_infer_deps_file_name : string val buck_infer_deps_file_name : string
val checks_disabled_by_default : string list
val captured_dir_name : string val captured_dir_name : string
val checks_disabled_by_default : string list
val cpp_models_dir : string
val csl_analysis : bool
val default_failure_name : string val default_failure_name : string
val default_in_zip_results_dir : string val default_in_zip_results_dir : string
val dotty_output : string val dotty_output : string
@ -61,25 +68,24 @@ val idempotent_getters : bool
val incremental_procs : bool val incremental_procs : bool
val initial_analysis_time : float val initial_analysis_time : float
val ivar_attributes : string val ivar_attributes : string
val load_average : float option
val log_analysis_crash : string
val log_analysis_file : string val log_analysis_file : string
val log_analysis_procedure : string val log_analysis_procedure : string
val log_analysis_wallclock_timeout : string
val log_analysis_symops_timeout : string
val log_analysis_recursion_timeout : string val log_analysis_recursion_timeout : string
val log_analysis_crash : string val log_analysis_symops_timeout : string
val buck_generated_folder : string val log_analysis_wallclock_timeout : string
val max_recursion : int val max_recursion : int
val meet_level : int val meet_level : int
val models_dir : string val models_dir : string
val cpp_models_dir : string val ncpu : int
val whitelisted_cpp_methods : string list list
val nsnotification_center_checker_backend : bool val nsnotification_center_checker_backend : bool
val objc_method_call_semantics : bool val objc_method_call_semantics : bool
val os_type : os_type val os_type : os_type
val patterns_modeled_expensive : pattern list
val patterns_never_returning_null : pattern list val patterns_never_returning_null : pattern list
val patterns_suppress_warnings : pattern list
val patterns_skip_translation : pattern list val patterns_skip_translation : pattern list
val patterns_modeled_expensive : pattern list val patterns_suppress_warnings : pattern list
val perf_stats_prefix : string val perf_stats_prefix : string
val proc_stats_filename : string val proc_stats_filename : string
val property_attributes : string val property_attributes : string
@ -94,11 +100,11 @@ val specs_dir_name : string
val specs_files_suffix : string val specs_files_suffix : string
val start_filename : string val start_filename : string
val taint_analysis : bool val taint_analysis : bool
val csl_analysis : bool
val trace_absarray : bool val trace_absarray : bool
val undo_join : bool val undo_join : bool
val unsafe_unret : string val unsafe_unret : string
val weak : string val weak : string
val whitelisted_cpp_methods : string list list
(** Configuration values specified by environment variables *) (** Configuration values specified by environment variables *)
@ -115,7 +121,9 @@ val sound_dynamic_dispatch : bool
(** Configuration values specified by command-line options *) (** Configuration values specified by command-line options *)
val anon_args : string list val anon_args : string list
val rest : string list
val abs_struct : int val abs_struct : int
val absolute_paths : bool
val allow_specs_cleanup : bool val allow_specs_cleanup : bool
val analysis_path_regex_whitelist : analyzer -> string list val analysis_path_regex_whitelist : analyzer -> string list
val analysis_path_regex_blacklist : analyzer -> string list val analysis_path_regex_blacklist : analyzer -> string list
@ -126,6 +134,8 @@ val analyzer : analyzer option
val angelic_execution : bool val angelic_execution : bool
val array_level : int val array_level : int
val ast_file : string option val ast_file : string option
val blacklist : string option
val buck : bool
val buck_out : string option val buck_out : string option
val bugs_csv : outfile option val bugs_csv : outfile option
val bugs_json : outfile option val bugs_json : outfile option
@ -142,6 +152,7 @@ val continue_capture : bool
val create_harness : bool val create_harness : bool
val cxx_experimental : bool val cxx_experimental : bool
val debug_mode : bool val debug_mode : bool
val debug_exceptions : bool
val dependency_mode : bool val dependency_mode : bool
val developer_mode : bool val developer_mode : bool
val disable_checks : string list val disable_checks : string list
@ -149,12 +160,19 @@ val dotty_cfg_libs : bool
val enable_checks : string list val enable_checks : string list
val eradicate : bool val eradicate : bool
val err_file_cmdline : string val err_file_cmdline : string
val failures_allowed : bool
val filtering : bool
val flavors : bool
val frontend_stats : bool
val headers : bool
val infer_cache : string option val infer_cache : string option
val iterations : int val iterations : int
val javac_verbose_out : string val javac_verbose_out : string
val jobs : int
val join_cond : int val join_cond : int
val latex : outfile option val latex : outfile option
val load_analysis_results : string option val load_analysis_results : string option
val llvm : bool
val makefile_cmdline : string val makefile_cmdline : string
val merge : bool val merge : bool
val ml_buckets : val ml_buckets :

@ -27,14 +27,13 @@ let set_env_for_clang_wrapper () =
let () = let () =
set_env_for_clang_wrapper () ; set_env_for_clang_wrapper () ;
let ( / ) = Filename.concat in
(* The infer executable in the bin directory is a symbolic link to the real binary in the lib (* The infer executable in the bin directory is a symbolic link to the real binary in the lib
directory, so that the python script in the lib directory can be found relative to it. *) directory, so that the python script in the lib directory can be found relative to it. *)
let real_exe = let real_exe =
match Unix.readlink Sys.executable_name with match Unix.readlink Sys.executable_name with
| link when Filename.is_relative link -> | link when Filename.is_relative link ->
(* Sys.executable_name is a relative symbolic link *) (* Sys.executable_name is a relative symbolic link *)
(Filename.dirname Sys.executable_name) / link (Filename.dirname Sys.executable_name) // link
| link -> | link ->
(* Sys.executable_name is an absolute symbolic link *) (* Sys.executable_name is an absolute symbolic link *)
link link
@ -42,12 +41,13 @@ let () =
(* Sys.executable_name is not a symbolic link *) (* Sys.executable_name is not a symbolic link *)
Sys.executable_name Sys.executable_name
in in
let infer_py = (Filename.dirname real_exe) / "python" / "infer.py" in let infer_py = (Filename.dirname real_exe) // "python" // "infer.py" in
let build_cmd = IList.rev Config.rest in let build_cmd = IList.rev Config.rest in
let buck = match build_cmd with "buck" :: _ -> true | _ -> false in let buck = match build_cmd with "buck" :: _ -> true | _ -> false in
let args_py = let args_py =
Array.of_list ( Array.of_list (
infer_py :: infer_py ::
Config.anon_args @
(match Config.analyzer with None -> [] | Some a -> (match Config.analyzer with None -> [] | Some a ->
["--analyzer"; Utils.string_of_analyzer a]) @ ["--analyzer"; Utils.string_of_analyzer a]) @
(match Config.blacklist with (match Config.blacklist with
@ -67,7 +67,7 @@ let () =
["--use-flavors"]) @ ["--use-flavors"]) @
(match Config.infer_cache with None -> [] | Some s -> (match Config.infer_cache with None -> [] | Some s ->
["--infer_cache"; s]) @ ["--infer_cache"; s]) @
"--multicore" :: (string_of_int Config.multicore) :: "--multicore" :: (string_of_int Config.jobs) ::
"--out" :: Config.results_dir :: "--out" :: Config.results_dir ::
(match Config.project_root with None -> [] | Some pr -> (match Config.project_root with None -> [] | Some pr ->
["--project_root"; pr]) @ ["--project_root"; pr]) @

@ -0,0 +1,12 @@
(*
* Copyright (c) 2016 - 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! Utils
(** Top-level driver that orchestrates build system integration, frontends, and backend *)

@ -387,6 +387,8 @@ let do_outf outf_opt f =
let close_outf outf = let close_outf outf =
close_out outf.out_c close_out outf.out_c
let ( // ) = Filename.concat
(** convert a filename to absolute path and normalize by removing occurrences of "." and ".." *) (** convert a filename to absolute path and normalize by removing occurrences of "." and ".." *)
module FileNormalize = struct module FileNormalize = struct
let rec fname_to_list_rev fname = let rec fname_to_list_rev fname =
@ -405,7 +407,7 @@ module FileNormalize = struct
(* concatenate a list of strings representing a path into a filename *) (* concatenate a list of strings representing a path into a filename *)
let rec list_to_fname base path = match path with let rec list_to_fname base path = match path with
| [] -> base | [] -> base
| x :: path' -> list_to_fname (Filename.concat base x) path' | x :: path' -> list_to_fname (base // x) path'
(* normalize a path where done_l is a reversed path from the root already normalized *) (* normalize a path where done_l is a reversed path from the root already normalized *)
(* and todo_l is the path still to normalize *) (* and todo_l is the path still to normalize *)
@ -429,7 +431,7 @@ module FileNormalize = struct
let is_relative = Filename.is_relative fname in let is_relative = Filename.is_relative fname in
let must_normalize = fname_contains_current_parent fname in let must_normalize = fname_contains_current_parent fname in
let simple_case () = let simple_case () =
if is_relative then Filename.concat (Unix.getcwd ()) fname if is_relative then Unix.getcwd () // fname
else fname in else fname in
if must_normalize then begin if must_normalize then begin
let done_l, todo_l = let done_l, todo_l =
@ -473,7 +475,7 @@ let filename_to_relative root fname =
String.sub s2 (n1 + 1) (n2 - (n1 + 1)) String.sub s2 (n1 + 1) (n2 - (n1 + 1))
else s2 in else s2 in
let norm_root = (* norm_root is root without any trailing / *) let norm_root = (* norm_root is root without any trailing / *)
Filename.concat (Filename.dirname root) (Filename.basename root) in Filename.dirname root // Filename.basename root in
let remainder = (* remove the path prefix to root including trailing / *) let remainder = (* remove the path prefix to root including trailing / *)
string_strict_subtract norm_root fname in string_strict_subtract norm_root fname in
remainder remainder
@ -506,7 +508,7 @@ let next compare =
let directory_fold f init path = let directory_fold f init path =
let collect current_dir (accu, dirs) path = let collect current_dir (accu, dirs) path =
let full_path = (Filename.concat current_dir path) in let full_path = current_dir // path in
try try
if Sys.is_directory full_path then if Sys.is_directory full_path then
(accu, full_path:: dirs) (accu, full_path:: dirs)
@ -528,7 +530,7 @@ let directory_fold f init path =
let directory_iter f path = let directory_iter f path =
let apply current_dir dirs path = let apply current_dir dirs path =
let full_path = (Filename.concat current_dir path) in let full_path = current_dir // path in
try try
if Sys.is_directory full_path then if Sys.is_directory full_path then
full_path:: dirs full_path:: dirs
@ -548,10 +550,11 @@ let directory_iter f path =
else else
f path f path
type analyzer = Infer | Eradicate | Checkers | Tracing type analyzer = Capture | Compile | Infer | Eradicate | Checkers | Tracing
let string_to_analyzer = let string_to_analyzer =
[("infer", Infer); ("eradicate", Eradicate); ("checkers", Checkers); ("tracing", Tracing)] [("capture", Capture); ("compile", Compile);
("infer", Infer); ("eradicate", Eradicate); ("checkers", Checkers); ("tracing", Tracing)]
let analyzers = IList.map snd string_to_analyzer let analyzers = IList.map snd string_to_analyzer
@ -600,6 +603,18 @@ let with_file file ~f =
let write_json_to_file destfile json = let write_json_to_file destfile json =
with_file destfile ~f:(fun oc -> Yojson.Basic.pretty_to_channel oc json) with_file destfile ~f:(fun oc -> Yojson.Basic.pretty_to_channel oc json)
let with_process_in command read =
let chan = Unix.open_process_in command in
let res =
try
read chan
with exc ->
Unix.close_process_in chan |> ignore ;
raise exc
in
Unix.close_process_in chan |> ignore ;
res
let failwithf fmt = let failwithf fmt =
Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ())) Format.kfprintf (fun _ -> failwith (Format.flush_str_formatter ()))
Format.str_formatter fmt Format.str_formatter fmt

@ -193,6 +193,9 @@ val copy_file : string -> string -> int option
(** read a source file and return a list of lines, or None in case of error *) (** read a source file and return a list of lines, or None in case of error *)
val read_file : string -> string list option val read_file : string -> string list option
(** Filename.concat *)
val ( // ) : string -> string -> string
(** Convert a filename to an absolute one if it is relative, and normalize "." and ".." *) (** Convert a filename to an absolute one if it is relative, and normalize "." and ".." *)
val filename_to_absolute : string -> string val filename_to_absolute : string -> string
@ -254,7 +257,7 @@ val directory_fold : ('a -> string -> 'a) -> 'a -> string -> 'a
val directory_iter : (string -> unit) -> string -> unit val directory_iter : (string -> unit) -> string -> unit
(** Various kind of analyzers *) (** Various kind of analyzers *)
type analyzer = Infer | Eradicate | Checkers | Tracing type analyzer = Capture | Compile | Infer | Eradicate | Checkers | Tracing
(** Association list of analyzers and their names *) (** Association list of analyzers and their names *)
val string_to_analyzer : (string * analyzer) list val string_to_analyzer : (string * analyzer) list
@ -270,6 +273,8 @@ val read_optional_json_file : string -> (Yojson.Basic.json, string) result
val write_json_to_file : string -> Yojson.Basic.json -> unit val write_json_to_file : string -> Yojson.Basic.json -> unit
val with_process_in: string -> (in_channel -> 'a) -> 'a
val failwithf : ('a, Format.formatter, unit, 'b) format4 -> 'a val failwithf : ('a, Format.formatter, unit, 'b) format4 -> 'a
val invalid_argf : ('a, Format.formatter, unit, 'b) format4 -> 'a val invalid_argf : ('a, Format.formatter, unit, 'b) format4 -> 'a

Loading…
Cancel
Save