diff --git a/infer/lib/python/infer.py b/infer/lib/python/infer.py index fef67e93c..ab72c63e1 100755 --- a/infer/lib/python/infer.py +++ b/infer/lib/python/infer.py @@ -40,6 +40,7 @@ MODULE_TO_COMMAND = { 'buck': ['buck'], 'gradle': ['gradle', 'gradlew'], 'javac': ['javac'], + 'java': ['java'], 'make': make.SUPPORTED_COMMANDS, 'xcodebuild': ['xcodebuild'], 'mvn': ['mvn'], @@ -213,7 +214,9 @@ def main(): buck_not_in_compilation_database_mode = \ mod_name == 'buck' and not args.use_compilation_database - if not (buck_not_in_compilation_database_mode or mod_name == 'javac'): + if not (buck_not_in_compilation_database_mode or + mod_name == 'javac' or + mod_name == 'java'): # Something should be already captured, otherwise analysis would fail if not os.path.exists(os.path.join(args.infer_out, 'captured')): print('There was nothing to analyze, exiting') diff --git a/infer/lib/python/inferlib/capture/java.py b/infer/lib/python/inferlib/capture/java.py new file mode 100644 index 000000000..ac3723313 --- /dev/null +++ b/infer/lib/python/inferlib/capture/java.py @@ -0,0 +1,62 @@ +# 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 subprocess +import traceback + +import util +from inferlib import jwlib, utils + +MODULE_NAME = __name__ +MODULE_DESCRIPTION = '''Run analysis of code built with a command like: +java -jar compiler.jar + +Analysis examples: +infer -- java -jar compiler.jar srcfile.java +infer -- /path/to/java -jar compiler.jar srcfile.java''' +LANG = ['java'] + + +def gen_instance(*args): + return JavaJarCapture(*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) + + +def parse_command_line(cmd): + cmd_parser = argparse.ArgumentParser() + cmd_parser.add_argument('-jar', type=utils.decode, metavar='Compiler jar') + java_jar, other_args = cmd_parser.parse_known_args(cmd[1:]) + if java_jar.jar is None: + utils.stderr('Expects a javac command or jar file for the compiler') + utils.stderr('Example: infer -- java -jar compiler.jar ...\n') + exit(1) + return cmd[0], java_jar.jar, other_args + + +class JavaJarCapture: + def __init__(self, args, cmd): + java_binary, java_jar, other_args = parse_command_line(cmd) + self.analysis = jwlib.AnalyzerWithJavaJar( + args, + java_binary, + java_jar, + other_args + ) + + def capture(self): + try: + self.analysis.start() + return os.EX_OK + except subprocess.CalledProcessError as exc: + if self.analysis.args.debug: + traceback.print_exc() + return exc.returncode diff --git a/infer/lib/python/inferlib/capture/javac.py b/infer/lib/python/inferlib/capture/javac.py index 2abaf3878..59cb621e5 100644 --- a/infer/lib/python/inferlib/capture/javac.py +++ b/infer/lib/python/inferlib/capture/javac.py @@ -32,7 +32,7 @@ create_argparser = util.base_argparser(MODULE_DESCRIPTION, MODULE_NAME) class JavacCapture: def __init__(self, args, cmd): - self.analysis = jwlib.AnalyzerWithFrontendWrapper( + self.analysis = jwlib.AnalyzerWithJavac( args, cmd[0], cmd[1:], diff --git a/infer/lib/python/inferlib/jwlib.py b/infer/lib/python/inferlib/jwlib.py index 9b379081d..b54f37d33 100644 --- a/infer/lib/python/inferlib/jwlib.py +++ b/infer/lib/python/inferlib/jwlib.py @@ -63,7 +63,7 @@ def create_infer_command(args, javac_arguments): infer_args.append('--debug') infer_args += ['--analyzer', 'capture'] - return AnalyzerWithFrontendWrapper( + return AnalyzerWithJavac( analyze.infer_parser.parse_args(infer_args), 'javac', _get_javac_args(['javac'] + javac_arguments) @@ -101,7 +101,7 @@ class CompilerCall(object): def run(self): if self.args.version: - return subprocess.call([self.javac_cmd] + self.original_arguments) + return subprocess.call(self.javac_cmd + self.original_arguments) else: javac_args = ['-verbose', '-g'] @@ -113,8 +113,6 @@ class CompilerCall(object): if self.args.classes_out is not None: javac_args += ['-d', self.args.classes_out] - javac_args.append('-J-Duser.language=en') - classpath = self.args.classpath # the -processorpath option precludes searching the classpath for # annotation processors, so we don't want to use it unless the @@ -215,7 +213,7 @@ class CompilerCall(object): delete=False) as file_out: self.verbose_out = file_out.name - command = [self.javac_cmd] + cli_args + \ + command = self.javac_cmd + cli_args + \ ['@' + str(self.command_line_file)] try: subprocess.check_call(command, stderr=file_out) @@ -239,14 +237,13 @@ class CompilerCall(object): class AnalyzerWithFrontendWrapper(analyze.AnalyzerWrapper): - def __init__(self, args, javac_cmd, javac_args): - self.javac = CompilerCall(javac_cmd, javac_args) + def __init__(self, infer_args, compiler_call): + analyze.AnalyzerWrapper.__init__(self, infer_args) + self.javac = compiler_call if not self.javac.args.version: - if javac_args is None: + if self.javac.original_arguments is None: raise Exception('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, @@ -341,3 +338,19 @@ class AnalyzerWithFrontendWrapper(analyze.AnalyzerWrapper): def _close(self): os.remove(self.javac.verbose_out) os.remove(self.javac.suppress_warnings_out) + + +class AnalyzerWithJavac(AnalyzerWithFrontendWrapper): + + def __init__(self, infer_args, javac_executable, javac_args): + javac_cmd = [javac_executable, '-J-Duser.language=en'] + compiler_call = CompilerCall(javac_cmd, javac_args) + AnalyzerWithFrontendWrapper.__init__(self, infer_args, compiler_call) + + +class AnalyzerWithJavaJar(AnalyzerWithFrontendWrapper): + + def __init__(self, infer_args, java_executable, jar_path, compiler_args): + javac_cmd = [java_executable, '-jar', jar_path] + compiler_call = CompilerCall(javac_cmd, compiler_args) + AnalyzerWithFrontendWrapper.__init__(self, infer_args, compiler_call)