[buck] replace python buck integration with ocaml

Reviewed By: jvillard

Differential Revision: D20248852

fbshipit-source-id: c842ccadf
master
Martin Trojer 5 years ago committed by Facebook Github Bot
parent ca06b7840c
commit 284c6fdb3b

@ -1,253 +0,0 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import argparse
import logging
import os
import shutil
import subprocess
import tempfile
import traceback
import time
from inferlib import config, issues, utils
from . import util
import re
MODULE_NAME = __name__
MODULE_DESCRIPTION = '''Run analysis of code built with a command like:
buck [options] [target]
Analysis examples:
infer --buck-clang -- buck build HelloWorld'''
LANG = ['clang']
KEEP_GOING_OPTION = "--keep-going"
def gen_instance(*args):
return BuckAnalyzer(*args)
def string_in_quotes(value):
return value.strip('\'')
def create_argparser(group_name=MODULE_NAME):
"""This defines the set of arguments that get added by this module to the
set of global args defined in the infer top-level module
Do not use this function directly, it should be invoked by the infer
top-level module"""
parser = argparse.ArgumentParser(add_help=False)
group = parser.add_argument_group(
'{grp} module'.format(grp=MODULE_NAME),
description=MODULE_DESCRIPTION,
)
group.add_argument('--xcode-developer-dir',
help='Specify the path to Xcode developer directory ')
group.add_argument('--blacklist-regex',
help='Specify the regex for files to skip during '
'the analysis')
group.add_argument('--Xbuck', action='append', default=[],
type=string_in_quotes,
help='Pass values as command-line arguments to '
'invocations of `buck build`.'
'NOTE: value should be wrapped in single quotes')
group.add_argument('--buck-merge-all-deps',
action='store_true',
help='Find and merge all deps produced by buck')
return parser
class BuckAnalyzer:
def __init__(self, args, cmd):
self.args = args
self.cmd = cmd
self.keep_going = KEEP_GOING_OPTION in self.args.Xbuck
util.log_java_version()
logging.info(util.run_cmd_ignore_fail(['buck', '--version']))
def capture(self):
try:
return self.capture_with_flavors()
except subprocess.CalledProcessError as exc:
if self.args.debug:
traceback.print_exc()
return exc.returncode
def create_cxx_buck_configuration_args(self):
# return a string that can be passed in input to buck
# and configures the paths to infer/clang/plugin/xcode
facebook_clang_plugins_root = config.FCP_DIRECTORY
clang_path = os.path.join(
facebook_clang_plugins_root,
'clang',
'install',
'bin',
'clang',
)
plugin_path = os.path.join(
facebook_clang_plugins_root,
'libtooling',
'build',
'FacebookClangPlugin.dylib',
)
args = [
'--config',
'client.id=infer.clang',
'--config',
'*//infer.infer_bin={bin}'
.format(bin=config.BIN_DIRECTORY),
'--config',
'*//infer.clang_compiler={clang}'.format(clang=clang_path),
'--config',
'*//infer.clang_plugin={plugin}'.format(plugin=plugin_path),
'--config',
'*//cxx.pch_enabled=false',
'--config', # Infer doesn't support C++ modules yet (T35656509)
'*//cxx.modules_default=false',
'--config',
'*//cxx.modules=False',
] + self.args.Xbuck
if self.args.xcode_developer_dir is not None:
args.append('--config')
args.append('apple.xcode_developer_dir={devdir}'.format(
devdir=self.args.xcode_developer_dir))
if self.args.blacklist_regex:
args.append('--config')
args.append('*//infer.blacklist_regex={regex}'.format(
regex=self.args.blacklist_regex))
return args
def _get_analysis_result_paths(self):
# TODO(8610738): Make targets extraction smarter
buck_results_cmd = [
self.cmd[0],
'targets',
'--show-output'
] + self.cmd[2:] + self.create_cxx_buck_configuration_args()
buck_results_cmd = \
[x for x in buck_results_cmd if x != KEEP_GOING_OPTION]
proc = subprocess.Popen(buck_results_cmd, stdout=subprocess.PIPE)
(buck_output, _) = proc.communicate()
if proc.returncode != 0:
return None
# remove target name prefixes from each line and split them into a list
out = [os.path.join(self.args.project_root, x.split(None, 1)[1])
for x in buck_output.strip().split('\n')]
return [os.path.dirname(x)
if os.path.isfile(x) else x
for x in out if os.path.exists(x)]
@staticmethod
def _merge_infer_dep_files(root_paths, merged_out_path):
potential_dep_files = [os.path.join(p, config.INFER_BUCK_DEPS_FILENAME)
for p in root_paths]
dep_files = filter(os.path.exists, potential_dep_files)
utils.merge_and_dedup_files_into_path(dep_files, merged_out_path)
@staticmethod
def _find_deps_and_merge(merged_out_path):
"""This function is used to compute the infer-deps.txt file that
contains the location of the infer-out folders with the captured
files created by buck. This is needed when keep-going is passed
to buck and there are compilation failures, because in that case
buck doesn't create this file."""
infer_out_folders = []
start_time = time.time()
print('finding captured files in buck-out...')
for root, dirs, files in os.walk(config.BUCK_OUT_GEN):
regex = re.compile('.*infer-out.*')
folders = \
[os.path.join(root, d) for d in dirs if re.match(regex, d)]
for d in folders:
if d not in infer_out_folders:
infer_out_folders.append(d)
with open(merged_out_path, 'w') as fmerged_out_path:
for dir in infer_out_folders:
fmerged_out_path.write('\t' + '\t' + dir + '\n')
elapsed_time = time.time() - start_time
print('time elapsed in finding captured files in buck-out: % 6.2fs'
% elapsed_time)
def _find_depsfiles_and_merge(self, merge_out_path):
""" Sometimes buck targets --show-output gets confused and returns a
folder that doesn't contain infer-deps.txt. This can happen with on
for example objc targes with a certain combination of BUCK modes and
flavours. This function will walk buck-out and find infer-deps.txt
It will merge ALL infer-deps.txt in buck-out, so you might want
to do a buck clean first."""
fs = []
for root, dirs, files in os.walk(config.BUCK_OUT_GEN):
fs += [os.path.dirname(os.path.join(root, f)) for f in files
if f == config.INFER_BUCK_DEPS_FILENAME]
self._merge_infer_dep_files(fs, merge_out_path)
def _move_buck_out(self):
""" If keep-going is passed, we may need to compute the infer-deps
file with the paths to the captured files. To make sure that
this is done in a consistent way, we need to start the analysis
with an empty buck-out folder."""
if not os.path.exists(config.BUCK_OUT_TRASH):
os.makedirs(config.BUCK_OUT_TRASH)
tmp = tempfile.mkdtemp(
dir=config.BUCK_OUT_TRASH,
prefix=config.BUCK_OUT)
print('moving files in ' + config.BUCK_OUT + ' to ' + tmp)
for filename in os.listdir(config.BUCK_OUT):
if filename != config.TRASH:
shutil.move(os.path.join(config.BUCK_OUT, filename), tmp)
def _run_buck_with_flavors(self):
env_vars = utils.read_env()
infer_args = env_vars['INFER_ARGS']
if infer_args != '':
infer_args += '^' # '^' must be CommandLineOption.env_var_sep
infer_args += '--fcp-syntax-only'
env_vars['INFER_ARGS'] = infer_args
env = utils.encode_env(env_vars)
command = self.cmd
command += ['-j', str(self.args.multicore)]
if self.args.load_average is not None:
command += ['-L', str(self.args.load_average)]
command += self.create_cxx_buck_configuration_args()
try:
subprocess.check_call(command, env=env)
return os.EX_OK
except subprocess.CalledProcessError as e:
if self.keep_going:
print('Buck failed, but continuing the analysis '
'because --keep-going was passed')
return -1
else:
raise e
def capture_with_flavors(self):
if self.keep_going and not self.args.continue_capture:
self._move_buck_out()
ret = self._run_buck_with_flavors()
if not ret == os.EX_OK and not self.keep_going:
return ret
result_paths = self._get_analysis_result_paths()
if result_paths is None:
# huho, the Buck command to extract results paths failed
return os.EX_SOFTWARE
merged_deps_path = os.path.join(
self.args.infer_out, config.INFER_BUCK_DEPS_FILENAME)
if (not ret == os.EX_OK and self.keep_going):
self._find_deps_and_merge(merged_deps_path)
elif self.args.buck_merge_all_deps:
self._find_depsfiles_and_merge(merged_deps_path)
else:
self._merge_infer_dep_files(result_paths, merged_deps_path)
return os.EX_OK

