Home | History | Annotate | Download | only in google
      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."""
      5 
      6 import errno
      7 import os
      8 import subprocess
      9 import sys
     10 
     11 class CommandNotFound(Exception): pass
     12 
     13 
     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
     20 
     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
     32 
     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
     39 
     40 def RunCommandFull(command, verbose=True, collect_output=False,
     41                    print_output=True):
     42   """Runs the command list.
     43 
     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.
     49 
     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
     57 
     58   Returns:
     59     A tuple consisting of the process's exit status and output.  If
     60     collect_output is False, the output will be [].
     61 
     62   Raises:
     63     CommandNotFound if the command executable could not be found.
     64   """
     65   print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n', ###
     66 
     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
     79 
     80   output = []
     81 
     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()
     89 
     90     if collect_output:
     91       output.append(line)
     92 
     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',
    102 
    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()
    107 
    108   # Make sure the process terminates.
    109   proc.wait()
    110 
    111   if not verbose:
    112     out.close()
    113   return (proc.returncode, output)
    114 
    115 def RunCommand(command, verbose=True):
    116   """Runs the command list, printing its output and returning its exit status.
    117 
    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.
    122 
    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.
    127 
    128   Returns:
    129     The process's exit status.
    130 
    131   Raises:
    132     CommandNotFound if the command executable could not be found.
    133   """
    134   return RunCommandFull(command, verbose)[0]
    135 
    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.
    141 
    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.
    150 
    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 [].
    154 
    155   Raises:
    156     CommandNotFound if any of the command executables could not be found.
    157   """
    158 
    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)]
    163 
    164   for command in commands:
    165     print '\n' + subprocess.list2cmdline(command).replace('\\', '/') + '\n',
    166 
    167   if verbose:
    168     out = subprocess.PIPE
    169     err = subprocess.STDOUT
    170   else:
    171     out = file(os.devnull, 'w')
    172     err = subprocess.PIPE
    173 
    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.
    185 
    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
    213 
    214   # Make sure the process terminates.
    215   for i in xrange(command_num):
    216     procs[i].wait()
    217 
    218   if not verbose:
    219     out.close()
    220 
    221   return [(procs[i].returncode, outputs[i]) for i in xrange(command_num)]
    222