#!/usr/bin/env python # 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 argparse import os import logging import subprocess import traceback from inferlib import infer def create_infer_command(args, javac_arguments): infer_args = ['-o', args.infer_out] if args.debug: infer_args.append('--debug') infer_args += ['--analyzer', 'capture'] return infer.Infer(infer.infer_parser.parse_args(infer_args), infer.get_javac_args(['javac'] + javac_arguments)) def get_build_output(build_cmd): # TODO make it return generator to be able to handle large builds proc = subprocess.Popen(build_cmd, stdout=subprocess.PIPE) (verbose_out_chars, _) = proc.communicate() return verbose_out_chars.split('\n') def run_compilation_commands(cmds, clean_cmd): """runs compilation commands, and suggests a project cleaning command in case there is nothing to compile. """ # TODO call it in parallel if len(cmds) == 0: print('Nothing to compile. Try running `%s` first.' % clean_cmd) return os.EX_NOINPUT for cmd in cmds: if cmd.start() != os.EX_OK: return os.EX_SOFTWARE return os.EX_OK def run_cmd_ignore_fail(cmd): try: return subprocess.check_output(cmd, stderr=subprocess.STDOUT) except: return 'calling {cmd} failed\n{trace}'.format( cmd=' '.join(cmd), trace=traceback.format_exc()) def log_java_version(): java_version = run_cmd_ignore_fail(['java', '-version']) javac_version = run_cmd_ignore_fail(['javac', '-version']) logging.info("java versions:\n%s%s", java_version, javac_version) def base_argparser(description, module_name): def _func(group_name=module_name): """This creates an empty argparser for the module, which provides only description/usage information and no arguments.""" parser = argparse.ArgumentParser(add_help=False) group = parser.add_argument_group( "{grp} module".format(grp=group_name), description=description, ) return parser return _func def clang_frontend_argparser(description, module_name): def _func(group_name=module_name): """This creates an argparser for all the modules that require clang for their capture phase, thus InferClang and clang wrappers""" parser = argparse.ArgumentParser(add_help=False) group = parser.add_argument_group( "{grp} module".format(grp=group_name), description=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 return _func def get_clang_frontend_envvars(args): """Return the environment variables that configure the clang wrapper, e.g. to emit debug information if needed, and the invocation of the Infer frontend for Clang, InferClang, e.g. to analyze headers, emit stats, etc""" env_vars = {} frontend_args = [] env_vars['INFER_RESULTS_DIR'] = args.infer_out if args.headers: frontend_args.append('-headers') if args.models_mode: frontend_args.append('-models_mode') if args.project_root: frontend_args += ['-project_root', args.project_root] if args.testing_mode: frontend_args.append('-testing_mode') if args.frontend_debug: frontend_args += ['-debug'] env_vars['FCP_DEBUG_MODE'] = '1' if args.frontend_stats: frontend_args += ['-stats'] env_vars['FCP_DEBUG_MODE'] = '1' if args.no_failures_allowed: env_vars['FCP_REPORT_FRONTEND_FAILURE'] = '1' if args.llvm: env_vars['LLVM_MODE'] = '1' # export an env variable with all the arguments to pass to InferClang env_vars['FCP_INFER_FRONTEND_ARGS'] = ' '.join(frontend_args) return env_vars