Home | History | Annotate | Download | only in pylib
      1 # Copyright (c) 2012 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 
      5 """A wrapper for subprocess to make calling shell commands easier."""
      6 
      7 import os
      8 import logging
      9 import pipes
     10 import signal
     11 import subprocess
     12 import tempfile
     13 
     14 import constants
     15 
     16 
     17 def _Call(args, stdout=None, stderr=None, shell=None, cwd=None):
     18   return subprocess.call(
     19       args=args, cwd=cwd, stdout=stdout, stderr=stderr,
     20       shell=shell, close_fds=True,
     21       preexec_fn=lambda: signal.signal(signal.SIGPIPE, signal.SIG_DFL))
     22 
     23 
     24 def RunCmd(args, cwd=None):
     25   """Opens a subprocess to execute a program and returns its return value.
     26 
     27   Args:
     28     args: A string or a sequence of program arguments. The program to execute is
     29       the string or the first item in the args sequence.
     30     cwd: If not None, the subprocess's current directory will be changed to
     31       |cwd| before it's executed.
     32 
     33   Returns:
     34     Return code from the command execution.
     35   """
     36   logging.info(str(args) + ' ' + (cwd or ''))
     37   return _Call(args, cwd=cwd)
     38 
     39 
     40 def GetCmdOutput(args, cwd=None, shell=False):
     41   """Open a subprocess to execute a program and returns its output.
     42 
     43   Args:
     44     args: A string or a sequence of program arguments. The program to execute is
     45       the string or the first item in the args sequence.
     46     cwd: If not None, the subprocess's current directory will be changed to
     47       |cwd| before it's executed.
     48     shell: Whether to execute args as a shell command.
     49 
     50   Returns:
     51     Captures and returns the command's stdout.
     52     Prints the command's stderr to logger (which defaults to stdout).
     53   """
     54   (_, output) = GetCmdStatusAndOutput(args, cwd, shell)
     55   return output
     56 
     57 
     58 def GetCmdStatusAndOutput(args, cwd=None, shell=False):
     59   """Executes a subprocess and returns its exit code and output.
     60 
     61   Args:
     62     args: A string or a sequence of program arguments. The program to execute is
     63       the string or the first item in the args sequence.
     64     cwd: If not None, the subprocess's current directory will be changed to
     65       |cwd| before it's executed.
     66     shell: Whether to execute args as a shell command.
     67 
     68   Returns:
     69     The tuple (exit code, output).
     70   """
     71   if isinstance(args, basestring):
     72     args_repr = args
     73     if not shell:
     74       raise Exception('string args must be run with shell=True')
     75   elif shell:
     76     raise Exception('array args must be run with shell=False')
     77   else:
     78     args_repr = ' '.join(map(pipes.quote, args))
     79 
     80   s = '[host]'
     81   if cwd:
     82     s += ':' + cwd
     83   s += '> ' + args_repr
     84   logging.info(s)
     85   tmpout = tempfile.TemporaryFile(bufsize=0)
     86   tmperr = tempfile.TemporaryFile(bufsize=0)
     87   exit_code = _Call(args, cwd=cwd, stdout=tmpout, stderr=tmperr, shell=shell)
     88   tmperr.seek(0)
     89   stderr = tmperr.read()
     90   tmperr.close()
     91   if stderr:
     92     logging.critical(stderr)
     93   tmpout.seek(0)
     94   stdout = tmpout.read()
     95   tmpout.close()
     96   if len(stdout) > 4096:
     97     logging.debug('Truncated output:')
     98   logging.debug(stdout[:4096])
     99   return (exit_code, stdout)
    100 
    101 
    102 class OutDirectory(object):
    103   _out_directory = os.path.join(constants.DIR_SOURCE_ROOT,
    104       os.environ.get('CHROMIUM_OUT_DIR','out'))
    105   @staticmethod
    106   def set(out_directory):
    107     OutDirectory._out_directory = out_directory
    108   @staticmethod
    109   def get():
    110     return OutDirectory._out_directory
    111