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