@ -155,6 +155,8 @@ let bound_error_allowed_in_procedure_call = true
let buck_infer_deps_file_name = "infer-deps.txt" let buck_infer_deps_file_name = "infer-deps.txt"
let buck_out_gen = "buck-out" ^/ "gen"
let buck_results_dir_name = "infer" let buck_results_dir_name = "infer"
let captured_dir_name = "captured" let captured_dir_name = "captured"
@ -414,6 +416,12 @@ let bin_dir =
Filename.dirname (Utils.realpath Sys.executable_name) Filename.dirname (Utils.realpath Sys.executable_name)
let fcp_dir =
bin_dir ^/ Filename.parent_dir_name ^/ Filename.parent_dir_name ^/ "facebook-clang-plugins"
let clang_plugin_path = fcp_dir ^/ "libtooling" ^/ "build" ^/ "FacebookClangPlugin.dylib"
let lib_dir = bin_dir ^/ Filename.parent_dir_name ^/ "lib" let lib_dir = bin_dir ^/ Filename.parent_dir_name ^/ "lib"
let etc_dir = bin_dir ^/ Filename.parent_dir_name ^/ "etc" let etc_dir = bin_dir ^/ Filename.parent_dir_name ^/ "etc"

@ -27,8 +27,6 @@ type build_system =
type scheduler = File | Restart | SyntacticCallGraph [@@deriving equal] type scheduler = File | Restart | SyntacticCallGraph [@@deriving equal]
val equal_build_system : build_system -> build_system -> bool
val build_system_of_exe_name : string -> build_system val build_system_of_exe_name : string -> build_system
val string_of_build_system : build_system -> string val string_of_build_system : build_system -> string
@ -65,6 +63,8 @@ val clang_initializer_prefix : string
val clang_inner_destructor_prefix : string val clang_inner_destructor_prefix : string
val clang_plugin_path : string
val classnames_dir_name : string val classnames_dir_name : string
val classpath : string option val classpath : string option
@ -87,6 +87,8 @@ val events_dir_name : string
val fail_on_issue_exit_code : int val fail_on_issue_exit_code : int
val fcp_dir : string
val frontend_stats_dir_name : string val frontend_stats_dir_name : string
val global_tenv_filename : string val global_tenv_filename : string
@ -250,6 +252,8 @@ val buck_mode : BuckMode.t option
val buck_out : string option val buck_out : string option
val buck_out_gen : string
val buck_targets_blacklist : string list val buck_targets_blacklist : string list
val bufferoverrun : bool val bufferoverrun : bool

