Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / tools / python / google / process_utils.py
blob64c92ea9f1976cbe2ad463a7f3ff8f6480f3aa45
1 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4 """Shared process-related utility functions."""
6 import errno
7 import os
8 import subprocess
9 import sys
11 class CommandNotFound(Exception): pass
14 TASKKILL = os.path.join(os.environ['WINDIR'], 'system32', 'taskkill.exe')
15 TASKKILL_PROCESS_NOT_FOUND_ERR = 128
16 # On windows 2000 there is no taskkill.exe, we need to have pskill somewhere
17 # in the path.
18 PSKILL = 'pskill.exe'
19 PSKILL_PROCESS_NOT_FOUND_ERR = -1
21 def KillAll(executables):
22 """Tries to kill all copies of each process in the processes list. Returns
23 an error if any running processes couldn't be killed.
24 """
25 result = 0
26 if os.path.exists(TASKKILL):
27 command = [TASKKILL, '/f', '/im']
28 process_not_found_err = TASKKILL_PROCESS_NOT_FOUND_ERR
29 else:
30 command = [PSKILL, '/t']
31 process_not_found_err = PSKILL_PROCESS_NOT_FOUND_ERR
33 for name in executables:
34 new_error = RunCommand(command + [name])
35 # Ignore "process not found" error.
36 if new_error != 0 and new_error != process_not_found_err:
37 result = new_error
38 return result
40 def RunCommandFull(command, verbose=True, collect_output=False,
41 print_output=True):
42 """Runs the command list.
44 Prints the given command (which should be a list of one or more strings).
45 If specified, prints its stderr (and optionally stdout) to stdout,
46 line-buffered, converting line endings to CRLF (see note below). If
47 specified, collects the output as a list of lines and returns it. Waits
48 for the command to terminate and returns its status.
50 Args:
51 command: the full command to run, as a list of one or more strings
52 verbose: if True, combines all output (stdout and stderr) into stdout.
53 Otherwise, prints only the command's stderr to stdout.
54 collect_output: if True, collects the output of the command as a list of
55 lines and returns it
56 print_output: if True, prints the output of the command
58 Returns:
59 A tuple consisting of the process's exit status and output. If
60 collect_output is False, the output will be [].
62 Raises:
63 CommandNotFound if the command executable could not be found.
64 """
65 print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n', ###
67 if verbose:
68 out = subprocess.PIPE
69 err = subprocess.STDOUT
70 else:
71 out = file(os.devnull, 'w')
72 err = subprocess.PIPE
73 try:
74 proc = subprocess.Popen(command, stdout=out, stderr=err, bufsize=1)
75 except OSError, e:
76 if e.errno == errno.ENOENT:
77 raise CommandNotFound('Unable to find "%s"' % command[0])
78 raise
80 output = []
82 if verbose:
83 read_from = proc.stdout
84 else:
85 read_from = proc.stderr
86 line = read_from.readline()
87 while line:
88 line = line.rstrip()
90 if collect_output:
91 output.append(line)
93 if print_output:
94 # Windows Python converts \n to \r\n automatically whenever it
95 # encounters it written to a text file (including stdout). The only
96 # way around it is to write to a binary file, which isn't feasible for
97 # stdout. So we end up with \r\n here even though we explicitly write
98 # \n. (We could write \r instead, which doesn't get converted to \r\n,
99 # but that's probably more troublesome for people trying to read the
100 # files.)
101 print line + '\n',
103 # Python on windows writes the buffer only when it reaches 4k. This is
104 # not fast enough for all purposes.
105 sys.stdout.flush()
106 line = read_from.readline()
108 # Make sure the process terminates.
109 proc.wait()
111 if not verbose:
112 out.close()
113 return (proc.returncode, output)
115 def RunCommand(command, verbose=True):
116 """Runs the command list, printing its output and returning its exit status.
118 Prints the given command (which should be a list of one or more strings),
119 then runs it and prints its stderr (and optionally stdout) to stdout,
120 line-buffered, converting line endings to CRLF. Waits for the command to
121 terminate and returns its status.
123 Args:
124 command: the full command to run, as a list of one or more strings
125 verbose: if True, combines all output (stdout and stderr) into stdout.
126 Otherwise, prints only the command's stderr to stdout.
128 Returns:
129 The process's exit status.
131 Raises:
132 CommandNotFound if the command executable could not be found.
134 return RunCommandFull(command, verbose)[0]
136 def RunCommandsInParallel(commands, verbose=True, collect_output=False,
137 print_output=True):
138 """Runs a list of commands in parallel, waits for all commands to terminate
139 and returns their status. If specified, the ouput of commands can be
140 returned and/or printed.
142 Args:
143 commands: the list of commands to run, each as a list of one or more
144 strings.
145 verbose: if True, combines stdout and stderr into stdout.
146 Otherwise, prints only the command's stderr to stdout.
147 collect_output: if True, collects the output of the each command as a list
148 of lines and returns it.
149 print_output: if True, prints the output of each command.
151 Returns:
152 A list of tuples consisting of each command's exit status and output. If
153 collect_output is False, the output will be [].
155 Raises:
156 CommandNotFound if any of the command executables could not be found.
159 command_num = len(commands)
160 outputs = [[] for i in xrange(command_num)]
161 procs = [None for i in xrange(command_num)]
162 eofs = [False for i in xrange(command_num)]
164 for command in commands:
165 print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n',
167 if verbose:
168 out = subprocess.PIPE
169 err = subprocess.STDOUT
170 else:
171 out = file(os.devnull, 'w')
172 err = subprocess.PIPE
174 for i in xrange(command_num):
175 try:
176 command = commands[i]
177 procs[i] = subprocess.Popen(command, stdout=out, stderr=err, bufsize=1)
178 except OSError, e:
179 if e.errno == errno.ENOENT:
180 raise CommandNotFound('Unable to find "%s"' % command[0])
181 raise
182 # We could consider terminating the processes already started.
183 # But Popen.kill() is only available in version 2.6.
184 # For now the clean up is done by KillAll.
186 while True:
187 eof_all = True
188 for i in xrange(command_num):
189 if eofs[i]:
190 continue
191 if verbose:
192 read_from = procs[i].stdout
193 else:
194 read_from = procs[i].stderr
195 line = read_from.readline()
196 if line:
197 eof_all = False
198 line = line.rstrip()
199 outputs[i].append(line)
200 if print_output:
201 # Windows Python converts \n to \r\n automatically whenever it
202 # encounters it written to a text file (including stdout). The only
203 # way around it is to write to a binary file, which isn't feasible
204 # for stdout. So we end up with \r\n here even though we explicitly
205 # write \n. (We could write \r instead, which doesn't get converted
206 # to \r\n, but that's probably more troublesome for people trying to
207 # read the files.)
208 print line + '\n',
209 else:
210 eofs[i] = True
211 if eof_all:
212 break
214 # Make sure the process terminates.
215 for i in xrange(command_num):
216 procs[i].wait()
218 if not verbose:
219 out.close()
221 return [(procs[i].returncode, outputs[i]) for i in xrange(command_num)]