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 """Common utilities for all buildbot scripts that specifically don't rely 6 on having a full chromium checkout. 7 """ 8 9 import os 10 import subprocess 11 import sys 12 13 from build_paths import SDK_SRC_DIR, NACL_DIR 14 15 sys.path.append(os.path.join(SDK_SRC_DIR, 'tools')) 16 import oshelpers 17 import getos 18 19 def IsSDKBuilder(): 20 """Returns True if this script is running on an SDK builder. 21 22 False means it is either running on a trybot, or a user's machine. 23 24 Trybot names: 25 (win|mac|linux)_nacl_sdk 26 27 Builder names: 28 (windows|mac|linux)-sdk-multi(rel)?""" 29 return '-sdk-multi' in os.getenv('BUILDBOT_BUILDERNAME', '') 30 31 32 def IsSDKTrybot(): 33 """Returns True if this script is running on an SDK trybot. 34 35 False means it is either running on an SDK builder, or a user's machine. 36 37 See IsSDKBuilder above for trybot/buildbot names.""" 38 return '_nacl_sdk' in os.getenv('BUILDBOT_BUILDERNAME', '') 39 40 41 def ErrorExit(msg): 42 """Write and error to stderr, then exit with 1 signaling failure.""" 43 sys.stderr.write(str(msg) + '\n') 44 sys.exit(1) 45 46 47 def GetWindowsEnvironment(): 48 sys.path.append(os.path.join(NACL_DIR, 'buildbot')) 49 import buildbot_standard 50 51 # buildbot_standard.SetupWindowsEnvironment expects a "context" object. We'll 52 # fake enough of that here to work. 53 class FakeContext(object): 54 def __init__(self): 55 self.env = os.environ 56 57 def GetEnv(self, key): 58 return self.env[key] 59 60 def __getitem__(self, key): 61 return self.env[key] 62 63 def SetEnv(self, key, value): 64 self.env[key] = value 65 66 def __setitem__(self, key, value): 67 self.env[key] = value 68 69 context = FakeContext() 70 buildbot_standard.SetupWindowsEnvironment(context) 71 72 # buildbot_standard.SetupWindowsEnvironment adds the directory which contains 73 # vcvarsall.bat to the path, but not the directory which contains cl.exe, 74 # link.exe, etc. 75 # Running vcvarsall.bat adds the correct directories to the path, which we 76 # extract below. 77 process = subprocess.Popen('vcvarsall.bat x86 > NUL && set', 78 stdout=subprocess.PIPE, env=context.env, shell=True) 79 stdout, _ = process.communicate() 80 81 # Parse environment from "set" command above. 82 # It looks like this: 83 # KEY1=VALUE1\r\n 84 # KEY2=VALUE2\r\n 85 # ... 86 return dict(line.split('=') for line in stdout.split('\r\n')[:-1]) 87 88 89 def BuildStep(name): 90 """Annotate a buildbot build step.""" 91 sys.stdout.flush() 92 print '\n@@@BUILD_STEP %s@@@' % name 93 sys.stdout.flush() 94 95 96 def Run(args, cwd=None, env=None, shell=False): 97 """Start a process with the provided arguments. 98 99 Starts a process in the provided directory given the provided arguments. If 100 shell is not False, the process is launched via the shell to provide shell 101 interpretation of the arguments. Shell behavior can differ between platforms 102 so this should be avoided when not using platform dependent shell scripts.""" 103 104 # We need to modify the environment to build host on Windows. 105 if not env and getos.GetPlatform() == 'win': 106 env = GetWindowsEnvironment() 107 108 print 'Running: ' + ' '.join(args) 109 sys.stdout.flush() 110 sys.stderr.flush() 111 try: 112 subprocess.check_call(args, cwd=cwd, env=env, shell=shell) 113 except subprocess.CalledProcessError as e: 114 sys.stdout.flush() 115 sys.stderr.flush() 116 ErrorExit('buildbot_common: %s' % e) 117 118 sys.stdout.flush() 119 sys.stderr.flush() 120 121 122 def CopyDir(src, dst, excludes=('.svn', '*/.svn')): 123 """Recursively copy a directory using.""" 124 args = ['-r', src, dst] 125 for exc in excludes: 126 args.append('--exclude=' + exc) 127 print 'cp -r %s %s' % (src, dst) 128 if os.path.abspath(src) == os.path.abspath(dst): 129 ErrorExit('ERROR: Copying directory onto itself: ' + src) 130 oshelpers.Copy(args) 131 132 133 def CopyFile(src, dst): 134 print 'cp %s %s' % (src, dst) 135 if os.path.abspath(src) == os.path.abspath(dst): 136 ErrorExit('ERROR: Copying file onto itself: ' + src) 137 args = [src, dst] 138 oshelpers.Copy(args) 139 140 141 def RemoveDir(dst): 142 """Remove the provided path.""" 143 print 'rm -fr ' + dst 144 oshelpers.Remove(['-fr', dst]) 145 146 147 def MakeDir(dst): 148 """Create the path including all parent directories as needed.""" 149 print 'mkdir -p ' + dst 150 oshelpers.Mkdir(['-p', dst]) 151 152 153 def Move(src, dst): 154 """Move the path src to dst.""" 155 print 'mv -f %s %s' % (src, dst) 156 oshelpers.Move(['-f', src, dst]) 157 158 159 def RemoveFile(dst): 160 """Remove the provided file.""" 161 print 'rm ' + dst 162 oshelpers.Remove(['-f', dst]) 163 164 165 BOT_GSUTIL = '/b/build/scripts/slave/gsutil' 166 # On Windows, the current working directory may be on a different drive than 167 # gsutil. 168 WIN_BOT_GSUTIL = 'E:' + BOT_GSUTIL 169 LOCAL_GSUTIL = 'gsutil' 170 171 172 def GetGsutil(): 173 if os.environ.get('BUILDBOT_BUILDERNAME') \ 174 and not os.environ.get('BUILDBOT_FAKE'): 175 if getos.GetPlatform() == 'win': 176 return WIN_BOT_GSUTIL 177 return BOT_GSUTIL 178 else: 179 return LOCAL_GSUTIL 180 181 182 def Archive(filename, bucket_path, cwd=None, step_link=True): 183 """Upload the given filename to Google Store.""" 184 full_dst = 'gs://%s/%s' % (bucket_path, filename) 185 186 # Since GetGsutil() might just return 'gsutil' and expect it to be looked 187 # up in the PATH, we must pass shell=True on windows. 188 # Without shell=True the windows implementation of subprocess.call will not 189 # search the PATH for the executable: http://bugs.python.org/issue8557 190 shell = getos.GetPlatform() == 'win' 191 192 cmd = [GetGsutil(), 'cp', '-a', 'public-read', filename, full_dst] 193 Run(cmd, shell=shell, cwd=cwd) 194 url = 'https://commondatastorage.googleapis.com/'\ 195 '%s/%s' % (bucket_path, filename) 196 if step_link: 197 print '@@@STEP_LINK@download@%s@@@' % url 198 sys.stdout.flush() 199