cmd_helper.py revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
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
7import os
8import logging
9import pipes
10import signal
11import subprocess
12import tempfile
13
14import constants
15
16
17def _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
24def 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
40def 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
58def 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
102class 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