diff --git a/.gitignore b/.gitignore index bad3ea1d8..dac9d150c 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,7 @@ /infer/_build /infer/tests/codetoanalyze/java/*/codetoanalyze _build_infer -*.exp.test -*.exp.test.* +*.exp.test* *.test.dot duplicates.txt *.ast.sh diff --git a/infer/src/backend/infer.ml b/infer/src/backend/infer.ml index c23455c22..043af48b3 100644 --- a/infer/src/backend/infer.ml +++ b/infer/src/backend/infer.ml @@ -209,7 +209,10 @@ let capture build_cmd = function "--out" :: Config.results_dir :: (match Config.xcode_developer_dir with None -> [] | Some d -> ["--xcode-developer-dir"; d]) @ - ("--" :: build_cmd) + "--" :: + if in_buck_mode && Config.flavors then + Buck.add_flavors_to_buck_command build_cmd + else build_cmd ) in run_command ~prog:infer_py ~args (fun status -> diff --git a/infer/src/base/Config.ml b/infer/src/base/Config.ml index 09381283e..9726279f8 100644 --- a/infer/src/base/Config.ml +++ b/infer/src/base/Config.ml @@ -26,6 +26,9 @@ let string_to_analyzer = ("tracing", Tracing); ("crashcontext", Crashcontext); ("linters", Linters); ("quandary", Quandary); ("threadsafety", Threadsafety)] +let string_of_analyzer a = + IList.find (fun (_, a') -> a = a') string_to_analyzer |> fst + let clang_frontend_action_symbols = [ ("lint", `Lint); ("capture", `Capture); diff --git a/infer/src/base/Config.mli b/infer/src/base/Config.mli index 2f830ae7f..48a059292 100644 --- a/infer/src/base/Config.mli +++ b/infer/src/base/Config.mli @@ -21,6 +21,7 @@ type analyzer = Capture | Compile | Infer | Eradicate | Checkers | Tracing (** Association list of analyzers and their names *) val string_to_analyzer : (string * analyzer) list +val string_of_analyzer : analyzer -> string type language = Clang | Java [@@deriving compare] diff --git a/infer/src/integration/Buck.ml b/infer/src/integration/Buck.ml new file mode 100644 index 000000000..e4b6f5e0f --- /dev/null +++ b/infer/src/integration/Buck.ml @@ -0,0 +1,64 @@ +(* + * 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! IStd + +type target = { name: string; flavors: string list; } + +let target_of_string target = + match String.split target ~on:'#' with + | name::flavors_string::[] -> + let flavors = String.split flavors_string ~on:',' in + {name; flavors} + | name::[] -> { name; flavors=[] } + | _ -> failwithf "Error: cannot parse target %s" target + +let string_of_target { name; flavors } = + let pp_string fmt s = Format.fprintf fmt "%s" s in + Format.asprintf "%s#%a" name (Pp.comma_seq pp_string) flavors + + +let is_target_string = + let target_regexp = Str.regexp "[^/]*//[^/]+.*:.*" in + fun s -> + Str.string_match target_regexp s 0 + +let no_targets_found_error_and_exit buck_cmd = + Process.print_error_and_exit + "No targets found in Buck command %s.@\nOnly fully qualified Buck targets are supported. \ + In particular, aliases are not allowed.@." + (String.concat ~sep:" " buck_cmd) + +let add_flavor_to_target target = + let infer_flavor = + match Config.analyzer with + | Compile -> + None + | Linters | Capture -> + Some "infer-capture-all" + | Checkers | Infer -> + Some "infer" + | Eradicate | Tracing | Crashcontext | Quandary | Threadsafety -> + failwithf "Unsupported infer analyzer with Buck flavors: %s" + (Config.string_of_analyzer Config.analyzer) in + if List.exists ~f:(String.is_prefix ~prefix:"infer") target.flavors then + (* there's already an infer flavor associated to the target, do nothing *) + target + else + { target with flavors = (Option.to_list infer_flavor) @ target.flavors } + + +let add_flavors_to_buck_command build_cmd = + let add_infer_if_target s (cmd, found_one_target) = + if not (is_target_string s) then (s::cmd, found_one_target) + else (string_of_target (add_flavor_to_target (target_of_string s))::cmd, true) in + let (cmd', found_one_target) = + List.fold_right build_cmd ~f:add_infer_if_target ~init:([], false) in + if not found_one_target then no_targets_found_error_and_exit build_cmd; + cmd' diff --git a/infer/src/integration/Buck.mli b/infer/src/integration/Buck.mli new file mode 100644 index 000000000..ff2b5f5e7 --- /dev/null +++ b/infer/src/integration/Buck.mli @@ -0,0 +1,22 @@ +(* + * 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. + *) + +(** is this a Buck target string, eg //foo/bar:baz or boo//foo/bar:baz *) +val is_target_string : string -> bool + +(** prints an error that no Buck targets were identified in the given list, and exits *) +val no_targets_found_error_and_exit : string list -> unit + +(** Add infer flavors to the targets in the given buck command, depending on the infer analyzer. For + instance, in capture mode, the buck command: + buck build //foo/bar:baz#some,flavor + becomes: + buck build //foo/bar:baz#infer-capture-all,some,flavor +*) +val add_flavors_to_buck_command : string list -> string list diff --git a/infer/src/integration/CaptureCompilationDatabase.ml b/infer/src/integration/CaptureCompilationDatabase.ml index 72bad72f9..45c55b3fe 100644 --- a/infer/src/integration/CaptureCompilationDatabase.ml +++ b/infer/src/integration/CaptureCompilationDatabase.ml @@ -29,11 +29,8 @@ let should_capture_file_from_index () = (** The buck targets are assumed to start with //, aliases are not supported. *) let check_args_for_targets args = - if not (IList.exists (String.is_prefix ~prefix:"//") args) then - let args_s = String.concat ~sep:" " args in - Process.print_error_and_exit - "Error reading buck command %s. Please, pass buck targets, aliases are not allowed.\n%!" - args_s + if not (IList.exists Buck.is_target_string args) then + Buck.no_targets_found_error_and_exit args let add_flavor_to_targets args = let flavor =