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