From d0825bca7fe65beaee391d30da42e937db621564 Mon Sep 17 00:00:00 2001 From: Steve Block Date: Tue, 2 Feb 2010 14:57:50 +0000 Subject: Merge webkit.org at r54127 : Initial merge by git Change-Id: Ib661abb595522f50ea406f72d3a0ce17f7193c82 --- WebKitTools/Scripts/webkitpy/executive.py | 171 ++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 WebKitTools/Scripts/webkitpy/executive.py (limited to 'WebKitTools/Scripts/webkitpy/executive.py') diff --git a/WebKitTools/Scripts/webkitpy/executive.py b/WebKitTools/Scripts/webkitpy/executive.py new file mode 100644 index 0000000..50b119b --- /dev/null +++ b/WebKitTools/Scripts/webkitpy/executive.py @@ -0,0 +1,171 @@ +# Copyright (c) 2009, Google Inc. All rights reserved. +# Copyright (c) 2009 Apple Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +import StringIO +import subprocess +import sys + +from webkitpy.webkit_logging import tee + + +class ScriptError(Exception): + + def __init__(self, + message=None, + script_args=None, + exit_code=None, + output=None, + cwd=None): + if not message: + message = 'Failed to run "%s"' % script_args + if exit_code: + message += " exit_code: %d" % exit_code + if cwd: + message += " cwd: %s" % cwd + + Exception.__init__(self, message) + self.script_args = script_args # 'args' is already used by Exception + self.exit_code = exit_code + self.output = output + self.cwd = cwd + + def message_with_output(self, output_limit=500): + if self.output: + if output_limit and len(self.output) > output_limit: + return "%s\nLast %s characters of output:\n%s" % \ + (self, output_limit, self.output[-output_limit:]) + return "%s\n%s" % (self, self.output) + return str(self) + + def command_name(self): + command_path = self.script_args + if type(command_path) is list: + command_path = command_path[0] + return os.path.basename(command_path) + + +def run_command(*args, **kwargs): + # FIXME: This should not be a global static. + # New code should use Executive.run_command directly instead + return Executive().run_command(*args, **kwargs) + + +class Executive(object): + + def _run_command_with_teed_output(self, args, teed_output): + child_process = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + # Use our own custom wait loop because Popen ignores a tee'd + # stderr/stdout. + # FIXME: This could be improved not to flatten output to stdout. + while True: + output_line = child_process.stdout.readline() + if output_line == "" and child_process.poll() != None: + return child_process.poll() + teed_output.write(output_line) + + def run_and_throw_if_fail(self, args, quiet=False): + # Cache the child's output locally so it can be used for error reports. + child_out_file = StringIO.StringIO() + if quiet: + dev_null = open(os.devnull, "w") + child_stdout = tee(child_out_file, dev_null if quiet else sys.stdout) + exit_code = self._run_command_with_teed_output(args, child_stdout) + if quiet: + dev_null.close() + + child_output = child_out_file.getvalue() + child_out_file.close() + + if exit_code: + raise ScriptError(script_args=args, + exit_code=exit_code, + output=child_output) + + @staticmethod + def cpu_count(): + # This API exists only in Python 2.6 and higher. :( + try: + import multiprocessing + return multiprocessing.cpu_count() + except (ImportError, NotImplementedError): + # This quantity is a lie but probably a reasonable guess for modern + # machines. + return 2 + + # Error handlers do not need to be static methods once all callers are + # updated to use an Executive object. + + @staticmethod + def default_error_handler(error): + raise error + + @staticmethod + def ignore_error(error): + pass + + # FIXME: This should be merged with run_and_throw_if_fail + + def run_command(self, + args, + cwd=None, + input=None, + error_handler=None, + return_exit_code=False, + return_stderr=True): + if hasattr(input, 'read'): # Check if the input is a file. + stdin = input + string_to_communicate = None + else: + stdin = subprocess.PIPE if input else None + string_to_communicate = input + if return_stderr: + stderr = subprocess.STDOUT + else: + stderr = None + + process = subprocess.Popen(args, + stdin=stdin, + stdout=subprocess.PIPE, + stderr=stderr, + cwd=cwd) + output = process.communicate(string_to_communicate)[0] + exit_code = process.wait() + if exit_code: + script_error = ScriptError(script_args=args, + exit_code=exit_code, + output=output, + cwd=cwd) + (error_handler or self.default_error_handler)(script_error) + if return_exit_code: + return exit_code + return output -- cgit v1.1