@ -26,12 +26,31 @@ let find_files ~path ~extension =
traverse_dir_aux files full_path traverse_dir_aux files full_path
| _ -> | _ ->
files files
| exception Unix.Unix_error (ENOENT, _, _) ->
files
in in
Sys.fold_dir ~init ~f:(aux dir_path) dir_path Sys.fold_dir ~init ~f:(aux dir_path) dir_path
in in
traverse_dir_aux [] path traverse_dir_aux [] path
let fold_folders ~init ~f ~path =
let rec traverse_dir_aux acc dir_path =
let aux base_path acc' rel_path =
let full_path = base_path ^/ rel_path in
match (Unix.stat full_path).Unix.st_kind with
| Unix.S_DIR ->
traverse_dir_aux (f acc' full_path) full_path
| _ ->
acc'
| exception Unix.Unix_error (ENOENT, _, _) ->
acc'
in
Sys.fold_dir ~init:acc ~f:(aux dir_path) dir_path
in
traverse_dir_aux init path
(** 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 *)
let read_file fname = let read_file fname =
let res = ref [] in let res = ref [] in

@ -14,6 +14,9 @@ val initial_times : Unix.process_times
val find_files : path:string -> extension:string -> string list val find_files : path:string -> extension:string -> string list
(** recursively traverse a path for files ending with a given extension *) (** recursively traverse a path for files ending with a given extension *)
val fold_folders : init:'acc -> f:('acc -> string -> 'acc) -> path:string -> 'acc
(** recursively traverse a path for folders, returning resuls by a given fold function *)
val string_crc_hex32 : string -> string val string_crc_hex32 : string -> string
(** Compute a 32-character hexadecimal crc using the Digest module *) (** Compute a 32-character hexadecimal crc using the Digest module *)

@ -15,13 +15,6 @@ type t =
; quoting_style: ClangQuotes.style ; quoting_style: ClangQuotes.style
; is_driver: bool } ; is_driver: bool }
let fcp_dir =
Config.bin_dir ^/ Filename.parent_dir_name ^/ Filename.parent_dir_name ^/ "facebook-clang-plugins"
(** path of the plugin to load in clang *)
let plugin_path = fcp_dir ^/ "libtooling" ^/ "build" ^/ "FacebookClangPlugin.dylib"
(** name of the plugin to use *) (** name of the plugin to use *)
let plugin_name = "BiniouASTExporter" let plugin_name = "BiniouASTExporter"
@ -148,7 +141,7 @@ let filter_and_replace_unsupported_args ?(replace_options_arg = fun _ s -> s) ?(
let clang_cc1_cmd_sanitizer cmd = let clang_cc1_cmd_sanitizer cmd =
let replace_args arg = function let replace_args arg = function
| Some override_regex when Str.string_match override_regex arg 0 -> | Some override_regex when Str.string_match override_regex arg 0 ->
fcp_dir ^/ "clang" ^/ "install" ^/ "lib" ^/ "clang" ^/ "9.0.0" ^/ "include" Config.fcp_dir ^/ "clang" ^/ "install" ^/ "lib" ^/ "clang" ^/ "9.0.0" ^/ "include"
| _ -> | _ ->
arg arg
in in
@ -175,7 +168,7 @@ let clang_cc1_cmd_sanitizer cmd =
match libcxx_include_to_override_regex with match libcxx_include_to_override_regex with
| Some libcxx_include_to_override_regex | Some libcxx_include_to_override_regex
when Str.string_match libcxx_include_to_override_regex arg 0 -> when Str.string_match libcxx_include_to_override_regex arg 0 ->
fcp_dir ^/ "clang" ^/ "install" ^/ "include" ^/ "c++" ^/ "v1" Config.fcp_dir ^/ "clang" ^/ "install" ^/ "include" ^/ "c++" ^/ "v1"
| _ -> | _ ->
arg ) arg )
| _ -> | _ ->
@ -250,7 +243,7 @@ let with_plugin_args args =
argv_cons "-cc1" argv_cons "-cc1"
|> List.rev_append |> List.rev_append
[ "-load" [ "-load"
; plugin_path ; Config.clang_plugin_path
; (* (t7400979) this is a workaround to avoid that clang crashes when the -fmodules flag and the ; (* (t7400979) this is a workaround to avoid that clang crashes when the -fmodules flag and the
YojsonASTExporter plugin are used. Since the -plugin argument disables the generation of .o YojsonASTExporter plugin are used. Since the -plugin argument disables the generation of .o
files, we invoke apple clang again to generate the expected artifacts. This will keep files, we invoke apple clang again to generate the expected artifacts. This will keep

@ -339,3 +339,109 @@ let filter_compatible subcommand args =
List.filter args ~f:(fun arg -> not (String.equal blacklist arg)) List.filter args ~f:(fun arg -> not (String.equal blacklist arg))
| _ -> | _ ->
args args
let capture_buck_args =
let clang_path =
List.fold ["clang"; "install"; "bin"; "clang"] ~init:Config.fcp_dir ~f:Filename.concat
in
List.append
[ "--show-output"
; "--config"
; "client.id=infer.clang"
; "--config"
; Printf.sprintf "*//infer.infer_bin=%s" Config.bin_dir
; "--config"
; Printf.sprintf "*//infer.clang_compiler=%s" clang_path
; "--config"
; Printf.sprintf "*//infer.clang_plugin=%s" Config.clang_plugin_path
; "--config"
; "*//cxx.pch_enabled=false"
; "--config"
; (* Infer doesn't support C++ modules yet (T35656509) *)
"*//cxx.modules_default=false"
; "--config"
; "*//cxx.modules=false" ]
( List.rev_append Config.buck_build_args
( if not (List.is_empty Config.buck_blacklist) then
[ "--config"
; Printf.sprintf "*//infer.blacklist_regex=(%s)"
(String.concat ~sep:")|(" Config.buck_blacklist) ]
else [] )
@ ( match Config.xcode_developer_dir with
| Some d ->
["--config"; Printf.sprintf "apple.xcode_developer_dir=%s" d]
| None ->
[] )
@ (if Config.keep_going then ["--keep-going"] else [])
@ ["-j"; Int.to_string Config.jobs]
@ match Config.load_average with Some l -> ["-L"; Float.to_string l] | None -> [] )
let run_buck_build prog buck_build_args =
L.debug Capture Verbose "%s %s@." prog (List.to_string ~f:Fn.id buck_build_args) ;
let {Unix.Process_info.stdin; stdout; stderr; pid} =
Unix.create_process ~prog ~args:buck_build_args
in
let buck_stderr = Unix.in_channel_of_descr stderr in
let buck_stdout = Unix.in_channel_of_descr stdout in
Utils.with_channel_in buck_stderr ~f:(L.progress "BUCK: %s@.") ;
Unix.close stdin ;
In_channel.close buck_stderr ;
(* Process a line of buck stdout output, in this case the result of '--show-output'
These paths (may) contain a 'infer-deps.txt' file, which we will later merge
*)
let process_buck_line acc line =
L.debug Capture Verbose "BUCK OUT: %s@." line ;
match String.split ~on:' ' line with
| [_; target_path] ->
let filename = Config.project_root ^/ target_path ^/ Config.buck_infer_deps_file_name in
if PolyVariantEqual.(Sys.file_exists filename = `Yes) then filename :: acc else acc
| _ ->
L.die ExternalError "Couldn't parse buck target output: %s" line
in
match Unix.waitpid pid with
| Ok () ->
let res = In_channel.fold_lines buck_stdout ~init:[] ~f:process_buck_line in
In_channel.close buck_stdout ; res
| Error _ as err ->
L.die ExternalError "*** capture failed to execute: %s"
(Unix.Exit_or_signal.to_string_hum err)
let merge_deps_files depsfiles =
let buck_out = Config.project_root ^/ Config.buck_out_gen in
let depslines, depsfiles =
match (depsfiles, Config.keep_going, Config.buck_merge_all_deps) with
| [], true, _ ->
let infouts =
Utils.fold_folders ~init:[] ~path:buck_out ~f:(fun acc dir ->
if
String.is_substring dir ~substring:"infer-out"
&& PolyVariantEqual.(
Sys.file_exists @@ dir ^/ ResultsDatabase.database_filename = `Yes)
then Printf.sprintf "\t\t%s" dir :: acc
else acc )
in
(infouts, depsfiles)
| [], _, true ->
let files = Utils.find_files ~path:buck_out ~extension:Config.buck_infer_deps_file_name in
([], files)
| _ ->
([], depsfiles)
in
depslines
@ List.fold depsfiles ~init:[] ~f:(fun acc file ->
List.rev_append acc (Utils.with_file_in file ~f:In_channel.input_lines) )
|> List.dedup_and_sort ~compare:String.compare
let clang_flavor_capture ~prog ~buck_build_cmd =
if Config.keep_going && not Config.continue_capture then
Process.create_process_and_wait ~prog ~args:["clean"] ;
let depsfiles = run_buck_build prog (buck_build_cmd @ capture_buck_args) in
let deplines = merge_deps_files depsfiles in
let infer_out_depsfile = Config.(results_dir ^/ buck_infer_deps_file_name) in
Utils.with_file_out infer_out_depsfile ~f:(fun out_chan ->
Out_channel.output_lines out_chan deplines ) ;
()

@ -32,3 +32,6 @@ val store_args_in_file : string list -> string list
val filter_compatible : [> `Targets] -> string list -> string list val filter_compatible : [> `Targets] -> string list -> string list
(** keep only the options compatible with the given Buck subcommand *) (** keep only the options compatible with the given Buck subcommand *)
val clang_flavor_capture : prog:string -> buck_build_cmd:string list -> unit
(** do a buck/clang flavor capture given the prog and build command (buck args) *)

@ -16,6 +16,7 @@ module F = Format
(* based on the build_system and options passed to infer, we run in different driver modes *) (* based on the build_system and options passed to infer, we run in different driver modes *)
type mode = type mode =
| Analyze | Analyze
| BuckClangFlavor of string list
| BuckGenrule of string | BuckGenrule of string
| BuckGenruleMaster of string list | BuckGenruleMaster of string list
| BuckCompilationDB of BuckMode.clang_compilation_db_deps * string * string list | BuckCompilationDB of BuckMode.clang_compilation_db_deps * string * string list
@ -31,6 +32,8 @@ let is_analyze_mode = function Analyze -> true | _ -> false
let pp_mode fmt = function let pp_mode fmt = function
| Analyze -> | Analyze ->
F.fprintf fmt "Analyze driver mode" F.fprintf fmt "Analyze driver mode"
| BuckClangFlavor args ->
F.fprintf fmt "BuckClangFlavor driver mode: args = %a" Pp.cli_args args
| BuckGenrule prog -> | BuckGenrule prog ->
F.fprintf fmt "BuckGenRule driver mode:@\nprog = '%s'" prog F.fprintf fmt "BuckGenRule driver mode:@\nprog = '%s'" prog
| BuckGenruleMaster build_cmd -> | BuckGenruleMaster build_cmd ->
@ -187,12 +190,12 @@ let capture_with_compilation_database db_files =
CaptureCompilationDatabase.capture_files_in_database compilation_database CaptureCompilationDatabase.capture_files_in_database compilation_database
let python_capture build_system build_cmd = let buck_capture build_cmd =
register_perf_stats_report PerfStats.TotalFrontend ; register_perf_stats_report PerfStats.TotalFrontend ;
let in_buck_mode = Config.equal_build_system build_system BBuck in let prog_build_cmd_opt =
let build_cmd_opt = let prog, buck_args = (List.hd_exn build_cmd, List.tl_exn build_cmd) in
match Config.buck_mode with match Config.buck_mode with
| Some ClangFlavors when in_buck_mode -> | Some ClangFlavors ->
(* let children infer processes know that they are inside Buck *) (* let children infer processes know that they are inside Buck *)
let infer_args_with_buck = let infer_args_with_buck =
String.concat String.concat
@ -200,7 +203,6 @@ let python_capture build_system build_cmd =
(Option.to_list (Sys.getenv CLOpt.args_env_var) @ ["--buck"]) (Option.to_list (Sys.getenv CLOpt.args_env_var) @ ["--buck"])
in in
Unix.putenv ~key:CLOpt.args_env_var ~data:infer_args_with_buck ; Unix.putenv ~key:CLOpt.args_env_var ~data:infer_args_with_buck ;
let prog, buck_args = (List.hd_exn build_cmd, List.tl_exn build_cmd) in
let {Buck.command; rev_not_targets; targets} = let {Buck.command; rev_not_targets; targets} =
Buck.add_flavors_to_buck_arguments ClangFlavors ~filter_kind:`Auto ~extra_flavors:[] Buck.add_flavors_to_buck_arguments ClangFlavors ~filter_kind:`Auto ~extra_flavors:[]
buck_args buck_args
@ -209,37 +211,36 @@ let python_capture build_system build_cmd =
else else
let all_args = List.rev_append rev_not_targets targets in let all_args = List.rev_append rev_not_targets targets in
let updated_buck_cmd = let updated_buck_cmd =
[prog; command] command
@ List.rev_append Config.buck_build_args_no_inline (Buck.store_args_in_file all_args) :: List.rev_append Config.buck_build_args_no_inline (Buck.store_args_in_file all_args)
in in
Logging.(debug Capture Quiet) Logging.(debug Capture Quiet)
"Processed buck command '%a'@\n" (Pp.seq F.pp_print_string) updated_buck_cmd ; "Processed buck command '%a'@\n" (Pp.seq F.pp_print_string) updated_buck_cmd ;
Some updated_buck_cmd Some (prog, updated_buck_cmd)
| _ -> | _ ->
Some build_cmd Some (prog, build_cmd)
in in
Option.iter build_cmd_opt ~f:(fun updated_build_cmd -> Option.iter prog_build_cmd_opt ~f:(fun (prog, buck_build_cmd) ->
L.progress "Capturing in buck mode...@." ;
if Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode then (
RunState.set_merge_capture true ; RunState.store () ) ;
Buck.clang_flavor_capture ~prog ~buck_build_cmd ) ;
PerfStats.get_reporter PerfStats.TotalFrontend ()
let python_capture build_system build_cmd =
register_perf_stats_report PerfStats.TotalFrontend ;
L.progress "Capturing in %s mode...@." (Config.string_of_build_system build_system) ; L.progress "Capturing in %s mode...@." (Config.string_of_build_system build_system) ;
let infer_py = Config.lib_dir ^/ "python" ^/ "infer.py" in let infer_py = Config.lib_dir ^/ "python" ^/ "infer.py" in
let args = let args =
List.rev_append Config.anon_args List.rev_append Config.anon_args
( ( match (build_system, Config.buck_blacklist) with ( (if not Config.continue_capture then [] else ["--continue"])
| Config.BBuck, _ :: _ ->
["--blacklist-regex"; "(" ^ String.concat ~sep:")|(" Config.buck_blacklist ^ ")"]
| _ ->
[] )
@ (if not Config.continue_capture then [] else ["--continue"])
@ ( match Config.force_integration with @ ( match Config.force_integration with
| None -> | None ->
[] []
| Some tool -> | Some tool ->
["--force-integration"; Config.string_of_build_system tool] ) ["--force-integration"; Config.string_of_build_system tool] )
@ (match Config.java_jar_compiler with None -> [] | Some p -> ["--java-jar-compiler"; p]) @ (match Config.java_jar_compiler with None -> [] | Some p -> ["--java-jar-compiler"; p])
@ ( match List.rev Config.buck_build_args with
| args when in_buck_mode ->
List.map ~f:(fun arg -> ["--Xbuck"; "'" ^ arg ^ "'"]) args |> List.concat
| _ ->
[] )
@ (if not Config.debug_mode then [] else ["--debug"]) @ (if not Config.debug_mode then [] else ["--debug"])
@ (if Config.filtering then [] else ["--no-filtering"]) @ (if Config.filtering then [] else ["--no-filtering"])
@ "-j" :: string_of_int Config.jobs @ "-j" :: string_of_int Config.jobs
@ -249,16 +250,10 @@ let python_capture build_system build_cmd =
@ (if not Config.quiet then [] else ["--quiet"]) @ (if not Config.quiet then [] else ["--quiet"])
@ "--out" :: Config.results_dir @ "--out" :: Config.results_dir
:: ::
( match Config.xcode_developer_dir with (match Config.xcode_developer_dir with None -> [] | Some d -> ["--xcode-developer-dir"; d])
| None ->
[]
| Some d ->
["--xcode-developer-dir"; d] )
@ (if not Config.buck_merge_all_deps then [] else ["--buck-merge-all-deps"]) @ (if not Config.buck_merge_all_deps then [] else ["--buck-merge-all-deps"])
@ ("--" :: updated_build_cmd) ) @ ("--" :: build_cmd) )
in in
if in_buck_mode && Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode then (
RunState.set_merge_capture true ; RunState.store () ) ;
run_command ~prog:infer_py ~args run_command ~prog:infer_py ~args
~cleanup:(function ~cleanup:(function
| Error (`Exit_non_zero exit_code) | Error (`Exit_non_zero exit_code)
@ -268,12 +263,14 @@ let python_capture build_system build_cmd =
| status -> | status ->
command_error_handling ~always_die:true ~prog:infer_py ~args status ) command_error_handling ~always_die:true ~prog:infer_py ~args status )
() ; () ;
PerfStats.get_reporter PerfStats.TotalFrontend () ) PerfStats.get_reporter PerfStats.TotalFrontend ()
let capture ~changed_files = function let capture ~changed_files = function
| Analyze -> | Analyze ->
() ()
| BuckClangFlavor build_cmd ->
buck_capture build_cmd
| BuckCompilationDB (deps, prog, args) -> | BuckCompilationDB (deps, prog, args) ->
L.progress "Capturing using Buck's compilation database...@." ; L.progress "Capturing using Buck's compilation database...@." ;
let json_cdb = let json_cdb =
@ -391,8 +388,7 @@ let error_nothing_to_analyze mode =
let analyze_and_report ?suppress_console_report ~changed_files mode = let analyze_and_report ?suppress_console_report ~changed_files mode =
let should_analyze, should_report = let should_analyze, should_report =
match (Config.command, mode) with match (Config.command, mode) with
| _, PythonCapture (BBuck, _) | _, BuckClangFlavor _ when not (Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode) ->
when not (Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode) ->
(* In Buck mode when compilation db is not used, analysis is invoked from capture if buck flavors are not used *) (* In Buck mode when compilation db is not used, analysis is invoked from capture if buck flavors are not used *)
(false, false) (false, false)
| _ when Config.infer_is_clang || Config.infer_is_javac -> | _ when Config.infer_is_clang || Config.infer_is_javac ->
@ -409,7 +405,7 @@ let analyze_and_report ?suppress_console_report ~changed_files mode =
| _ when Config.merge -> | _ when Config.merge ->
(* [--merge] overrides other behaviors *) (* [--merge] overrides other behaviors *)
true true
| PythonCapture (BBuck, _) | BuckClangFlavor _
when Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode when Option.exists ~f:BuckMode.is_clang_flavors Config.buck_mode
&& InferCommand.equal Run Config.command -> && InferCommand.equal Run Config.command ->
(* if doing capture + analysis of buck with flavors, we always need to merge targets before the analysis phase *) (* if doing capture + analysis of buck with flavors, we always need to merge targets before the analysis phase *)
@ -532,7 +528,7 @@ let mode_of_build_command build_cmd (buck_mode : BuckMode.t option) =
L.user_warning L.user_warning
"WARNING: the linters require --buck-compilation-database to be set.@ Alternatively, \ "WARNING: the linters require --buck-compilation-database to be set.@ Alternatively, \
set --no-linters to disable them and this warning.@." ; set --no-linters to disable them and this warning.@." ;
PythonCapture (BBuck, build_cmd) BuckClangFlavor build_cmd
| BBuck, Some JavaGenruleMaster -> | BBuck, Some JavaGenruleMaster ->
BuckGenruleMaster build_cmd BuckGenruleMaster build_cmd
| BClang, _ -> | BClang, _ ->
@ -547,7 +543,8 @@ let mode_of_build_command build_cmd (buck_mode : BuckMode.t option) =
Maven (prog, args) Maven (prog, args)
| BXcode, _ when Config.xcpretty -> | BXcode, _ when Config.xcpretty ->
XcodeXcpretty (prog, args) XcodeXcpretty (prog, args)
| (BBuck as build_system), Some ClangFlavors | BBuck, Some ClangFlavors ->
BuckClangFlavor build_cmd
| ((BAnt | BGradle | BNdk | BXcode) as build_system), _ -> | ((BAnt | BGradle | BNdk | BXcode) as build_system), _ ->
PythonCapture (build_system, build_cmd) ) PythonCapture (build_system, build_cmd) )

@ -13,6 +13,7 @@ open! IStd
(** based on the build_system and options passed to infer, we run in different driver modes *) (** based on the build_system and options passed to infer, we run in different driver modes *)
type mode = type mode =
| Analyze | Analyze
| BuckClangFlavor of string list
| BuckGenrule of string | BuckGenrule of string
| BuckGenruleMaster of string list | BuckGenruleMaster of string list
| BuckCompilationDB of BuckMode.clang_compilation_db_deps * string * string list | BuckCompilationDB of BuckMode.clang_compilation_db_deps * string * string list

Loading…
Cancel
Save