blob: e382445087132d3ad312d4db672e9611f369dc1b [file] [log] [blame]
Scott Zawalski6bc41ac2010-09-08 12:47:28 -07001# Copyright (c) 2010 The Chromium OS 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"""Common python commands used by various build scripts."""
6
Scott Zawalski98ac6b22010-09-08 15:59:23 -07007import os
Scott Zawalski6bc41ac2010-09-08 12:47:28 -07008import subprocess
9import sys
10
11_STDOUT_IS_TTY = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
12
Tan Gao2f310882010-09-10 14:50:47 -070013
14class CommandResult(object):
15 """An object to store various attributes of a child process."""
16
17 def __init__(self):
18 self.cmd = None
19 self.error = None
20 self.output = None
21 self.returncode = None
22
23
24class RunCommandError(Exception):
25 """Error caught in RunCommand() method."""
26 pass
27
28
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070029def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None,
30 exit_code=False, redirect_stdout=False, redirect_stderr=False,
David James6db8f522010-09-09 10:49:11 -070031 cwd=None, input=None, enter_chroot=False, shell=False):
32 """Runs a command.
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070033
34 Keyword arguments:
David James6db8f522010-09-09 10:49:11 -070035 cmd - cmd to run. Should be input to subprocess.Popen.
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070036 print_cmd -- prints the command before running it.
37 error_ok -- does not raise an exception on error.
38 error_message -- prints out this message when an error occurrs.
39 exit_code -- returns the return code of the shell command.
40 redirect_stdout -- returns the stdout.
41 redirect_stderr -- holds stderr output until input is communicated.
42 cwd -- the working directory to run this cmd.
43 input -- input to pipe into this command through stdin.
44 enter_chroot -- this command should be run from within the chroot. If set,
45 cwd must point to the scripts directory.
David James6db8f522010-09-09 10:49:11 -070046 shell -- If shell is True, the specified command will be executed through the shell.
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070047 Raises:
48 Exception: Raises generic exception on error with optional error_message.
49 """
50 # Set default for variables.
51 stdout = None
52 stderr = None
53 stdin = None
54 output = ''
Tan Gao2f310882010-09-10 14:50:47 -070055 cmd_result = CommandResult()
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070056
57 # Modify defaults based on parameters.
58 if redirect_stdout: stdout = subprocess.PIPE
59 if redirect_stderr: stderr = subprocess.PIPE
60 if input: stdin = subprocess.PIPE
David James6db8f522010-09-09 10:49:11 -070061 if isinstance(cmd, basestring):
62 if enter_chroot: cmd = './enter_chroot.sh -- ' + cmd
63 cmd_str = cmd
64 else:
65 if enter_chroot: cmd = ['./enter_chroot.sh', '--'] + cmd
66 cmd_str = ' '.join(cmd)
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070067
68 # Print out the command before running.
69 if print_cmd:
David James6db8f522010-09-09 10:49:11 -070070 Info('RunCommand: %s' % cmd_str)
Tan Gao2f310882010-09-10 14:50:47 -070071 cmd_result.cmd = cmd_str
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070072
73 try:
Tan Gao2f310882010-09-10 14:50:47 -070074 proc = subprocess.Popen(cmd_str, cwd=cwd, stdin=stdin,
David James6db8f522010-09-09 10:49:11 -070075 stdout=stdout, stderr=stderr,
76 shell=shell)
Tan Gao2f310882010-09-10 14:50:47 -070077 (cmd_result.output, cmd_result.error) = proc.communicate(input)
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070078 if exit_code:
Tan Gao2f310882010-09-10 14:50:47 -070079 cmd_result.returncode = proc.returncode
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070080
81 if not error_ok and proc.returncode:
Tan Gao2f310882010-09-10 14:50:47 -070082 msg = ('Command "%s" failed.\n' % cmd_str +
83 (error_message or cmd_result.error or cmd_result.output or ''))
84 raise RunCommandError(msg)
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070085 except Exception,e:
86 if not error_ok:
87 raise
88 else:
89 Warning(str(e))
90
Tan Gao2f310882010-09-10 14:50:47 -070091 return cmd_result
Scott Zawalski6bc41ac2010-09-08 12:47:28 -070092
93
94class Color(object):
95 """Conditionally wraps text in ANSI color escape sequences."""
96 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8)
97 BOLD = -1
98 COLOR_START = '\033[1;%dm'
99 BOLD_START = '\033[1m'
100 RESET = '\033[0m'
101
102 def __init__(self, enabled=True):
103 self._enabled = enabled
104
105 def Color(self, color, text):
106 """Returns text with conditionally added color escape sequences.
107
108 Keyword arguments:
109 color: Text color -- one of the color constants defined in this class.
110 text: The text to color.
111
112 Returns:
113 If self._enabled is False, returns the original text. If it's True,
114 returns text with color escape sequences based on the value of color.
115 """
116 if not self._enabled:
117 return text
118 if color == self.BOLD:
119 start = self.BOLD_START
120 else:
121 start = self.COLOR_START % (color + 30)
122 return start + text + self.RESET
123
124
125def Die(message):
126 """Emits a red error message and halts execution.
127
128 Keyword arguments:
129 message: The message to be emitted before exiting.
130 """
131 print >> sys.stderr, (
132 Color(_STDOUT_IS_TTY).Color(Color.RED, '\nERROR: ' + message))
133 sys.exit(1)
134
135
136def Warning(message):
137 """Emits a yellow warning message and continues execution.
138
139 Keyword arguments:
140 message: The message to be emitted.
141 """
142 print >> sys.stderr, (
143 Color(_STDOUT_IS_TTY).Color(Color.YELLOW, '\nWARNING: ' + message))
144
145
146def Info(message):
147 """Emits a blue informational message and continues execution.
148
149 Keyword arguments:
150 message: The message to be emitted.
151 """
152 print >> sys.stderr, (
153 Color(_STDOUT_IS_TTY).Color(Color.BLUE, '\nINFO: ' + message))
Scott Zawalski98ac6b22010-09-08 15:59:23 -0700154
155
156def ListFiles(base_dir):
157 """Recurively list files in a directory.
158
159 Keyword arguments:
160 base_dir: directory to start recursively listing in.
161
162 Returns:
163 A list of files relative to the base_dir path or
164 An empty list of there are no files in the directories.
165 """
166 directories = [base_dir]
167 files_list = []
168 while directories:
169 directory = directories.pop()
170 for name in os.listdir(directory):
171 fullpath = os.path.join(directory, name)
172 if os.path.isfile(fullpath):
173 files_list.append(fullpath)
174 elif os.path.isdir(fullpath):
175 directories.append(fullpath)
176
177 return files_list