Chromium Code Reviews| Index: tools/python/google/process_utils.py |
| =================================================================== |
| --- tools/python/google/process_utils.py (revision 21827) |
| +++ tools/python/google/process_utils.py (working copy) |
| @@ -133,3 +133,89 @@ |
| """ |
| 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 |
|
M-A Ruel
2009/07/29 22:24:46
This looks odd.
sys.stdout.write(line + '\n')
will
|
| + # 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)] |