[driver] Add warning about nothing captured

Reviewed By: jvillard

Differential Revision: D4795382

fbshipit-source-id: 7bd1909
master
Dulma Churchill 8 years ago committed by Facebook Github Bot
parent 54547c9fd7
commit 15ede90c44

@ -87,7 +87,7 @@ OBJC_MODELS_FILE = $(SPECS_LIB_DIR)/objc_models
CLANG_DEPS_NO_MODELS = \ CLANG_DEPS_NO_MODELS = \
$(addprefix $(PYTHON_LIB_DIR)/, \ $(addprefix $(PYTHON_LIB_DIR)/, \
analyze.py config.py issues.py source.py utils.py) \ analyze.py config.py issues.py source.py utils.py) \
$(addprefix $(CAPTURE_LIB_DIR)/, make.py util.py) \ $(addprefix $(CAPTURE_LIB_DIR)/, util.py) \
$(INFER_BIN) $(INFERANALYZE_BIN) $(INFERCLANG_BIN) $(INFERPRINT_BIN) $(INFER_BIN) $(INFERANALYZE_BIN) $(INFERCLANG_BIN) $(INFERPRINT_BIN)
CLANG_DEPS = $(CLANG_DEPS_NO_MODELS) $(C_MODELS_FILE) $(CPP_MODELS_FILE) \ CLANG_DEPS = $(CLANG_DEPS_NO_MODELS) $(C_MODELS_FILE) $(CPP_MODELS_FILE) \

@ -22,7 +22,6 @@ import sys
import inferlib import inferlib
from inferlib import analyze, config, utils from inferlib import analyze, config, utils
from inferlib.capture import make
CAPTURE_PACKAGE = 'capture' CAPTURE_PACKAGE = 'capture'
@ -37,7 +36,6 @@ MODULE_TO_COMMAND = {
'ant': ['ant'], 'ant': ['ant'],
'buck': ['buck'], 'buck': ['buck'],
'gradle': ['gradle', 'gradlew'], 'gradle': ['gradle', 'gradlew'],
'make': make.SUPPORTED_COMMANDS,
'xcodebuild': ['xcodebuild'], 'xcodebuild': ['xcodebuild'],
'ndk-build': ['ndk-build'], 'ndk-build': ['ndk-build'],
} }

@ -1,85 +0,0 @@
# Copyright (c) 2015 - 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.
import logging
import os
import subprocess
import traceback
import util
from inferlib import config, utils
MODULE_NAME = 'make/cc/clang/gcc'
MODULE_DESCRIPTION = '''Run analysis of code built with commands like:
make [target]
clang [compiler_options] <filename>
gcc [compiler_options] <filename>
cc [compiler_options] <filename>
Analysis examples:
infer -- make all
infer -- clang -c srcfile.m
infer -- gcc -c srcfile.c'''
LANG = ['clang']
ALIASED_COMMANDS = ['clang', 'clang++', 'cc', 'gcc', 'g++']
BUILD_COMMANDS = ['cmake', 'configure', 'make', 'waf']
SUPPORTED_COMMANDS = ALIASED_COMMANDS + BUILD_COMMANDS
def gen_instance(*args):
return MakeCapture(*args)
# This creates an empty argparser for the module, which provides only
# description/usage information and no arguments.
create_argparser = util.base_argparser(MODULE_DESCRIPTION, MODULE_NAME)
class MakeCapture:
def __init__(self, args, cmd):
self.args = args
self.cmd = cmd
command_name = os.path.basename(cmd[0])
if command_name in ALIASED_COMMANDS:
# remove absolute paths for these commands as we want to
# substitue our own wrappers instead using a PATH trick
cmd[0] = command_name
def get_envvars(self):
env_vars = utils.read_env()
wrappers_path = config.WRAPPERS_DIRECTORY
# INFER_RESULTS_DIR and INFER_OLD_PATH are used by javac wrapper only
env_vars['INFER_OLD_PATH'] = env_vars['PATH']
env_vars['PATH'] = '{wrappers}{sep}{path}'.format(
wrappers=wrappers_path,
sep=os.pathsep,
path=env_vars['PATH'],
)
env_vars['INFER_RESULTS_DIR'] = self.args.infer_out
return env_vars
def capture(self):
try:
env = utils.encode_env(self.get_envvars())
cmd = map(utils.encode, self.cmd)
logging.info('Running command %s with env:\n%s' % (cmd, env))
subprocess.check_call(cmd, env=env)
capture_dir = os.path.join(self.args.infer_out, 'captured')
if len(os.listdir(capture_dir)) < 1:
# Don't return with a failure code unless we're
# running make. It could be normal to have captured
# nothing (eg, empty source file). Further output will
# alert the user that there was nothing to analyze.
if self.cmd[0] == 'make':
# reuse code from gradle, etc. integration
return util.run_compilation_commands([], 'make clean')
return os.EX_OK
except subprocess.CalledProcessError as exc:
if self.args.debug:
traceback.print_exc()
return exc.returncode

