import argparse import os import subprocess import traceback MODULE_NAME = 'make/cc/clang/gcc' MODULE_DESCRIPTION = '''Run analysis of code built with commands like: make [target] clang [compiler_options] gcc [compiler_options] cc [compiler_options] Analysis examples: infer -- make all infer -- clang -c srcfile.m infer -- gcc -c srcfile.c''' def gen_instance(*args): return MakeCapture(*args) def mkdir_if_not_exists(path): if not os.path.exists(path): os.mkdir(path) 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( '-hd', '--headers', action='store_true', help='Analyze code in header files', ) group.add_argument( '--models_mode', action='store_true', dest='models_mode', help='Mode for computing the models', ) group.add_argument( '--no_failures_allowed', action='store_true', dest='no_failures_allowed', help='Fail if at least one of the translations fails', ) group.add_argument( '-tm', '--testing_mode', dest='testing_mode', action='store_true', help='Testing mode for the translation: Do not translate libraries' ' (including enums)') group.add_argument( '-fs', '--frontend-stats', dest='frontend_stats', action='store_true', help='Output statistics about the capture phase to *.o.astlog') group.add_argument( '-fd', '--frontend-debug', dest='frontend_debug', action='store_true', help='Output debugging information to *.o.astlog during capture') return parser class MakeCapture: def __init__(self, args, cmd): self.args = args self.cmd = [os.path.basename(cmd[0])] + cmd[1:] def create_results_dir(self): results_dir = self.args.infer_out mkdir_if_not_exists(results_dir) mkdir_if_not_exists(os.path.join(results_dir, 'specs')) mkdir_if_not_exists(os.path.join(results_dir, 'captured')) mkdir_if_not_exists(os.path.join(results_dir, 'sources')) def get_envvars(self): env_vars = dict(os.environ) env_vars['INFER_RESULTS_DIR'] = self.args.infer_out wrappers_path = os.path.join( os.path.dirname(os.path.realpath(__file__)), '..', 'wrappers') 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'], ) return env_vars def capture(self): self.create_results_dir() env_vars = self.get_envvars() frontend_args = [] if self.args.headers: frontend_args.append('-headers') if self.args.models_mode: frontend_args.append('-models_mode') if self.args.project_root: frontend_args += ['-project_root', self.args.project_root] if self.args.testing_mode: frontend_args.append('-testing_mode') if self.args.frontend_debug: frontend_args += ['-debug'] env_vars['FCP_DEBUG_MODE'] = '1' if self.args.frontend_stats: frontend_args += ['-stats'] env_vars['FCP_DEBUG_MODE'] = '1' if self.args.no_failures_allowed: env_vars['FCP_REPORT_FRONTEND_FAILURE'] = '1' # export an env variable with all the arguments to pass to InferClang env_vars['FCP_INFER_FRONTEND_ARGS'] = ' '.join(frontend_args) try: subprocess.check_call(self.cmd, env=env_vars) return os.EX_OK except subprocess.CalledProcessError as exc: if self.args.debug: traceback.print_exc() return exc.returncode