move java-specific code out of analyze.Infer

Summary:public
This class expects a working `jwlib.CompilerCommand` even when we're not doing
anything Java-related. Split the java-specific functionality into a new child
class in jwlib.py.

Reviewed By: jeremydubreil

Differential Revision: D2965832

fb-gh-sync-id: e895b33
shipit-source-id: e895b33
master
Jules Villard 9 years ago committed by facebook-github-bot-7
parent be8f25c90b
commit 2277c23c60

@ -160,7 +160,7 @@ def main():
if not os.path.exists(os.path.join(args.infer_out, 'captured')): if not os.path.exists(os.path.join(args.infer_out, 'captured')):
print('There was nothing to analyze, exiting') print('There was nothing to analyze, exiting')
exit(os.EX_USAGE) exit(os.EX_USAGE)
analysis = analyze.Infer(args) analysis = analyze.AnalyzerWrapper(args)
analysis.analyze_and_report() analysis.analyze_and_report()
analysis.save_stats() analysis.save_stats()

@ -23,7 +23,7 @@ import subprocess
import sys import sys
import time import time
from . import config, issues, jwlib, utils from . import config, issues, utils
# Increase the limit of the CSV parser to sys.maxlimit # Increase the limit of the CSV parser to sys.maxlimit
csv.field_size_limit(sys.maxsize) csv.field_size_limit(sys.maxsize)
@ -220,29 +220,18 @@ def run_command(cmd, debug_mode, javac_arguments, step, analyzer):
raise e raise e
class Infer: class AnalyzerWrapper(object):
def __init__(self, args, javac_cmd=None, javac_args=[]): javac = None
def __init__(self, args):
self.args = args self.args = args
if self.args.analyzer not in config.ANALYZERS: if self.args.analyzer not in config.ANALYZERS:
help_exit('Unknown analysis mode \"{0}\"' help_exit('Unknown analysis mode \"{0}\"'
.format(self.args.analyzer)) .format(self.args.analyzer))
self.javac = jwlib.CompilerCall(javac_cmd, javac_args)
if not self.javac.args.version:
if javac_args is None:
help_exit('No javac command detected')
if self.args.infer_out is None: if self.args.infer_out is None:
help_exit('Expect Infer results directory') help_exit('Expect Infer results directory')
if self.args.buck:
self.args.infer_out = os.path.join(
self.javac.args.classes_out,
config.BUCK_INFER_OUT)
self.args.infer_out = os.path.abspath(self.args.infer_out)
try: try:
os.mkdir(self.args.infer_out) os.mkdir(self.args.infer_out)
except OSError as e: except OSError as e:
@ -270,79 +259,12 @@ class Infer:
['-specs-dir-list-file', ['-specs-dir-list-file',
os.path.abspath(self.args.specs_dir_list_file)] os.path.abspath(self.args.specs_dir_list_file)]
def clean_exit(self): def clean_exit(self):
if os.path.isdir(self.args.infer_out): if os.path.isdir(self.args.infer_out):
utils.stdout('removing {}'.format(self.args.infer_out)) utils.stdout('removing {}'.format(self.args.infer_out))
shutil.rmtree(self.args.infer_out) shutil.rmtree(self.args.infer_out)
exit(os.EX_OK) exit(os.EX_OK)
# create a classpath to pass to the frontend
def create_frontend_classpath(self):
classes_out = '.'
if self.javac.args.classes_out is not None:
classes_out = self.javac.args.classes_out
classes_out = os.path.abspath(classes_out)
original_classpath = []
if self.javac.args.bootclasspath is not None:
original_classpath = self.javac.args.bootclasspath.split(':')
if self.javac.args.classpath is not None:
original_classpath += self.javac.args.classpath.split(':')
if len(original_classpath) > 0:
# add classes_out, unless it's already in the classpath
classpath = [os.path.abspath(p) for p in original_classpath]
if not classes_out in classpath:
classpath = [classes_out] + classpath
# remove models.jar; it's added by the frontend
models_jar = os.path.abspath(config.MODELS_JAR)
if models_jar in classpath:
classpath.remove(models_jar)
return ':'.join(classpath)
return classes_out
def run_infer_frontend(self):
infer_cmd = [utils.get_cmd_in_bin_dir('InferJava')]
infer_cmd += ['-classpath', self.create_frontend_classpath()]
infer_cmd += ['-class_source_map', self.javac.class_source_map]
if not self.args.absolute_paths:
infer_cmd += ['-project_root', self.args.project_root]
infer_cmd += [
'-results_dir', self.args.infer_out,
'-verbose_out', self.javac.verbose_out,
]
if os.path.isfile(config.MODELS_JAR):
infer_cmd += ['-models', config.MODELS_JAR]
infer_cmd.append('-no-static_final')
if self.args.debug:
infer_cmd.append('-debug')
if self.args.analyzer == config.ANALYZER_TRACING:
infer_cmd.append('-tracing')
if self.args.android_harness:
infer_cmd.append('-harness')
if (self.args.android_harness
or self.args.analyzer in [config.ANALYZER_CHECKERS,
config.ANALYZER_ERADICATE]):
os.environ['INFER_CREATE_CALLEE_PDESC'] = 'Y'
return run_command(
infer_cmd,
self.args.debug,
self.javac.original_arguments,
'frontend',
self.args.analyzer
)
def compile(self):
return self.javac.run()
def analyze(self): def analyze(self):
logging.info('Starting analysis') logging.info('Starting analysis')
infer_analyze = [ infer_analyze = [
@ -408,7 +330,7 @@ class Infer:
exit_status = os.EX_OK exit_status = os.EX_OK
if self.args.buck: if self.javac is not None and self.args.buck:
infer_options += ['-project_root', os.getcwd(), '-java'] infer_options += ['-project_root', os.getcwd(), '-java']
if self.javac.args.classpath is not None: if self.javac.args.classpath is not None:
for path in self.javac.args.classpath.split(os.pathsep): for path in self.javac.args.classpath.split(os.pathsep):
@ -423,13 +345,16 @@ class Infer:
os.environ['INFER_OPTIONS'] = ' '.join(infer_options) os.environ['INFER_OPTIONS'] = ' '.join(infer_options)
javac_original_arguments = \
self.javac.original_arguments if self.javac is not None else []
if self.args.multicore == 1: if self.args.multicore == 1:
analysis_start_time = time.time() analysis_start_time = time.time()
analyze_cmd = infer_analyze + infer_options analyze_cmd = infer_analyze + infer_options
exit_status = run_command( exit_status = run_command(
analyze_cmd, analyze_cmd,
self.args.debug, self.args.debug,
self.javac.original_arguments, javac_original_arguments,
'analysis', 'analysis',
self.args.analyzer self.args.analyzer
) )
@ -450,7 +375,7 @@ class Infer:
makefile_status = run_command( makefile_status = run_command(
analyze_cmd, analyze_cmd,
self.args.debug, self.args.debug,
self.javac.original_arguments, javac_original_arguments,
'create_makefile', 'create_makefile',
self.args.analyzer self.args.analyzer
) )
@ -465,7 +390,7 @@ class Infer:
make_status = run_command( make_status = run_command(
make_cmd, make_cmd,
self.args.debug, self.args.debug,
self.javac.original_arguments, javac_original_arguments,
'run_makefile', 'run_makefile',
self.args.analyzer self.args.analyzer
) )
@ -506,7 +431,7 @@ class Infer:
'-procs', procs_report, '-procs', procs_report,
'-analyzer', self.args.analyzer '-analyzer', self.args.analyzer
] ]
if self.javac.annotations_out is not None: if self.javac is not None and self.javac.annotations_out is not None:
infer_print_options += [ infer_print_options += [
'-local_config', self.javac.annotations_out] '-local_config', self.javac.annotations_out]
if self.args.debug or self.args.debug_exceptions: if self.args.debug or self.args.debug_exceptions:
@ -551,11 +476,6 @@ class Infer:
stats_path = os.path.join(self.args.infer_out, config.STATS_FILENAME) stats_path = os.path.join(self.args.infer_out, config.STATS_FILENAME)
utils.dump_json_to_path(self.stats, stats_path) utils.dump_json_to_path(self.stats, stats_path)
def close(self):
os.remove(self.javac.verbose_out)
os.remove(self.javac.annotations_out)
def analyze_and_report(self): def analyze_and_report(self):
should_print_errors = False should_print_errors = False
if self.args.analyzer not in [config.ANALYZER_COMPILE, if self.args.analyzer not in [config.ANALYZER_COMPILE,
@ -578,29 +498,3 @@ class Infer:
files_total = self.stats['int']['files'] files_total = self.stats['int']['files']
files_str = utils.get_plural('file', files_total) files_str = utils.get_plural('file', files_total)
print('Analyzed {}'.format(files_str)) print('Analyzed {}'.format(files_str))
def start(self):
if self.javac.args.version:
if self.args.buck:
key = self.args.analyzer
utils.stderr(utils.infer_key(key), errors="strict")
else:
return self.javac.run()
else:
start_time = time.time()
self.compile()
if self.args.analyzer == config.ANALYZER_COMPILE:
return os.EX_OK
self.run_infer_frontend()
self.timing['capture'] = utils.elapsed_time(start_time)
if self.args.analyzer == config.ANALYZER_CAPTURE:
return os.EX_OK
self.analyze_and_report()
self.close()
self.timing['total'] = utils.elapsed_time(start_time)
self.save_stats()
return self.stats

@ -10,7 +10,7 @@ import subprocess
import traceback import traceback
import util import util
from inferlib import analyze from inferlib import analyze, jwlib
MODULE_NAME = __name__ MODULE_NAME = __name__
MODULE_DESCRIPTION = '''Run analysis of code built with a command like: MODULE_DESCRIPTION = '''Run analysis of code built with a command like:
@ -31,7 +31,11 @@ create_argparser = util.base_argparser(MODULE_DESCRIPTION, MODULE_NAME)
class JavacCapture: class JavacCapture:
def __init__(self, args, cmd): def __init__(self, args, cmd):
self.analysis = analyze.Infer(args, cmd[0], cmd[1:]) self.analysis = jwlib.AnalyzerWithFrontendWrapper(
args,
cmd[0],
cmd[1:],
)
def capture(self): def capture(self):
try: try:

@ -18,7 +18,7 @@ import logging
import subprocess import subprocess
import traceback import traceback
from inferlib import analyze, utils from inferlib import analyze, jwlib, utils
def get_build_output(build_cmd): def get_build_output(build_cmd):
# TODO make it return generator to be able to handle large builds # TODO make it return generator to be able to handle large builds

@ -6,14 +6,18 @@
# 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.
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import argparse import argparse
import os import os
import tempfile
import subprocess import subprocess
import tempfile
import time
import analyze from . import analyze, config, utils
import config
import utils
# javac options # javac options
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
@ -52,9 +56,11 @@ def create_infer_command(args, javac_arguments):
infer_args.append('--debug') infer_args.append('--debug')
infer_args += ['--analyzer', 'capture'] infer_args += ['--analyzer', 'capture']
return analyze.Infer(analyze.infer_parser.parse_args(infer_args), return AnalyzerWithFrontendWrapper(
analyze.infer_parser.parse_args(infer_args),
'javac', 'javac',
_get_javac_args(['javac'] + javac_arguments)) _get_javac_args(['javac'] + javac_arguments)
)
class AnnotationProcessorNotFound(Exception): class AnnotationProcessorNotFound(Exception):
@ -66,9 +72,10 @@ class AnnotationProcessorNotFound(Exception):
return repr(self.path + ' not found') return repr(self.path + ' not found')
class CompilerCall: class CompilerCall(object):
def __init__(self, javac_cmd, arguments): def __init__(self, javac_cmd, arguments):
assert javac_cmd is not None and arguments is not None
self.javac_cmd = javac_cmd self.javac_cmd = javac_cmd
self.original_arguments = arguments self.original_arguments = arguments
self.args, self.remaining_args = parser.parse_known_args(arguments) self.args, self.remaining_args = parser.parse_known_args(arguments)
@ -156,3 +163,114 @@ class CompilerCall:
subprocess.check_call(failing_cmd) subprocess.check_call(failing_cmd)
return os.EX_OK return os.EX_OK
class AnalyzerWithFrontendWrapper(analyze.AnalyzerWrapper):
def __init__(self, args, javac_cmd, javac_args):
self.javac = CompilerCall(javac_cmd, javac_args)
if not self.javac.args.version:
if javac_args is None:
help_exit('No javac command detected')
analyze.AnalyzerWrapper.__init__(self, args)
if self.args.buck:
self.args.infer_out = os.path.join(
self.javac.args.classes_out,
config.BUCK_INFER_OUT)
self.args.infer_out = os.path.abspath(self.args.infer_out)
def start(self):
if self.javac.args.version:
if self.args.buck:
key = self.args.analyzer
print(utils.infer_key(key), file=sys.stderr)
else:
return self.javac.run()
else:
start_time = time.time()
self._compile()
if self.args.analyzer == config.ANALYZER_COMPILE:
return os.EX_OK
self._run_infer_frontend()
self.timing['capture'] = utils.elapsed_time(start_time)
if self.args.analyzer == config.ANALYZER_CAPTURE:
return os.EX_OK
self.analyze_and_report()
self._close()
self.timing['total'] = utils.elapsed_time(start_time)
self.save_stats()
return self.stats
# create a classpath to pass to the frontend
def _create_frontend_classpath(self):
classes_out = '.'
if self.javac.args.classes_out is not None:
classes_out = self.javac.args.classes_out
classes_out = os.path.abspath(classes_out)
original_classpath = []
if self.javac.args.bootclasspath is not None:
original_classpath = self.javac.args.bootclasspath.split(':')
if self.javac.args.classpath is not None:
original_classpath += self.javac.args.classpath.split(':')
if len(original_classpath) > 0:
# add classes_out, unless it's already in the classpath
classpath = [os.path.abspath(p) for p in original_classpath]
if classes_out not in classpath:
classpath = [classes_out] + classpath
# remove models.jar; it's added by the frontend
models_jar = os.path.abspath(config.MODELS_JAR)
if models_jar in classpath:
classpath.remove(models_jar)
return ':'.join(classpath)
return classes_out
def _run_infer_frontend(self):
infer_cmd = [utils.get_cmd_in_bin_dir('InferJava')]
infer_cmd += ['-classpath', self._create_frontend_classpath()]
infer_cmd += ['-class_source_map', self.javac.class_source_map]
if not self.args.absolute_paths:
infer_cmd += ['-project_root', self.args.project_root]
infer_cmd += [
'-results_dir', self.args.infer_out,
'-verbose_out', self.javac.verbose_out,
]
if os.path.isfile(config.MODELS_JAR):
infer_cmd += ['-models', config.MODELS_JAR]
infer_cmd.append('-no-static_final')
if self.args.debug:
infer_cmd.append('-debug')
if self.args.analyzer == config.ANALYZER_TRACING:
infer_cmd.append('-tracing')
if self.args.android_harness:
infer_cmd.append('-harness')
if (self.args.android_harness or
self.args.analyzer in [config.ANALYZER_CHECKERS,
config.ANALYZER_ERADICATE]):
os.environ['INFER_CREATE_CALLEE_PDESC'] = 'Y'
return analyze.run_command(
infer_cmd,
self.args.debug,
self.javac.original_arguments,
'frontend',
self.args.analyzer
)
def _compile(self):
return self.javac.run()
def _close(self):
os.remove(self.javac.verbose_out)
os.remove(self.javac.annotations_out)

Loading…
Cancel
Save