|  | # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. | 
|  | # Use of this source code is governed by a BSD-style license that can be | 
|  | # found in the LICENSE file. | 
|  | """Shared process-related utility functions.""" | 
|  |  | 
|  | import errno | 
|  | import os | 
|  | import subprocess | 
|  | import sys | 
|  |  | 
|  | class CommandNotFound(Exception): pass | 
|  |  | 
|  |  | 
|  | TASKKILL = os.path.join(os.environ['WINDIR'], 'system32', 'taskkill.exe') | 
|  | TASKKILL_PROCESS_NOT_FOUND_ERR = 128 | 
|  | # On windows 2000 there is no taskkill.exe, we need to have pskill somewhere | 
|  | # in the path. | 
|  | PSKILL = 'pskill.exe' | 
|  | PSKILL_PROCESS_NOT_FOUND_ERR = -1 | 
|  |  | 
|  | def KillAll(executables): | 
|  | """Tries to kill all copies of each process in the processes list.  Returns | 
|  | an error if any running processes couldn't be killed. | 
|  | """ | 
|  | result = 0 | 
|  | if os.path.exists(TASKKILL): | 
|  | command = [TASKKILL, '/f', '/im'] | 
|  | process_not_found_err = TASKKILL_PROCESS_NOT_FOUND_ERR | 
|  | else: | 
|  | command = [PSKILL, '/t'] | 
|  | process_not_found_err = PSKILL_PROCESS_NOT_FOUND_ERR | 
|  |  | 
|  | for name in executables: | 
|  | new_error = RunCommand(command + [name]) | 
|  | # Ignore "process not found" error. | 
|  | if new_error != 0 and new_error != process_not_found_err: | 
|  | result = new_error | 
|  | return result | 
|  |  | 
|  | def RunCommandFull(command, verbose=True, collect_output=False, | 
|  | print_output=True): | 
|  | """Runs the command list. | 
|  |  | 
|  | Prints the given command (which should be a list of one or more strings). | 
|  | If specified, prints its stderr (and optionally stdout) to stdout, | 
|  | line-buffered, converting line endings to CRLF (see note below).  If | 
|  | specified, collects the output as a list of lines and returns it.  Waits | 
|  | for the command to terminate and returns its status. | 
|  |  | 
|  | Args: | 
|  | command: the full command to run, as a list of one or more strings | 
|  | verbose: if True, combines all output (stdout and stderr) into stdout. | 
|  | Otherwise, prints only the command's stderr to stdout. | 
|  | collect_output: if True, collects the output of the command as a list of | 
|  | lines and returns it | 
|  | print_output: if True, prints the output of the command | 
|  |  | 
|  | Returns: | 
|  | A tuple consisting of the process's exit status and output.  If | 
|  | collect_output is False, the output will be []. | 
|  |  | 
|  | Raises: | 
|  | CommandNotFound if the command executable could not be found. | 
|  | """ | 
|  | print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n', ### | 
|  |  | 
|  | if verbose: | 
|  | out = subprocess.PIPE | 
|  | err = subprocess.STDOUT | 
|  | else: | 
|  | out = file(os.devnull, 'w') | 
|  | err = subprocess.PIPE | 
|  | try: | 
|  | proc = subprocess.Popen(command, stdout=out, stderr=err, bufsize=1) | 
|  | except OSError, e: | 
|  | if e.errno == errno.ENOENT: | 
|  | raise CommandNotFound('Unable to find "%s"' % command[0]) | 
|  | raise | 
|  |  | 
|  | output = [] | 
|  |  | 
|  | if verbose: | 
|  | read_from = proc.stdout | 
|  | else: | 
|  | read_from = proc.stderr | 
|  | line = read_from.readline() | 
|  | while line: | 
|  | line = line.rstrip() | 
|  |  | 
|  | if collect_output: | 
|  | output.append(line) | 
|  |  | 
|  | if print_output: | 
|  | # Windows Python converts \n to \r\n automatically whenever it | 
|  | # encounters it written to a text file (including stdout).  The only | 
|  | # way around it is to write to a binary file, which isn't feasible for | 
|  | # stdout. So we end up with \r\n here even though we explicitly write | 
|  | # \n.  (We could write \r instead, which doesn't get converted to \r\n, | 
|  | # but that's probably more troublesome for people trying to read the | 
|  | # files.) | 
|  | print line + '\n', | 
|  |  | 
|  | # Python on windows writes the buffer only when it reaches 4k. This is | 
|  | # not fast enough for all purposes. | 
|  | sys.stdout.flush() | 
|  | line = read_from.readline() | 
|  |  | 
|  | # Make sure the process terminates. | 
|  | proc.wait() | 
|  |  | 
|  | if not verbose: | 
|  | out.close() | 
|  | return (proc.returncode, output) | 
|  |  | 
|  | def RunCommand(command, verbose=True): | 
|  | """Runs the command list, printing its output and returning its exit status. | 
|  |  | 
|  | Prints the given command (which should be a list of one or more strings), | 
|  | then runs it and prints its stderr (and optionally stdout) to stdout, | 
|  | line-buffered, converting line endings to CRLF.  Waits for the command to | 
|  | terminate and returns its status. | 
|  |  | 
|  | Args: | 
|  | command: the full command to run, as a list of one or more strings | 
|  | verbose: if True, combines all output (stdout and stderr) into stdout. | 
|  | Otherwise, prints only the command's stderr to stdout. | 
|  |  | 
|  | Returns: | 
|  | The process's exit status. | 
|  |  | 
|  | Raises: | 
|  | CommandNotFound if the command executable could not be found. | 
|  | """ | 
|  | return RunCommandFull(command, verbose)[0] | 
|  |  | 
|  | def RunCommandsInParallel(commands, verbose=True, collect_output=False, | 
|  | print_output=True): | 
|  | """Runs a list of commands in parallel, waits for all commands to terminate | 
|  | and returns their status. If specified, the ouput of commands can be | 
|  | returned and/or printed. | 
|  |  | 
|  | Args: | 
|  | commands: the list of commands to run, each as a list of one or more | 
|  | strings. | 
|  | verbose: if True, combines stdout and stderr into stdout. | 
|  | Otherwise, prints only the command's stderr to stdout. | 
|  | collect_output: if True, collects the output of the each command as a list | 
|  | of lines and returns it. | 
|  | print_output: if True, prints the output of each command. | 
|  |  | 
|  | Returns: | 
|  | A list of tuples consisting of each command's exit status and output.  If | 
|  | collect_output is False, the output will be []. | 
|  |  | 
|  | Raises: | 
|  | CommandNotFound if any of the command executables could not be found. | 
|  | """ | 
|  |  | 
|  | command_num = len(commands) | 
|  | outputs = [[] for i in xrange(command_num)] | 
|  | procs = [None for i in xrange(command_num)] | 
|  | eofs = [False for i in xrange(command_num)] | 
|  |  | 
|  | for command in commands: | 
|  | print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n', | 
|  |  | 
|  | if verbose: | 
|  | out = subprocess.PIPE | 
|  | err = subprocess.STDOUT | 
|  | else: | 
|  | out = file(os.devnull, 'w') | 
|  | err = subprocess.PIPE | 
|  |  | 
|  | for i in xrange(command_num): | 
|  | try: | 
|  | command = commands[i] | 
|  | procs[i] = subprocess.Popen(command, stdout=out, stderr=err, bufsize=1) | 
|  | except OSError, e: | 
|  | if e.errno == errno.ENOENT: | 
|  | raise CommandNotFound('Unable to find "%s"' % command[0]) | 
|  | raise | 
|  | # We could consider terminating the processes already started. | 
|  | # But Popen.kill() is only available in version 2.6. | 
|  | # For now the clean up is done by KillAll. | 
|  |  | 
|  | while True: | 
|  | eof_all = True | 
|  | for i in xrange(command_num): | 
|  | if eofs[i]: | 
|  | continue | 
|  | if verbose: | 
|  | read_from = procs[i].stdout | 
|  | else: | 
|  | read_from = procs[i].stderr | 
|  | line = read_from.readline() | 
|  | if line: | 
|  | eof_all = False | 
|  | line = line.rstrip() | 
|  | outputs[i].append(line) | 
|  | if print_output: | 
|  | # Windows Python converts \n to \r\n automatically whenever it | 
|  | # encounters it written to a text file (including stdout).  The only | 
|  | # way around it is to write to a binary file, which isn't feasible | 
|  | # for stdout. So we end up with \r\n here even though we explicitly | 
|  | # write \n.  (We could write \r instead, which doesn't get converted | 
|  | # to \r\n, but that's probably more troublesome for people trying to | 
|  | # read the files.) | 
|  | print line + '\n', | 
|  | else: | 
|  | eofs[i] = True | 
|  | if eof_all: | 
|  | break | 
|  |  | 
|  | # Make sure the process terminates. | 
|  | for i in xrange(command_num): | 
|  | procs[i].wait() | 
|  |  | 
|  | if not verbose: | 
|  | out.close() | 
|  |  | 
|  | return [(procs[i].returncode, outputs[i]) for i in xrange(command_num)] |