[infer][java] basic support to run Infer using Buck genrules

Summary: Add some basic command line API to run Infer using Buck genrules. Remains to fix issues with absolute vs relative paths and to see how to create these genrules on the fly for a given java or android library.

Reviewed By: sblackshear

Differential Revision: D4245622

fbshipit-source-id: 1cda4ee
master
Jeremy Dubreil 8 years ago committed by Facebook Github Bot
parent d4c634e50f
commit e269f2a3fd

@ -39,7 +39,7 @@ let rec rmtree name =
type build_mode =
| Analyze | Ant | Buck | ClangCompilationDatabase | Gradle | Java | Javac | Make | Mvn | Ndk
| Xcode
| Xcode | Genrule
let build_mode_of_string path =
match Filename.basename path with
@ -47,6 +47,7 @@ let build_mode_of_string path =
| "ant" -> Ant
| "buck" -> Buck
| "clang-compilation-database" -> ClangCompilationDatabase
| "genrule" -> Genrule
| "gradle" | "gradlew" -> Gradle
| "java" -> Java
| "javac" -> Javac
@ -61,6 +62,7 @@ let string_of_build_mode = function
| Ant -> "ant"
| Buck -> "buck"
| ClangCompilationDatabase -> "clang-compilation-database"
| Genrule -> "genrule"
| Gradle -> "gradle"
| Java -> "java"
| Javac -> "javac"
@ -69,6 +71,7 @@ let string_of_build_mode = function
| Ndk -> "ndk-build"
| Xcode -> "xcodebuild"
let remove_results_dir () =
rmtree Config.results_dir
@ -174,6 +177,10 @@ let capture build_cmd = function
check_xcpretty ();
let json_cdb = CaptureCompilationDatabase.get_compilation_database_files_xcodebuild () in
capture_with_compilation_database json_cdb
| Genrule ->
L.stdout "Capturing for Buck genrule compatibility...@\n";
let infer_java = Config.bin_dir // "InferJava" in
run_command [infer_java] (function _ -> ())
| build_mode ->
L.stdout "Capturing in %s mode...@." (string_of_build_mode build_mode);
let in_buck_mode = build_mode = Buck in
@ -279,7 +286,7 @@ let analyze = function
| Java | Javac ->
(* In Java and Javac modes, analysis is invoked from capture. *)
()
| Analyze | Ant | Buck | ClangCompilationDatabase | Gradle | Make | Mvn | Ndk | Xcode ->
| Analyze | Ant | Buck | ClangCompilationDatabase | Genrule | Gradle | Make | Mvn | Ndk | Xcode ->
if not (Sys.file_exists Config.(results_dir // captured_dir_name)) then (
L.stderr "There was nothing to analyze, exiting" ;
Config.print_usage_exit ()

@ -595,6 +595,11 @@ and blacklist =
~meta:"regex" "Skip analysis of files matched by the specified regular expression (Buck \
flavors only)"
and bootclasspath =
CLOpt.mk_string_opt ~long:"bootclasspath"
~exes:CLOpt.[Toplevel; Java]
"Specify the Java bootclasspath"
(** Automatically set when running from within Buck *)
and buck =
CLOpt.mk_bool ~long:"buck"
@ -703,6 +708,11 @@ and clang_include_to_override =
location of internal compiler headers. This option should specify the path to those headers \
so that infer can use its own clang internal headers instead."
and classpath =
CLOpt.mk_string_opt ~long:"classpath"
~exes:CLOpt.[Java]
"Specify the Java classpath"
and cluster =
CLOpt.mk_path_opt ~deprecated:["cluster"] ~long:"cluster"
~meta:"file" "Specify a .cluster file to be analyzed"
@ -931,6 +941,11 @@ and frontend_tests =
~exes:CLOpt.frontend_exes
"Save filename.ext.test.dot with the cfg in dotty format for frontend tests"
and generated_classes =
CLOpt.mk_path_opt ~long:"generated-classes"
~exes:CLOpt.[Toplevel; Java]
"Specify where to load the generated class files"
and headers =
CLOpt.mk_bool ~deprecated:["headers"] ~deprecated_no:["no_headers"] ~long:"headers" ~short:"hd"
~exes:CLOpt.[Clang]
@ -1156,6 +1171,16 @@ and skip_translation_headers =
~exes:CLOpt.[Clang]
~meta:"path prefix" "Ignore headers whose path matches the given prefix"
and sources =
CLOpt.mk_string_list ~long:"sources"
~exes:CLOpt.[Java]
"Specify the list of source files"
and sourcepath =
CLOpt.mk_string_opt ~long:"sourcepath"
~exes:CLOpt.[Java]
"Specify the sourcepath"
and spec_abs_level =
CLOpt.mk_int ~deprecated:["spec_abs_level"] ~long:"spec-abs-level" ~default:1
~meta:"int" "Set the level of abstracting the postconditions of discovered specs:\n\
@ -1418,12 +1443,14 @@ and angelic_execution = !angelic_execution
and array_level = !array_level
and ast_file = !ast_file
and blacklist = !blacklist
and bootclasspath = !bootclasspath
and buck = !buck
and buck_build_args = !buck_build_args
and buck_out = !buck_out
and bugs_csv = !bugs_csv
and bugs_json = !bugs_json
and frontend_tests = !frontend_tests
and generated_classes = !generated_classes
and bugs_tests = !bugs_tests
and bugs_txt = !bugs_txt
and bugs_xml = !bugs_xml
@ -1434,6 +1461,7 @@ and checkers = !checkers
and checkers_repeated_calls = !checkers_repeated_calls
and clang_biniou_file = !clang_biniou_file
and clang_include_to_override = !clang_include_to_override
and classpath = !classpath
and cluster_cmdline = !cluster
and compute_analytics = !compute_analytics
and continue_capture = !continue
@ -1525,6 +1553,8 @@ and show_progress_bar = !progress_bar
and skip_analysis_in_path = !skip_analysis_in_path
and skip_clang_analysis_in_path = !skip_clang_analysis_in_path
and skip_translation_headers = !skip_translation_headers
and sources = !sources
and sourcepath = !sourcepath
and spec_abs_level = !spec_abs_level
and stacktrace = !stacktrace
and stacktraces_dir = !stacktraces_dir
@ -1602,9 +1632,10 @@ let patterns_suppress_warnings =
| `Null -> []
| json -> patterns_of_json_with_key json_key json)
| Error msg -> error ("Could not read or parse the supplied " ^ path ^ ":\n" ^ msg))
| None when CLOpt.(current_exe <> Java) -> []
| None when Option.is_some generated_classes -> []
| None ->
if CLOpt.(current_exe <> Java) then []
else error ("Error: The option " ^ suppress_warnings_annotations_long ^ " was not provided")
error ("Error: The option " ^ suppress_warnings_annotations_long ^ " was not provided")
let specs_library =
match infer_cache with

@ -61,6 +61,7 @@ val buck_infer_deps_file_name : string
val captured_dir_name : string
val checks_disabled_by_default : string list
val clang_initializer_prefix : string
val classpath : string option
val cpp_extra_include_dir : string
val relative_cpp_models_dir : string
val csl_analysis : bool
@ -113,6 +114,8 @@ val save_compact_summaries : bool
val save_time_in_summaries : bool
val smt_output : bool
val source_file_extentions : string list
val sources : string list
val sourcepath : string option
val specs_dir_name : string
val specs_files_suffix : string
val start_filename : string
@ -143,6 +146,7 @@ val angelic_execution : bool
val array_level : int
val ast_file : string option
val blacklist : string option
val bootclasspath : string option
val buck : bool
val buck_build_args : string list
val buck_out : string option
@ -199,6 +203,7 @@ val from_json_report : string option
val frontend_debug : bool
val frontend_tests : bool
val frontend_stats : bool
val generated_classes : string option
val headers : bool
val icfg_dotty_outfile : string option
val infer_cache : string option

@ -92,7 +92,6 @@ type file_entry =
| Singleton of DB.source_file
| Duplicate of (string * DB.source_file) list
(* Open the source file and search for the package declaration.
Only the case where the package is declared in a single line is supported *)
let read_package_declaration source_file =
@ -146,7 +145,7 @@ let add_source_file path map =
StringMap.add basename entry map
let load_sources_and_classes () =
let load_from_verbose_output () =
let file_in = open_in Config.javac_verbose_out in
let class_filename_re =
Str.regexp
@ -188,6 +187,94 @@ let load_sources_and_classes () =
loop [] [] StringMap.empty JBasics.ClassSet.empty
let classname_of_class_filename class_filename =
let parts = Str.split (Str.regexp "/") class_filename in
let classname_str =
if IList.length parts > 1 then
IList.fold_left (fun s p -> s^"."^p) (IList.hd parts) (IList.tl parts)
else
IList.hd parts in
JBasics.make_cn classname_str
let extract_classnames classnames jar_filename =
let file_in = Zip.open_in jar_filename in
let collect classes entry =
let class_filename = entry.Zip.filename in
try
let () = ignore (Str.search_forward (Str.regexp "class") class_filename 0) in
(classname_of_class_filename (Filename.chop_extension class_filename):: classes)
with Not_found -> classes in
let classnames_after = IList.fold_left collect classnames (Zip.entries file_in) in
Zip.close_in file_in;
classnames_after
let collect_classnames start_classmap jar_filename =
let classpath = Javalib.class_path jar_filename in
let classmap =
IList.fold_left
(fun map cn -> JBasics.ClassSet.add cn map)
start_classmap
(extract_classnames [] jar_filename) in
Javalib.close_class_path classpath;
classmap
let search_classes path =
let add_class roots classes class_filename =
let cn, root_dir =
Javalib.extract_class_name_from_file class_filename in
let updated_roots =
if IList.exists (fun p -> p = root_dir) roots then roots
else root_dir:: roots in
(updated_roots, JBasics.ClassSet.add cn classes) in
directory_fold
(fun accu p ->
let paths, classes = accu in
if Filename.check_suffix p "class" then
add_class paths classes p
else if Filename.check_suffix p "jar" then
(p :: paths, collect_classnames classes p)
else accu)
([], JBasics.ClassSet.empty)
path
let search_sources () =
let initial_map =
IList.fold_left
(fun map path -> add_source_file path map)
StringMap.empty
Config.sources in
match Config.sourcepath with
| None -> initial_map
| Some sourcepath ->
directory_fold
(fun map p ->
if Filename.check_suffix p "java"
then add_source_file p map
else map)
initial_map
sourcepath
let load_from_arguments classes_out_path =
let roots, classes = search_classes classes_out_path in
let sources = search_sources () in
let split cp_option =
Option.map_default split_classpath [] cp_option in
let paths =
(split Config.bootclasspath) @ roots @ (split Config.classpath) in
let classpath = IList.fold_left append_path "" paths in
(classpath, sources, classes)
let load_sources_and_classes () =
match Config.generated_classes with
| None -> load_from_verbose_output ()
| Some path -> load_from_arguments path
type classmap = JCode.jcode Javalib.interface_or_class JBasics.ClassMap.t
@ -230,29 +317,6 @@ let lookup_node cn program =
| Invalid_argument _ -> None
let classname_of_class_filename class_filename =
let parts = Str.split (Str.regexp "/") class_filename in
let classname_str =
if IList.length parts > 1 then
IList.fold_left (fun s p -> s^"."^p) (IList.hd parts) (IList.tl parts)
else
IList.hd parts in
JBasics.make_cn classname_str
let extract_classnames classnames jar_filename =
let file_in = Zip.open_in jar_filename in
let collect classes entry =
let class_filename = entry.Zip.filename in
try
let () = ignore (Str.search_forward (Str.regexp "class") class_filename 0) in
(classname_of_class_filename (Filename.chop_extension class_filename):: classes)
with Not_found -> classes in
let classnames_after = IList.fold_left collect classnames (Zip.entries file_in) in
Zip.close_in file_in;
classnames_after
let collect_classes start_classmap jar_filename =
let classpath = Javalib.class_path jar_filename in
let collect classmap cn =

@ -1,8 +1,10 @@
# TODO: this file exists only to support buck integration in infer/tests/build_systems/build_integration_tests.py
sources = glob(['**/*.java'])
java_library(
name = 'compile',
srcs = glob(['**/*.java']),
srcs = sources,
deps = [
'//dependencies/java/guava:guava',
'//dependencies/java/jsr-305:jsr-305',
@ -15,3 +17,21 @@ java_library(
'PUBLIC'
]
)
genrule(
name = 'run_infer',
srcs = sources,
out = 'infer-out',
bash = ' '.join([
'infer',
'--sourcepath',
'$SRCDIR',
'--classpath',
'$(classpath :compile)',
'--generated-classes',
'$(location :compile)',
'--out',
'$OUT',
'--',
'genrule']),
)

Loading…
Cancel
Save