# 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 util import tempfile from inferlib import config, jwlib, utils MODULE_NAME = __name__ MODULE_DESCRIPTION = '''Run analysis of code built with a command like: gradle [options] [task] Analysis examples: infer -- gradle build infer -- ./gradlew build''' LANG = ['java'] def gen_instance(*args): return GradleCapture(*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 GradleCapture: def __init__(self, args, cmd): self.args = args # TODO: make the extraction of targets smarter self.build_cmd = [cmd[0], '--debug'] + cmd[1:] # That contains javac version as well version_str = util.run_cmd_ignore_fail([cmd[0], '--version']) path = os.path.join(self.args.infer_out, config.JAVAC_FILELISTS_FILENAME) if not os.path.exists(path): os.mkdir(path) logging.info('Running with:\n' + utils.decode(version_str)) def get_infer_commands(self, verbose_output): argument_start_pattern = ' Compiler arguments: ' calls = [] seen_build_cmds = set([]) for line in verbose_output: if argument_start_pattern in line: content = line.partition(argument_start_pattern)[2].strip() # if we're building both the debug and release configuration # and the build commands are identical up to "release/debug", # only do capture for one set of commands build_agnostic_cmd = content.replace('release', 'debug') if build_agnostic_cmd in seen_build_cmds: continue seen_build_cmds.add(build_agnostic_cmd) javac_arguments = content.split(' ') java_files = [] java_args = [] for java_arg in javac_arguments: if java_arg.endswith('.java'): java_files.append(java_arg) else: java_args.append(java_arg) with tempfile.NamedTemporaryFile( mode='w', suffix='.txt', prefix='gradle_', dir=os.path.join(self.args.infer_out, config.JAVAC_FILELISTS_FILENAME), delete=False) as sources: sources.write('\n'.join(map(utils.encode, java_files))) sources.flush() java_args.append('@' + sources.name) capture = jwlib.create_infer_command(java_args) calls.append(capture) return calls def capture(self): print('Running and capturing gradle compilation...') (code, verbose_out) = util.get_build_output(self.build_cmd) if code != os.EX_OK: return code cmds = self.get_infer_commands(verbose_out) clean_cmd = '%s clean' % self.build_cmd[0] return util.run_compilation_commands(cmds, clean_cmd)