From cccfad2445d2857fe58cef983f6de7bf21381b0c Mon Sep 17 00:00:00 2001 From: Josh Berdine Date: Fri, 16 Dec 2016 04:01:29 -0800 Subject: [PATCH] Directly handle javac -version option in build command Summary: This diff parses the build command args to directly handle the -version option passed to java and javac, to make the integration with buck more robust by ensuring that the version and no additional debug logging is generated for `infer --debug -- javac -version`. Reviewed By: jeremydubreil Differential Revision: D4158011 fbshipit-source-id: e7d6b4d --- infer/lib/python/inferlib/analyze.py | 25 ---- infer/lib/python/inferlib/jwlib.py | 172 ++++++++++++--------------- infer/src/base/Config.ml | 66 ++++++---- 3 files changed, 116 insertions(+), 147 deletions(-) diff --git a/infer/lib/python/inferlib/analyze.py b/infer/lib/python/inferlib/analyze.py index 1000548c0..e8eece105 100644 --- a/infer/lib/python/inferlib/analyze.py +++ b/infer/lib/python/inferlib/analyze.py @@ -29,27 +29,6 @@ csv.field_size_limit(sys.maxsize) INFER_ANALYZE_BINARY = 'InferAnalyze' -def get_infer_version(): - try: - return subprocess.check_output([ - utils.get_cmd_in_bin_dir(INFER_ANALYZE_BINARY), '-version']) - except subprocess.CalledProcessError: - utils.stdout('Failed to run {0} binary, exiting' - .format(INFER_ANALYZE_BINARY)) - sys.exit(os.EX_UNAVAILABLE) - - -# https://github.com/python/cpython/blob/aa8ea3a6be22c92e774df90c6a6ee697915ca8ec/Lib/argparse.py -class VersionAction(argparse._VersionAction): - def __call__(self, parser, namespace, values, option_string=None): - # set self.version so that argparse version action knows it - self.version = get_infer_version() - super(VersionAction, self).__call__(parser, - namespace, - values, - option_string) - - base_parser = argparse.ArgumentParser(add_help=False) base_group = base_parser.add_argument_group('global arguments') base_group.add_argument('-o', '--out', metavar='', @@ -83,10 +62,6 @@ base_group.add_argument('--android-harness', action='store_true', help='''[experimental] Create harness to detect bugs involving the Android lifecycle''') -base_parser.add_argument('-v', '--version', - help='''Print the version of Infer and exit''', - action=VersionAction) - base_group.add_argument('--pmd-xml', action='store_true', help='''Output issues in (PMD) XML format.''') diff --git a/infer/lib/python/inferlib/jwlib.py b/infer/lib/python/inferlib/jwlib.py index c44a99b5c..fcd4db8ed 100644 --- a/infer/lib/python/inferlib/jwlib.py +++ b/infer/lib/python/inferlib/jwlib.py @@ -25,7 +25,6 @@ parser = argparse.ArgumentParser() current_directory = utils.decode(os.getcwd()) -parser.add_argument('-version', action='store_true') parser.add_argument('-d', dest='classes_out', default=current_directory) @@ -67,74 +66,65 @@ class CompilerCall(object): self.args, self.remaining_args = parser.parse_known_args(arguments) self.verbose_out = None - def get_version(self): - assert self.args.version - return subprocess.check_output( - self.javac_cmd + self.original_arguments, - stderr=subprocess.STDOUT).strip() - def run(self): - if self.args.version: - return subprocess.call(self.javac_cmd + self.original_arguments) - else: - javac_args = ['-verbose', '-g'] - - if self.args.classes_out is not None: - javac_args += ['-d', self.args.classes_out] - - javac_args += self.remaining_args - - def arg_must_go_on_cli(arg): - # as mandated by javac, argument files must not contain - # arguments - return arg.startswith('-J') or arg.startswith('@') - file_args = filter(lambda x: not arg_must_go_on_cli(x), javac_args) - cli_args = filter(arg_must_go_on_cli, javac_args) - - # pass non-special args via a file to avoid blowing up the - # command line size limit - with tempfile.NamedTemporaryFile( - mode='w', - prefix='javac_args_', - delete=False) as command_line: - escaped_args = map(lambda x: '"%s"' % (x.replace('"', '\\"')), - file_args) - command_line.write(utils.encode('\n'.join(escaped_args))) - command_line.write('\n') - self.command_line_file = command_line.name - - with tempfile.NamedTemporaryFile( - mode='w', - suffix='.out', - prefix='javac_', - delete=False) as file_out: - self.verbose_out = file_out.name - - command = self.javac_cmd + cli_args + \ - ['@' + str(self.command_line_file)] + javac_args = ['-verbose', '-g'] + + if self.args.classes_out is not None: + javac_args += ['-d', self.args.classes_out] + + javac_args += self.remaining_args + + def arg_must_go_on_cli(arg): + # as mandated by javac, argument files must not contain + # arguments + return arg.startswith('-J') or arg.startswith('@') + file_args = filter(lambda x: not arg_must_go_on_cli(x), javac_args) + cli_args = filter(arg_must_go_on_cli, javac_args) + + # pass non-special args via a file to avoid blowing up the + # command line size limit + with tempfile.NamedTemporaryFile( + mode='w', + prefix='javac_args_', + delete=False) as command_line: + escaped_args = map(lambda x: '"%s"' % (x.replace('"', '\\"')), + file_args) + command_line.write(utils.encode('\n'.join(escaped_args))) + command_line.write('\n') + self.command_line_file = command_line.name + + with tempfile.NamedTemporaryFile( + mode='w', + suffix='.out', + prefix='javac_', + delete=False) as file_out: + self.verbose_out = file_out.name + + command = self.javac_cmd + cli_args + \ + ['@' + str(self.command_line_file)] + try: + subprocess.check_call(command, stderr=file_out) + except subprocess.CalledProcessError: try: - subprocess.check_call(command, stderr=file_out) + fallback_command = ['javac'] + cli_args + \ + ['@' + str(self.command_line_file)] + subprocess.check_call( + fallback_command, stderr=file_out) except subprocess.CalledProcessError: - try: - fallback_command = ['javac'] + cli_args + \ - ['@' + str(self.command_line_file)] - subprocess.check_call( - fallback_command, stderr=file_out) - except subprocess.CalledProcessError: - error_msg = 'ERROR: failure during compilation ' \ - + 'command.\nYou can run the failing ' \ - + 'compilation command again by ' \ - + 'copy-pasting the\nlines below in ' \ - + 'your terminal:\n\n"""\n' \ - + 'python < version := `Javac) in CLOpt.mk_subcommand ~exes:CLOpt.[Toplevel] "Stop argument processing, use remaining arguments as a build command" @@ -1251,7 +1252,8 @@ let rest = match Filename.basename build_exe with | "java" | "javac" -> [ ("-classes_out", classes_out_spec, ""); ("-d", classes_out_spec, ""); - ("-classpath", classpath_spec, ""); ("-cp", classpath_spec, "") + ("-classpath", classpath_spec, ""); ("-cp", classpath_spec, ""); + ("-version", version_spec, "") ] | _ -> [] ) @@ -1284,21 +1286,35 @@ let exe_usage (exe : CLOpt.exe) = version_string let post_parsing_initialization () = - F.set_margin !margin ; - - if !version then ( - (* TODO(11791235) change back to stdout once buck integration is fixed *) - F.fprintf F.err_formatter "%s@." version_string ; - exit 0 - ); - if !version_json then ( - F.fprintf F.std_formatter "%s@." Version.versionJson ; - exit 0 - ); - if !version_vcs then ( - F.fprintf F.std_formatter "%s@." Version.commit ; - exit 0 + (match !version with + | `Full -> + (* TODO(11791235) change back to stdout once buck integration is fixed *) + prerr_endline version_string + | `Javac when !buck -> + (* print buck key *) + let javac_version = + (* stderr contents of build command *) + let chans = Unix.open_process_full (String.concat ~sep:" " (List.rev !rest)) ~env:[||] in + let err = String.strip (In_channel.input_all chans.stderr) in + Unix.close_process_full chans |> ignore; + err in + let analyzer_name = + IList.assoc (=) + (match !analyzer with Some a -> a | None -> Infer) + (IList.map (fun (n,a) -> (a,n)) string_to_analyzer) in + let infer_version = Version.commit in + F.eprintf "%s/%s/%s@." javac_version analyzer_name infer_version + | `Javac -> + prerr_endline version_string + | `Json -> + print_endline Version.versionJson + | `Vcs -> + print_endline Version.commit + | `None -> () ); + if !version <> `None then exit 0; + + F.set_margin !margin ; let set_minor_heap_size nMb = (* increase the minor heap size to speed up gc *) let ctrl = Gc.get () in