@ -5,8 +5,13 @@
# LICENSE file in the root directory of this source tree. An additional grant # 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. # of patent rights can be found in the PATENTS file in the same directory.
import make import logging
import os
import subprocess
import traceback
import util import util
from inferlib import config, utils
MODULE_NAME = 'ndk-build/clang' MODULE_NAME = 'ndk-build/clang'
MODULE_DESCRIPTION = '''Run analysis of code built with ndk-build MODULE_DESCRIPTION = '''Run analysis of code built with ndk-build
@ -15,6 +20,7 @@ MODULE_DESCRIPTION = '''Run analysis of code built with ndk-build
infer -- ndk-build''' infer -- ndk-build'''
LANG = ['clang'] LANG = ['clang']
ALIASED_COMMANDS = ['clang', 'clang++', 'cc', 'gcc', 'g++']
def gen_instance(*args): def gen_instance(*args):
return NdkBuildCapture(*args) return NdkBuildCapture(*args)
@ -23,8 +29,48 @@ def gen_instance(*args):
create_argparser = util.base_argparser(MODULE_DESCRIPTION, MODULE_NAME) create_argparser = util.base_argparser(MODULE_DESCRIPTION, MODULE_NAME)
class NdkBuildCapture(make.MakeCapture): class NdkBuildCapture():
def __init__(self, args, cmd): def __init__(self, args, cmd):
cmd = [cmd[0], 'NDK_TOOLCHAIN_VERSION=clang', 'TARGET_CC=clang', cmd = [cmd[0], 'NDK_TOOLCHAIN_VERSION=clang', 'TARGET_CC=clang',
'TARGET_CXX=clang++', 'TARGET_LD=ld'] + cmd[1:] 'TARGET_CXX=clang++', 'TARGET_LD=ld'] + cmd[1:]
make.MakeCapture.__init__(self, args, cmd) self.args = args
self.cmd = cmd
command_name = os.path.basename(cmd[0])
if command_name in ALIASED_COMMANDS:
# remove absolute paths for these commands as we want to
# substitue our own wrappers instead using a PATH trick
cmd[0] = command_name
def get_envvars(self):
env_vars = utils.read_env()
wrappers_path = config.WRAPPERS_DIRECTORY
# INFER_RESULTS_DIR and INFER_OLD_PATH are used by javac wrapper only
env_vars['INFER_OLD_PATH'] = env_vars['PATH']
env_vars['PATH'] = '{wrappers}{sep}{path}'.format(
wrappers=wrappers_path,
sep=os.pathsep,
path=env_vars['PATH'],
)
env_vars['INFER_RESULTS_DIR'] = self.args.infer_out
return env_vars
def capture(self):
try:
env = utils.encode_env(self.get_envvars())
cmd = map(utils.encode, self.cmd)
logging.info('Running command %s with env:\n%s' % (cmd, env))
subprocess.check_call(cmd, env=env)
capture_dir = os.path.join(self.args.infer_out, 'captured')
if len(os.listdir(capture_dir)) < 1:
# Don't return with a failure code unless we're
# running make. It could be normal to have captured
# nothing (eg, empty source file). Further output will
# alert the user that there was nothing to analyze.
if self.cmd[0] == 'make':
# reuse code from gradle, etc. integration
return util.run_compilation_commands([], 'make clean')
return os.EX_OK
except subprocess.CalledProcessError as exc:
if self.args.debug:
traceback.print_exc()
return exc.returncode

@ -115,6 +115,14 @@ let pp_driver_mode fmt driver_mode =
F.fprintf fmt "Clang driver mode:@\nprog = %s@\n" prog; F.fprintf fmt "Clang driver mode:@\nprog = %s@\n" prog;
List.iter ~f:(F.fprintf fmt "Arg: %s@\n") args List.iter ~f:(F.fprintf fmt "Arg: %s@\n") args
(* A clean command for each driver mode to be suggested to the user
in case nothing got captured. *)
let clean_compilation_command driver_mode =
match driver_mode with
| Clang (_, prog, _) ->
Some (prog ^ " clean")
| _ -> None
let remove_results_dir () = let remove_results_dir () =
rmtree Config.results_dir rmtree Config.results_dir
@ -156,6 +164,20 @@ let clean_results_dir () =
() in () in
clean Config.results_dir clean Config.results_dir
let check_captured_empty driver_mode =
let clean_command_opt = clean_compilation_command driver_mode in
(* if merge is passed, the captured folder will be empty at this point,
but will be filled later on. *)
if Utils.dir_is_empty Config.captured_dir && not Config.merge then ((
match clean_command_opt with
| Some clean_command ->
Logging.stderr "@\nNothing to compile. Try running `%s` first.@." clean_command
| None ->
Logging.stderr "@\nNothing to compile. Try cleaning the build first.@."
);
true
) else
false
let register_perf_stats_report () = let register_perf_stats_report () =
let stats_dir = Filename.concat Config.results_dir Config.backend_stats_dir_name in let stats_dir = Filename.concat Config.results_dir Config.backend_stats_dir_name in
@ -355,11 +377,11 @@ let analyze driver_mode =
| _, Linters -> | _, Linters ->
false, true in false, true in
if (should_analyze || should_report) && if (should_analyze || should_report) &&
(Sys.file_exists Config.(results_dir ^/ captured_dir_name)) <> `Yes then ( (((Sys.file_exists Config.captured_dir) <> `Yes) ||
L.stderr "There was nothing to analyze, exiting@." ; check_captured_empty driver_mode) then (
exit 1 L.stderr "There was nothing to analyze.@\n@." ;
); ) else if should_analyze then
if should_analyze then execute_analyze (); execute_analyze ();
if should_report then report () if should_report then report ()
(** as the Config.fail_on_bug flag mandates, exit with error when an issue is reported *) (** as the Config.fail_on_bug flag mandates, exit with error when an issue is reported *)

@ -1751,6 +1751,8 @@ and analysis_suppress_errors analyzer =
let checkers_enabled = let checkers_enabled =
not (eradicate || crashcontext || quandary || threadsafety || checkers_repeated_calls) not (eradicate || crashcontext || quandary || threadsafety || checkers_repeated_calls)
let captured_dir = results_dir ^/ captured_dir_name
let clang_frontend_do_capture, clang_frontend_do_lint = let clang_frontend_do_capture, clang_frontend_do_lint =
match !clang_frontend_action with match !clang_frontend_action with
| Some `Lint -> false, true (* no capture, lint *) | Some `Lint -> false, true (* no capture, lint *)

@ -187,6 +187,10 @@ val bugs_txt : string option
val bugs_xml : string option val bugs_xml : string option
val changed_files_index : string option val changed_files_index : string option
val calls_csv : string option val calls_csv : string option
(** directory where the results of the capture phase are stored *)
val captured_dir : string
val checkers : bool val checkers : bool
val checkers_enabled : bool val checkers_enabled : bool
val checkers_repeated_calls : bool val checkers_repeated_calls : bool

@ -31,17 +31,14 @@ let source_dir_get_internal_file source_dir extension =
let fname = source_dir_name ^ extension in let fname = source_dir_name ^ extension in
Filename.concat source_dir fname Filename.concat source_dir fname
let captured_dir =
Filename.concat Config.results_dir Config.captured_dir_name
(** get the source directory corresponding to a source file *) (** get the source directory corresponding to a source file *)
let source_dir_from_source_file source_file = let source_dir_from_source_file source_file =
Filename.concat captured_dir (SourceFile.encoding source_file) Filename.concat Config.captured_dir (SourceFile.encoding source_file)
(** Find the source directories in the results dir *) (** Find the source directories in the results dir *)
let find_source_dirs () = let find_source_dirs () =
let source_dirs = ref [] in let source_dirs = ref [] in
let files_in_results_dir = Array.to_list (Sys.readdir captured_dir) in let files_in_results_dir = Array.to_list (Sys.readdir Config.captured_dir) in
let add_cg_files_from_dir dir = let add_cg_files_from_dir dir =
let files = Array.to_list (Sys.readdir dir) in let files = Array.to_list (Sys.readdir dir) in
List.iter ~f:(fun fname -> List.iter ~f:(fun fname ->
@ -49,7 +46,7 @@ let find_source_dirs () =
if Filename.check_suffix path ".cg" then source_dirs := dir :: !source_dirs) if Filename.check_suffix path ".cg" then source_dirs := dir :: !source_dirs)
files in files in
List.iter ~f:(fun fname -> List.iter ~f:(fun fname ->
let dir = Filename.concat captured_dir fname in let dir = Filename.concat Config.captured_dir fname in
if Sys.is_directory dir = `Yes then add_cg_files_from_dir dir) if Sys.is_directory dir = `Yes then add_cg_files_from_dir dir)
files_in_results_dir; files_in_results_dir;
List.rev !source_dirs List.rev !source_dirs
@ -214,7 +211,7 @@ end
let global_tenv_fname = let global_tenv_fname =
let basename = Config.global_tenv_filename in let basename = Config.global_tenv_filename in
filename_concat captured_dir basename filename_concat Config.captured_dir basename
let is_source_file path = let is_source_file path =
List.exists List.exists

@ -85,9 +85,6 @@ val source_dir_get_internal_file : source_dir -> string -> filename
(** get the source directory corresponding to a source file *) (** get the source directory corresponding to a source file *)
val source_dir_from_source_file : SourceFile.t -> source_dir val source_dir_from_source_file : SourceFile.t -> source_dir
(** directory where the results of the capture phase are stored *)
val captured_dir : filename
(** create the directory containing the file bane *) (** create the directory containing the file bane *)
val filename_create_dir : filename -> unit val filename_create_dir : filename -> unit

@ -176,6 +176,19 @@ let directory_iter f path =
else else
f path f path
let dir_is_empty path =
let dir_handle = Unix.opendir path in
let is_empty = ref true in
(try
while !is_empty;
do if not (List.mem ~equal:String.equal["."; ".."] (Unix.readdir dir_handle)) then
is_empty := false;
done;
with End_of_file -> ()
);
Unix.closedir dir_handle;
!is_empty
let string_crc_hex32 s = Digest.to_hex (Digest.string s) let string_crc_hex32 s = Digest.to_hex (Digest.string s)

@ -52,6 +52,9 @@ val directory_fold : ('a -> string -> 'a) -> 'a -> string -> 'a
(** Functional iter function over all the file of a directory *) (** Functional iter function over all the file of a directory *)
val directory_iter : (string -> unit) -> string -> unit val directory_iter : (string -> unit) -> string -> unit
(** Returns true if a given directory is empty. The directory is assumed to exist. *)
val dir_is_empty : string -> bool
val read_optional_json_file : string -> (Yojson.Basic.json, string) Result.t val read_optional_json_file : string -> (Yojson.Basic.json, string) Result.t
val with_file : string -> f:(out_channel -> 'a) -> 'a val with_file : string -> f:(out_channel -> 'a) -> 'a

Loading…
Cancel
Save