| OLD | NEW |
| (Empty) |
| 1 # 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 | |
| 7 import os | |
| 8 import re | |
| 9 import subprocess | |
| 10 import sys | |
| 11 | |
| 12 _STDOUT_IS_TTY = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty() | |
| 13 | |
| 14 | |
| 15 class CommandResult(object): | |
| 16 """An object to store various attributes of a child process.""" | |
| 17 | |
| 18 def __init__(self): | |
| 19 self.cmd = None | |
| 20 self.error = None | |
| 21 self.output = None | |
| 22 self.returncode = None | |
| 23 | |
| 24 | |
| 25 class RunCommandError(Exception): | |
| 26 """Error caught in RunCommand() method.""" | |
| 27 pass | |
| 28 | |
| 29 | |
| 30 def RunCommand(cmd, print_cmd=True, error_ok=False, error_message=None, | |
| 31 exit_code=False, redirect_stdout=False, redirect_stderr=False, | |
| 32 cwd=None, input=None, enter_chroot=False, shell=False): | |
| 33 """Runs a command. | |
| 34 | |
| 35 Args: | |
| 36 cmd: cmd to run. Should be input to subprocess.Popen. | |
| 37 print_cmd: prints the command before running it. | |
| 38 error_ok: does not raise an exception on error. | |
| 39 error_message: prints out this message when an error occurrs. | |
| 40 exit_code: returns the return code of the shell command. | |
| 41 redirect_stdout: returns the stdout. | |
| 42 redirect_stderr: holds stderr output until input is communicated. | |
| 43 cwd: the working directory to run this cmd. | |
| 44 input: input to pipe into this command through stdin. | |
| 45 enter_chroot: this command should be run from within the chroot. If set, | |
| 46 cwd must point to the scripts directory. | |
| 47 shell: If shell is True, the specified command will be executed through | |
| 48 the shell. | |
| 49 | |
| 50 Returns: | |
| 51 A CommandResult object. | |
| 52 | |
| 53 Raises: | |
| 54 Exception: Raises generic exception on error with optional error_message. | |
| 55 """ | |
| 56 # Set default for variables. | |
| 57 stdout = None | |
| 58 stderr = None | |
| 59 stdin = None | |
| 60 cmd_result = CommandResult() | |
| 61 | |
| 62 # Modify defaults based on parameters. | |
| 63 if redirect_stdout: stdout = subprocess.PIPE | |
| 64 if redirect_stderr: stderr = subprocess.PIPE | |
| 65 # TODO(sosa): gpylint complains about redefining built-in 'input'. | |
| 66 # Can we rename this variable? | |
| 67 if input: stdin = subprocess.PIPE | |
| 68 if isinstance(cmd, basestring): | |
| 69 if enter_chroot: cmd = './enter_chroot.sh -- ' + cmd | |
| 70 cmd_str = cmd | |
| 71 else: | |
| 72 if enter_chroot: cmd = ['./enter_chroot.sh', '--'] + cmd | |
| 73 cmd_str = ' '.join(cmd) | |
| 74 | |
| 75 # Print out the command before running. | |
| 76 if print_cmd: | |
| 77 Info('RunCommand: %s' % cmd_str) | |
| 78 cmd_result.cmd = cmd_str | |
| 79 | |
| 80 try: | |
| 81 proc = subprocess.Popen(cmd, cwd=cwd, stdin=stdin, stdout=stdout, | |
| 82 stderr=stderr, shell=shell) | |
| 83 (cmd_result.output, cmd_result.error) = proc.communicate(input) | |
| 84 if exit_code: | |
| 85 cmd_result.returncode = proc.returncode | |
| 86 | |
| 87 if not error_ok and proc.returncode: | |
| 88 msg = ('Command "%s" failed.\n' % cmd_str + | |
| 89 (error_message or cmd_result.error or cmd_result.output or '')) | |
| 90 raise RunCommandError(msg) | |
| 91 # TODO(sosa): is it possible not to use the catch-all Exception here? | |
| 92 except Exception, e: | |
| 93 if not error_ok: | |
| 94 raise | |
| 95 else: | |
| 96 Warning(str(e)) | |
| 97 | |
| 98 return cmd_result | |
| 99 | |
| 100 | |
| 101 class Color(object): | |
| 102 """Conditionally wraps text in ANSI color escape sequences.""" | |
| 103 BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) | |
| 104 BOLD = -1 | |
| 105 COLOR_START = '\033[1;%dm' | |
| 106 BOLD_START = '\033[1m' | |
| 107 RESET = '\033[0m' | |
| 108 | |
| 109 def __init__(self, enabled=True): | |
| 110 self._enabled = enabled | |
| 111 | |
| 112 def Color(self, color, text): | |
| 113 """Returns text with conditionally added color escape sequences. | |
| 114 | |
| 115 Args: | |
| 116 color: Text color -- one of the color constants defined in this class. | |
| 117 text: The text to color. | |
| 118 | |
| 119 Returns: | |
| 120 If self._enabled is False, returns the original text. If it's True, | |
| 121 returns text with color escape sequences based on the value of color. | |
| 122 """ | |
| 123 if not self._enabled: | |
| 124 return text | |
| 125 if color == self.BOLD: | |
| 126 start = self.BOLD_START | |
| 127 else: | |
| 128 start = self.COLOR_START % (color + 30) | |
| 129 return start + text + self.RESET | |
| 130 | |
| 131 | |
| 132 def Die(message): | |
| 133 """Emits a red error message and halts execution. | |
| 134 | |
| 135 Args: | |
| 136 message: The message to be emitted before exiting. | |
| 137 """ | |
| 138 print >> sys.stderr, ( | |
| 139 Color(_STDOUT_IS_TTY).Color(Color.RED, '\nERROR: ' + message)) | |
| 140 sys.exit(1) | |
| 141 | |
| 142 | |
| 143 # pylint: disable-msg=W0622 | |
| 144 def Warning(message): | |
| 145 """Emits a yellow warning message and continues execution. | |
| 146 | |
| 147 Args: | |
| 148 message: The message to be emitted. | |
| 149 """ | |
| 150 print >> sys.stderr, ( | |
| 151 Color(_STDOUT_IS_TTY).Color(Color.YELLOW, '\nWARNING: ' + message)) | |
| 152 | |
| 153 | |
| 154 def Info(message): | |
| 155 """Emits a blue informational message and continues execution. | |
| 156 | |
| 157 Args: | |
| 158 message: The message to be emitted. | |
| 159 """ | |
| 160 print >> sys.stderr, ( | |
| 161 Color(_STDOUT_IS_TTY).Color(Color.BLUE, '\nINFO: ' + message)) | |
| 162 | |
| 163 | |
| 164 def ListFiles(base_dir): | |
| 165 """Recurively list files in a directory. | |
| 166 | |
| 167 Args: | |
| 168 base_dir: directory to start recursively listing in. | |
| 169 | |
| 170 Returns: | |
| 171 A list of files relative to the base_dir path or | |
| 172 An empty list of there are no files in the directories. | |
| 173 """ | |
| 174 directories = [base_dir] | |
| 175 files_list = [] | |
| 176 while directories: | |
| 177 directory = directories.pop() | |
| 178 for name in os.listdir(directory): | |
| 179 fullpath = os.path.join(directory, name) | |
| 180 if os.path.isfile(fullpath): | |
| 181 files_list.append(fullpath) | |
| 182 elif os.path.isdir(fullpath): | |
| 183 directories.append(fullpath) | |
| 184 | |
| 185 return files_list | |
| 186 | |
| 187 | |
| 188 def IsInsideChroot(): | |
| 189 """Returns True if we are inside chroot.""" | |
| 190 return os.path.exists('/etc/debian_chroot') | |
| 191 | |
| 192 | |
| 193 def GetSrcRoot(): | |
| 194 """Get absolute path to src/scripts/ directory. | |
| 195 | |
| 196 Assuming test script will always be run from descendent of src/scripts. | |
| 197 | |
| 198 Returns: | |
| 199 A string, absolute path to src/scripts directory. None if not found. | |
| 200 """ | |
| 201 src_root = None | |
| 202 match_str = '/src/scripts/' | |
| 203 test_script_path = os.path.abspath('.') | |
| 204 | |
| 205 path_list = re.split(match_str, test_script_path) | |
| 206 if path_list: | |
| 207 src_root = os.path.join(path_list[0], match_str.strip('/')) | |
| 208 Info ('src_root = %r' % src_root) | |
| 209 else: | |
| 210 Info ('No %r found in %r' % (match_str, test_script_path)) | |
| 211 | |
| 212 return src_root | |
| 213 | |
| 214 | |
| 215 def GetChromeosVersion(str_obj): | |
| 216 """Helper method to parse output for CHROMEOS_VERSION_STRING. | |
| 217 | |
| 218 Args: | |
| 219 str_obj: a string, which may contain Chrome OS version info. | |
| 220 | |
| 221 Returns: | |
| 222 A string, value of CHROMEOS_VERSION_STRING environment variable set by | |
| 223 chromeos_version.sh. Or None if not found. | |
| 224 """ | |
| 225 if str_obj is not None: | |
| 226 match = re.search('CHROMEOS_VERSION_STRING=([0-9_.]+)', str_obj) | |
| 227 if match and match.group(1): | |
| 228 Info ('CHROMEOS_VERSION_STRING = %s' % match.group(1)) | |
| 229 return match.group(1) | |
| 230 | |
| 231 Info ('CHROMEOS_VERSION_STRING NOT found') | |
| 232 return None | |
| 233 | |
| 234 | |
| 235 def GetOutputImageDir(board, cros_version): | |
| 236 """Construct absolute path to output image directory. | |
| 237 | |
| 238 Args: | |
| 239 board: a string. | |
| 240 cros_version: a string, Chrome OS version. | |
| 241 | |
| 242 Returns: | |
| 243 a string: absolute path to output directory. | |
| 244 """ | |
| 245 src_root = GetSrcRoot() | |
| 246 rel_path = 'build/images/%s' % board | |
| 247 # ASSUME: --build_attempt always sets to 1 | |
| 248 version_str = '-'.join([cros_version, 'a1']) | |
| 249 output_dir = os.path.join(os.path.dirname(src_root), rel_path, version_str) | |
| 250 Info ('output_dir = %s' % output_dir) | |
| 251 return output_dir | |
| OLD | NEW |