| OLD | NEW |
| 1 # Copyright (c) 2009, Google Inc. All rights reserved. | 1 # Copyright (c) 2009, Google Inc. All rights reserved. |
| 2 # Copyright (c) 2009 Apple Inc. All rights reserved. | 2 # Copyright (c) 2009 Apple Inc. All rights reserved. |
| 3 # | 3 # |
| 4 # Redistribution and use in source and binary forms, with or without | 4 # Redistribution and use in source and binary forms, with or without |
| 5 # modification, are permitted provided that the following conditions are | 5 # modification, are permitted provided that the following conditions are |
| 6 # met: | 6 # met: |
| 7 # | 7 # |
| 8 # * Redistributions of source code must retain the above copyright | 8 # * Redistributions of source code must retain the above copyright |
| 9 # notice, this list of conditions and the following disclaimer. | 9 # notice, this list of conditions and the following disclaimer. |
| 10 # * Redistributions in binary form must reproduce the above | 10 # * Redistributions in binary form must reproduce the above |
| (...skipping 19 matching lines...) Expand all Loading... |
| 30 import csv | 30 import csv |
| 31 import errno | 31 import errno |
| 32 import logging | 32 import logging |
| 33 import multiprocessing | 33 import multiprocessing |
| 34 import os | 34 import os |
| 35 import signal | 35 import signal |
| 36 import subprocess | 36 import subprocess |
| 37 import sys | 37 import sys |
| 38 import time | 38 import time |
| 39 | 39 |
| 40 from webkitpy.common.system.outputtee import Tee |
| 40 from webkitpy.common.system.filesystem import FileSystem | 41 from webkitpy.common.system.filesystem import FileSystem |
| 41 | 42 |
| 42 | 43 |
| 43 _log = logging.getLogger(__name__) | 44 _log = logging.getLogger(__name__) |
| 44 | 45 |
| 45 | 46 |
| 46 class ScriptError(Exception): | 47 class ScriptError(Exception): |
| 47 | 48 |
| 48 def __init__(self, | 49 def __init__(self, |
| 49 message=None, | 50 message=None, |
| (...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 160 # Cygwin python sometimes can't kill native processes. | 161 # Cygwin python sometimes can't kill native processes. |
| 161 return | 162 return |
| 162 raise | 163 raise |
| 163 | 164 |
| 164 def _win32_check_running_pid(self, pid): | 165 def _win32_check_running_pid(self, pid): |
| 165 # importing ctypes at the top-level seems to cause weird crashes at | 166 # importing ctypes at the top-level seems to cause weird crashes at |
| 166 # exit under cygwin on apple's win port. Only win32 needs cygwin, so | 167 # exit under cygwin on apple's win port. Only win32 needs cygwin, so |
| 167 # we import it here instead. See https://bugs.webkit.org/show_bug.cgi?id
=91682 | 168 # we import it here instead. See https://bugs.webkit.org/show_bug.cgi?id
=91682 |
| 168 import ctypes | 169 import ctypes |
| 169 | 170 |
| 170 # pylint: disable=invalid-name | |
| 171 class PROCESSENTRY32(ctypes.Structure): | 171 class PROCESSENTRY32(ctypes.Structure): |
| 172 _fields_ = [("dwSize", ctypes.c_ulong), | 172 _fields_ = [("dwSize", ctypes.c_ulong), |
| 173 ("cntUsage", ctypes.c_ulong), | 173 ("cntUsage", ctypes.c_ulong), |
| 174 ("th32ProcessID", ctypes.c_ulong), | 174 ("th32ProcessID", ctypes.c_ulong), |
| 175 ("th32DefaultHeapID", ctypes.POINTER(ctypes.c_ulong)), | 175 ("th32DefaultHeapID", ctypes.POINTER(ctypes.c_ulong)), |
| 176 ("th32ModuleID", ctypes.c_ulong), | 176 ("th32ModuleID", ctypes.c_ulong), |
| 177 ("cntThreads", ctypes.c_ulong), | 177 ("cntThreads", ctypes.c_ulong), |
| 178 ("th32ParentProcessID", ctypes.c_ulong), | 178 ("th32ParentProcessID", ctypes.c_ulong), |
| 179 ("pcPriClassBase", ctypes.c_ulong), | 179 ("pcPriClassBase", ctypes.c_ulong), |
| 180 ("dwFlags", ctypes.c_ulong), | 180 ("dwFlags", ctypes.c_ulong), |
| 181 ("szExeFile", ctypes.c_char * 260)] | 181 ("szExeFile", ctypes.c_char * 260)] |
| 182 | 182 |
| 183 CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapsh
ot | 183 CreateToolhelp32Snapshot = ctypes.windll.kernel32.CreateToolhelp32Snapsh
ot |
| 184 Process32First = ctypes.windll.kernel32.Process32First | 184 Process32First = ctypes.windll.kernel32.Process32First |
| 185 Process32Next = ctypes.windll.kernel32.Process32Next | 185 Process32Next = ctypes.windll.kernel32.Process32Next |
| 186 CloseHandle = ctypes.windll.kernel32.CloseHandle | 186 CloseHandle = ctypes.windll.kernel32.CloseHandle |
| 187 TH32CS_SNAPPROCESS = 0x00000002 # win32 magic number | 187 TH32CS_SNAPPROCESS = 0x00000002 # win32 magic number |
| 188 hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) | 188 hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) |
| 189 pe32 = PROCESSENTRY32() | 189 pe32 = PROCESSENTRY32() |
| 190 pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) # pylint: disable=attribute
-defined-outside-init | 190 pe32.dwSize = ctypes.sizeof(PROCESSENTRY32) |
| 191 result = False | 191 result = False |
| 192 if not Process32First(hProcessSnap, ctypes.byref(pe32)): | 192 if not Process32First(hProcessSnap, ctypes.byref(pe32)): |
| 193 _log.debug("Failed getting first process.") | 193 _log.debug("Failed getting first process.") |
| 194 CloseHandle(hProcessSnap) | 194 CloseHandle(hProcessSnap) |
| 195 return result | 195 return result |
| 196 while True: | 196 while True: |
| 197 if pe32.th32ProcessID == pid: | 197 if pe32.th32ProcessID == pid: |
| 198 result = True | 198 result = True |
| 199 break | 199 break |
| 200 if not Process32Next(hProcessSnap, ctypes.byref(pe32)): | 200 if not Process32Next(hProcessSnap, ctypes.byref(pe32)): |
| (...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 304 # updated to use an Executive object. | 304 # updated to use an Executive object. |
| 305 | 305 |
| 306 @staticmethod | 306 @staticmethod |
| 307 def default_error_handler(error): | 307 def default_error_handler(error): |
| 308 raise error | 308 raise error |
| 309 | 309 |
| 310 @staticmethod | 310 @staticmethod |
| 311 def ignore_error(error): | 311 def ignore_error(error): |
| 312 pass | 312 pass |
| 313 | 313 |
| 314 def _compute_stdin(self, input_obj): | 314 def _compute_stdin(self, input): |
| 315 """Returns (stdin, string_to_communicate)""" | 315 """Returns (stdin, string_to_communicate)""" |
| 316 # FIXME: We should be returning /dev/null for stdin | 316 # FIXME: We should be returning /dev/null for stdin |
| 317 # or closing stdin after process creation to prevent | 317 # or closing stdin after process creation to prevent |
| 318 # child processes from getting input from the user. | 318 # child processes from getting input from the user. |
| 319 if not input_obj: | 319 if not input: |
| 320 return (None, None) | 320 return (None, None) |
| 321 if hasattr(input_obj, "read"): # Check if the input_obj is a file. | 321 if hasattr(input, "read"): # Check if the input is a file. |
| 322 return (input_obj, None) # Assume the file is in the right encoding
. | 322 return (input, None) # Assume the file is in the right encoding. |
| 323 | 323 |
| 324 # Popen in Python 2.5 and before does not automatically encode unicode o
bjects. | 324 # Popen in Python 2.5 and before does not automatically encode unicode o
bjects. |
| 325 # http://bugs.python.org/issue5290 | 325 # http://bugs.python.org/issue5290 |
| 326 # See https://bugs.webkit.org/show_bug.cgi?id=37528 | 326 # See https://bugs.webkit.org/show_bug.cgi?id=37528 |
| 327 # for an example of a regression caused by passing a unicode string dire
ctly. | 327 # for an example of a regression caused by passing a unicode string dire
ctly. |
| 328 # FIXME: We may need to encode differently on different platforms. | 328 # FIXME: We may need to encode differently on different platforms. |
| 329 if isinstance(input_obj, unicode): | 329 if isinstance(input, unicode): |
| 330 input_obj = input_obj.encode(self._child_process_encoding()) | 330 input = input.encode(self._child_process_encoding()) |
| 331 return (self.PIPE, input_obj) | 331 return (self.PIPE, input) |
| 332 | 332 |
| 333 def command_for_printing(self, args): | 333 def command_for_printing(self, args): |
| 334 """Returns a print-ready string representing command args. | 334 """Returns a print-ready string representing command args. |
| 335 The string should be copy/paste ready for execution in a shell.""" | 335 The string should be copy/paste ready for execution in a shell.""" |
| 336 args = self._stringify_args(args) | 336 args = self._stringify_args(args) |
| 337 escaped_args = [] | 337 escaped_args = [] |
| 338 for arg in args: | 338 for arg in args: |
| 339 if isinstance(arg, unicode): | 339 if isinstance(arg, unicode): |
| 340 # Escape any non-ascii characters for easy copy/paste | 340 # Escape any non-ascii characters for easy copy/paste |
| 341 arg = arg.encode("unicode_escape") | 341 arg = arg.encode("unicode_escape") |
| 342 # FIXME: Do we need to fix quotes here? | 342 # FIXME: Do we need to fix quotes here? |
| 343 escaped_args.append(arg) | 343 escaped_args.append(arg) |
| 344 return " ".join(escaped_args) | 344 return " ".join(escaped_args) |
| 345 | 345 |
| 346 def run_command(self, | 346 def run_command(self, |
| 347 args, | 347 args, |
| 348 cwd=None, | 348 cwd=None, |
| 349 env=None, | 349 env=None, |
| 350 input_func=None, | 350 input=None, |
| 351 error_handler=None, | 351 error_handler=None, |
| 352 return_exit_code=False, | 352 return_exit_code=False, |
| 353 return_stderr=True, | 353 return_stderr=True, |
| 354 decode_output=True, debug_logging=True): | 354 decode_output=True, debug_logging=True): |
| 355 """Popen wrapper for convenience and to work around python bugs.""" | 355 """Popen wrapper for convenience and to work around python bugs.""" |
| 356 assert isinstance(args, list) or isinstance(args, tuple) | 356 assert isinstance(args, list) or isinstance(args, tuple) |
| 357 start_time = time.time() | 357 start_time = time.time() |
| 358 | 358 |
| 359 stdin, string_to_communicate = self._compute_stdin(input_func) | 359 stdin, string_to_communicate = self._compute_stdin(input) |
| 360 stderr = self.STDOUT if return_stderr else None | 360 stderr = self.STDOUT if return_stderr else None |
| 361 | 361 |
| 362 process = self.popen(args, | 362 process = self.popen(args, |
| 363 stdin=stdin, | 363 stdin=stdin, |
| 364 stdout=self.PIPE, | 364 stdout=self.PIPE, |
| 365 stderr=stderr, | 365 stderr=stderr, |
| 366 cwd=cwd, | 366 cwd=cwd, |
| 367 env=env, | 367 env=env, |
| 368 close_fds=self._should_close_fds()) | 368 close_fds=self._should_close_fds()) |
| 369 output = process.communicate(string_to_communicate)[0] | 369 output = process.communicate(string_to_communicate)[0] |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 457 pool.close() | 457 pool.close() |
| 458 pool.join() | 458 pool.join() |
| 459 | 459 |
| 460 | 460 |
| 461 def _run_command_thunk(cmd_line_and_cwd): | 461 def _run_command_thunk(cmd_line_and_cwd): |
| 462 # Note that this needs to be a bare module (and hence Picklable) method to w
ork with multiprocessing.Pool. | 462 # Note that this needs to be a bare module (and hence Picklable) method to w
ork with multiprocessing.Pool. |
| 463 (cmd_line, cwd) = cmd_line_and_cwd | 463 (cmd_line, cwd) = cmd_line_and_cwd |
| 464 proc = subprocess.Popen(cmd_line, cwd=cwd, stdout=subprocess.PIPE, stderr=su
bprocess.PIPE) | 464 proc = subprocess.Popen(cmd_line, cwd=cwd, stdout=subprocess.PIPE, stderr=su
bprocess.PIPE) |
| 465 stdout, stderr = proc.communicate() | 465 stdout, stderr = proc.communicate() |
| 466 return (proc.returncode, stdout, stderr) | 466 return (proc.returncode, stdout, stderr) |
| OLD | NEW |