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"""Generic utilities for all python scripts.""" 6 7import atexit 8import httplib 9import os 10import signal 11import stat 12import subprocess 13import sys 14import tempfile 15import urlparse 16import zipfile 17 18 19def GetPlatformName(): 20 """Return a string to be used in paths for the platform.""" 21 if IsWindows(): 22 return 'win' 23 if IsMac(): 24 return 'mac' 25 if IsLinux(): 26 return 'linux' 27 raise NotImplementedError('Unknown platform "%s".' % sys.platform) 28 29 30def IsWindows(): 31 return sys.platform == 'cygwin' or sys.platform.startswith('win') 32 33 34def IsLinux(): 35 return sys.platform.startswith('linux') 36 37 38def IsMac(): 39 return sys.platform.startswith('darwin') 40 41 42def GetAbsolutePathOfUserPath(user_path): 43 """Expand the given |user_path| (like "~/file") and return its absolute path. 44 """ 45 if user_path is None: 46 return None 47 return os.path.abspath(os.path.expanduser(user_path)) 48 49 50def _DeleteDir(path): 51 """Deletes a directory recursively, which must exist.""" 52 # Don't use shutil.rmtree because it can't delete read-only files on Win. 53 for root, dirs, files in os.walk(path, topdown=False): 54 for name in files: 55 filename = os.path.join(root, name) 56 os.chmod(filename, stat.S_IWRITE) 57 os.remove(filename) 58 for name in dirs: 59 os.rmdir(os.path.join(root, name)) 60 os.rmdir(path) 61 62 63def Delete(path): 64 """Deletes the given file or directory (recursively), which must exist.""" 65 if os.path.isdir(path): 66 _DeleteDir(path) 67 else: 68 os.remove(path) 69 70 71def MaybeDelete(path): 72 """Deletes the given file or directory (recurisvely), if it exists.""" 73 if os.path.exists(path): 74 Delete(path) 75 76 77def MakeTempDir(parent_dir=None): 78 """Creates a temporary directory and returns an absolute path to it. 79 80 The temporary directory is automatically deleted when the python interpreter 81 exits normally. 82 83 Args: 84 parent_dir: the directory to create the temp dir in. If None, the system 85 temp dir is used. 86 87 Returns: 88 The absolute path to the temporary directory. 89 """ 90 path = tempfile.mkdtemp(dir=parent_dir) 91 atexit.register(MaybeDelete, path) 92 return path 93 94 95def Zip(path): 96 """Zips the given path and returns the zipped file.""" 97 zip_path = os.path.join(MakeTempDir(), 'build.zip') 98 f = zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) 99 f.write(path, os.path.basename(path)) 100 f.close() 101 return zip_path 102 103 104def Unzip(zip_path, output_dir): 105 """Unzips the given zip file using a system installed unzip tool. 106 107 Args: 108 zip_path: zip file to unzip. 109 output_dir: directory to unzip the contents of the zip file. The directory 110 must exist. 111 112 Raises: 113 RuntimeError if the unzip operation fails. 114 """ 115 if IsWindows(): 116 unzip_cmd = ['C:\\Program Files\\7-Zip\\7z.exe', 'x', '-y'] 117 else: 118 unzip_cmd = ['unzip', '-o'] 119 unzip_cmd += [zip_path] 120 if RunCommand(unzip_cmd, output_dir) != 0: 121 raise RuntimeError('Unable to unzip %s to %s' % (zip_path, output_dir)) 122 123 124def Kill(pid): 125 """Terminate the given pid.""" 126 if IsWindows(): 127 subprocess.call(['taskkill.exe', '/T', '/F', '/PID', str(pid)]) 128 else: 129 os.kill(pid, signal.SIGTERM) 130 131 132def RunCommand(cmd, cwd=None): 133 """Runs the given command and returns the exit code. 134 135 Args: 136 cmd: list of command arguments. 137 cwd: working directory to execute the command, or None if the current 138 working directory should be used. 139 140 Returns: 141 The exit code of the command. 142 """ 143 sys.stdout.flush() 144 process = subprocess.Popen(cmd, cwd=cwd) 145 process.wait() 146 sys.stdout.flush() 147 return process.returncode 148 149 150def DoesUrlExist(url): 151 """Determines whether a resource exists at the given URL. 152 153 Args: 154 url: URL to be verified. 155 156 Returns: 157 True if url exists, otherwise False. 158 """ 159 parsed = urlparse.urlparse(url) 160 try: 161 conn = httplib.HTTPConnection(parsed.netloc) 162 conn.request('HEAD', parsed.path) 163 response = conn.getresponse() 164 except (socket.gaierror, socket.error): 165 return False 166 finally: 167 conn.close() 168 # Follow both permanent (301) and temporary (302) redirects. 169 if response.status == 302 or response.status == 301: 170 return DoesUrlExist(response.getheader('location')) 171 return response.status == 200 172 173 174def MarkBuildStepStart(name): 175 print '@@@BUILD_STEP %s@@@' % name 176 sys.stdout.flush() 177 178 179def MarkBuildStepError(): 180 print '@@@STEP_FAILURE@@@' 181 sys.stdout.flush() 182 183 184def AddBuildStepText(text): 185 print '@@@STEP_TEXT@%s@@@' % text 186 sys.stdout.flush() 187 188 189def PrintAndFlush(text): 190 print text 191 sys.stdout.flush() 192 193 194def AddLink(label, url): 195 """Adds a link with name |label| linking to |url| to current buildbot step. 196 197 Args: 198 label: A string with the name of the label. 199 url: A string of the URL. 200 """ 201 print '@@@STEP_LINK@%s@%s@@@' % (label, url) 202