From baaf81b554fe545088ba69fa21a1053862fd434b Mon Sep 17 00:00:00 2001 From: Martin Trojer Date: Thu, 12 Mar 2020 08:28:34 -0700 Subject: [PATCH] [xcodebuild] replace python integration with ocaml Reviewed By: ngorogiannis Differential Revision: D20416859 fbshipit-source-id: abac2c6b4 --- .../lib/python/inferlib/capture/xcodebuild.py | 78 ------------------- infer/src/base/Process.ml | 17 ++++ infer/src/base/Process.mli | 5 ++ infer/src/integration/Driver.ml | 11 ++- infer/src/integration/Driver.mli | 1 + infer/src/integration/XcodeBuild.ml | 50 ++++++++++++ infer/src/integration/XcodeBuild.mli | 10 +++ 7 files changed, 93 insertions(+), 79 deletions(-) delete mode 100644 infer/lib/python/inferlib/capture/xcodebuild.py create mode 100644 infer/src/integration/XcodeBuild.ml create mode 100644 infer/src/integration/XcodeBuild.mli diff --git a/infer/lib/python/inferlib/capture/xcodebuild.py b/infer/lib/python/inferlib/capture/xcodebuild.py deleted file mode 100644 index e783af50c..000000000 --- a/infer/lib/python/inferlib/capture/xcodebuild.py +++ /dev/null @@ -1,78 +0,0 @@ -# Copyright (c) Facebook, Inc. and its affiliates. -# -# This source code is licensed under the MIT license found in the -# LICENSE file in the root directory of this source tree. - -import logging -import os -import subprocess -import traceback - -import util -from inferlib import config, utils - - -MODULE_NAME = __name__ -MODULE_DESCRIPTION = '''Run analysis of code built with a command like: -xcodebuild [options] - -Analysis examples: -infer -- xcodebuild -target HelloWorldApp -sdk iphonesimulator -infer -- xcodebuild -workspace HelloWorld.xcworkspace -scheme HelloWorld''' -LANG = ['clang'] - -CLANG_WRAPPER = os.path.join(config.WRAPPERS_DIRECTORY, 'clang') -CLANGPLUSPLUS_WRAPPER = os.path.join(config.WRAPPERS_DIRECTORY, 'clang++') - - -def gen_instance(*args): - return XcodebuildCapture(*args) - - -create_argparser = util.base_argparser(MODULE_DESCRIPTION, MODULE_NAME) - - -class XcodebuildCapture: - def __init__(self, args, cmd): - self.args = args - self.apple_clang_path = \ - subprocess.check_output(['xcrun', '--find', 'clang']).strip() - - xcode_version = util.run_cmd_ignore_fail(['xcodebuild', '-version']) - apple_clang_version = util.run_cmd_ignore_fail([self.apple_clang_path, - '--version']) - logging.info('Xcode version:\n%s', xcode_version) - - logging.info('clang version:\n%s', apple_clang_version) - - self.cmd = cmd - - def get_envvars(self): - env_vars = utils.read_env() - infer_args = env_vars['INFER_ARGS'] - if infer_args != '': - infer_args += '^' # '^' must be CommandLineOption.env_var_sep - infer_args += '--fcp-apple-clang^' + self.apple_clang_path - env_vars['INFER_ARGS'] = infer_args - return env_vars - - def capture(self): - # these settings will instruct xcodebuild on which clang to use - self.cmd += ['CC={wrapper}'.format(wrapper=CLANG_WRAPPER)] - self.cmd += [ - 'CPLUSPLUS={wrapper}'.format(wrapper=CLANGPLUSPLUS_WRAPPER)] - - # skip the ProcessPCH phase to fix the "newer/older" incompatibility - # error for the pch files generated by apple's clang and - # the open-source one - self.cmd += ['GCC_PRECOMPILE_PREFIX_HEADER=NO'] - - try: - env = utils.encode_env(self.get_envvars()) - cmd = map(utils.encode, self.cmd) - subprocess.check_call(cmd, env=env) - return os.EX_OK - except subprocess.CalledProcessError as exc: - if self.args.debug: - traceback.print_exc() - return exc.returncode diff --git a/infer/src/base/Process.ml b/infer/src/base/Process.ml index 6791d9c38..0d99a867f 100644 --- a/infer/src/base/Process.ml +++ b/infer/src/base/Process.ml @@ -35,6 +35,23 @@ let create_process_and_wait ~prog ~args = (Unix.Exit_or_signal.to_string_hum status) +let create_process_and_wait_with_output ~prog ~args = + let {Unix.Process_info.stdin; stdout; stderr; pid} = Unix.create_process ~prog ~args in + let stderr_chan = Unix.in_channel_of_descr stderr in + let stdout_chan = Unix.in_channel_of_descr stdout in + Unix.close stdin ; + match Unix.waitpid pid with + | Ok () -> + let out = In_channel.input_all stdout_chan in + let err = In_channel.input_all stderr_chan in + In_channel.close stdout_chan ; In_channel.close stderr_chan ; (out, err) + | Error _ as status -> + L.(die ExternalError) + "Error executing: %s@\n%s@\n" + (String.concat ~sep:" " (prog :: args)) + (Unix.Exit_or_signal.to_string_hum status) + + let pipeline ~producer_prog ~producer_args ~consumer_prog ~consumer_args = let pipe_in, pipe_out = Unix.pipe () in match Unix.fork () with diff --git a/infer/src/base/Process.mli b/infer/src/base/Process.mli index 0c6f9967e..12af21769 100644 --- a/infer/src/base/Process.mli +++ b/infer/src/base/Process.mli @@ -12,6 +12,11 @@ val create_process_and_wait : prog:string -> args:string list -> unit execution. The standard out and error are not redirected. If the commands fails to execute, prints an error message and exits. *) +val create_process_and_wait_with_output : prog:string -> args:string list -> string * string +(** Given an command to be executed, creates a process to execute this command, and waits for its + execution. The standard out and error are returned. If the commands fails to execute, prints an + error message and exits. *) + val print_error_and_exit : ?exit_code:int -> ('a, Format.formatter, unit, 'b) format4 -> 'a (** Prints an error message to a log file, prints a message saying that the error can be found in that file, and exist, with default code 1 or a given code. *) diff --git a/infer/src/integration/Driver.ml b/infer/src/integration/Driver.ml index 3c702a08f..ecfe1e3b1 100644 --- a/infer/src/integration/Driver.ml +++ b/infer/src/integration/Driver.ml @@ -26,6 +26,7 @@ type mode = | Maven of string * string list | NdkBuild of string list | PythonCapture of Config.build_system * string list + | XcodeBuild of string * string list | XcodeXcpretty of string * string list let is_analyze_mode = function Analyze -> true | _ -> false @@ -48,6 +49,8 @@ let pp_mode fmt = function F.fprintf fmt "PythonCapture driver mode:@\nbuild system = '%s'@\nargs = %a" (Config.string_of_build_system bs) Pp.cli_args args + | XcodeBuild (prog, args) -> + F.fprintf fmt "XcodeBuild driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args | XcodeXcpretty (prog, args) -> F.fprintf fmt "XcodeXcpretty driver mode:@\nprog = '%s'@\nargs = %a" prog Pp.cli_args args | Javac (_, prog, args) -> @@ -290,9 +293,13 @@ let capture ~changed_files = function L.progress "Capturing in maven mode...@." ; Maven.capture ~prog ~args | NdkBuild build_cmd -> + L.progress "Capturing in ndk-build mode...@." ; NdkBuild.capture ~build_cmd | PythonCapture (build_system, build_cmd) -> python_capture build_system build_cmd + | XcodeBuild (prog, args) -> + L.progress "Capturing in xcodebuild mode...@." ; + XcodeBuild.capture ~prog ~args | XcodeXcpretty (prog, args) -> L.progress "Capturing using xcodebuild and xcpretty...@." ; check_xcpretty () ; @@ -534,11 +541,13 @@ let mode_of_build_command build_cmd (buck_mode : BuckMode.t option) = Maven (prog, args) | BXcode, _ when Config.xcpretty -> XcodeXcpretty (prog, args) + | BXcode, _ -> + XcodeBuild (prog, args) | BBuck, Some ClangFlavors -> BuckClangFlavor build_cmd | BNdk, _ -> NdkBuild build_cmd - | ((BAnt | BGradle | BXcode) as build_system), _ -> + | ((BAnt | BGradle) as build_system), _ -> PythonCapture (build_system, build_cmd) ) diff --git a/infer/src/integration/Driver.mli b/infer/src/integration/Driver.mli index ef9319bcd..392a37ffc 100644 --- a/infer/src/integration/Driver.mli +++ b/infer/src/integration/Driver.mli @@ -23,6 +23,7 @@ type mode = | Maven of string * string list | NdkBuild of string list | PythonCapture of Config.build_system * string list + | XcodeBuild of string * string list | XcodeXcpretty of string * string list val is_analyze_mode : mode -> bool diff --git a/infer/src/integration/XcodeBuild.ml b/infer/src/integration/XcodeBuild.ml new file mode 100644 index 000000000..5551f3f56 --- /dev/null +++ b/infer/src/integration/XcodeBuild.ml @@ -0,0 +1,50 @@ +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) +open! IStd +module L = Logging + +let capture ~prog ~args = + let apple_clang = + Process.create_process_and_wait_with_output ~prog:"xcrun" ~args:["--find"; "clang"] + |> fst |> String.strip + in + let xcode_version, _ = + Process.create_process_and_wait_with_output ~prog:"xcodebuild" ~args:["-version"] + in + let apple_clang_version, _ = + Process.create_process_and_wait_with_output ~prog:apple_clang ~args:["--version"] + in + L.environment_info "Xcode version: %s@." xcode_version ; + L.environment_info "clang version: %s@." apple_clang_version ; + let args = + List.append args + [ Printf.sprintf "CC=%s" Config.wrappers_dir ^/ "clang" + ; Printf.sprintf "CPLUSPLUS=%s" Config.wrappers_dir ^/ "clang++" + ; "GCC_PRECOMPILE_PREFIX_HEADER=NO" ] + in + let infer_args = + Option.fold (Sys.getenv CommandLineOption.args_env_var) + ~init:(Printf.sprintf "%s%c%s" "--fcp-apple-clang" CommandLineOption.env_var_sep apple_clang) + ~f:(fun acc arg -> Printf.sprintf "%s%c%s" acc CommandLineOption.env_var_sep arg) + in + L.debug Capture Verbose "%s [%s] [%s]@." prog (String.concat ~sep:"," args) infer_args ; + let {Unix.Process_info.stdin; stdout; stderr; pid} = + Unix.create_process_env ~prog ~args + ~env:(`Extend [(CommandLineOption.args_env_var, infer_args)]) + () + in + let stdout_chan = Unix.in_channel_of_descr stdout in + let stderr_chan = Unix.in_channel_of_descr stderr in + Unix.close stdin ; + Utils.with_channel_in stdout_chan ~f:(L.progress "XCODEBUILD: %s@.") ; + Utils.with_channel_in stderr_chan ~f:(L.progress "XCODEBUILD: %s@.") ; + match Unix.waitpid pid with + | Ok () -> + In_channel.close stdout_chan ; In_channel.close stderr_chan + | Error _ as err -> + L.die ExternalError "*** capture failed to execute: %s" + (Unix.Exit_or_signal.to_string_hum err) diff --git a/infer/src/integration/XcodeBuild.mli b/infer/src/integration/XcodeBuild.mli new file mode 100644 index 000000000..9b11361af --- /dev/null +++ b/infer/src/integration/XcodeBuild.mli @@ -0,0 +1,10 @@ +(* + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + *) + +open! IStd + +val capture : prog:string -> args:string list -> unit