blob: 9f42c052ac08eff786e1eb59089829e08fa06dbc [file] [log] [blame]
David James8c846492011-01-25 17:07:29 -08001#!/usr/bin/python
David Jamesb619a782012-07-25 19:37:57 -07002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
David James8c846492011-01-25 17:07:29 -08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Brian Harringaf019fb2012-05-10 15:06:13 -07006"""This script is used to upload host prebuilts as well as board BINHOSTS.
David James8c846492011-01-25 17:07:29 -08007
David James015af872012-06-19 15:24:36 -07008Prebuilts are uploaded using gsutil to Google Storage. After these prebuilts
9are successfully uploaded, a file is updated with the proper BINHOST version.
David James8c846492011-01-25 17:07:29 -080010
11To read more about prebuilts/binhost binary packages please refer to:
David James015af872012-06-19 15:24:36 -070012http://goto/chromeos-prebuilts
David James8c846492011-01-25 17:07:29 -080013
14Example of uploading prebuilt amd64 host files to Google Storage:
David Jamesc5cbd472012-06-19 16:25:45 -070015upload_prebuilts -p /b/cbuild/build -s -u gs://chromeos-prebuilt
David James8c846492011-01-25 17:07:29 -080016
17Example of uploading x86-dogfood binhosts to Google Storage:
David Jamesc5cbd472012-06-19 16:25:45 -070018upload_prebuilts -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
David James8c846492011-01-25 17:07:29 -080019
David James8c846492011-01-25 17:07:29 -080020"""
21
Chris Sosa1dc96132012-05-11 15:40:50 -070022import datetime
23import multiprocessing
24import optparse
25import os
26import sys
Chris Sosa1dc96132012-05-11 15:40:50 -070027
Zdenek Behan33a34112012-09-10 21:07:51 +020028from chromite.buildbot import constants
David Jamese5867812012-10-19 12:02:20 -070029from chromite.buildbot import portage_utilities
David James6450a0a2012-12-04 07:59:53 -080030from chromite.lib import binpkg
Chris Sosa1dc96132012-05-11 15:40:50 -070031from chromite.lib import cros_build_lib
David James97d95872012-11-16 15:09:56 -080032from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070033from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070034from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080035from chromite.lib import parallel
Mike Frysinger8e727a32013-01-16 16:57:53 -050036from chromite.lib import toolchain
Chris Sosa1dc96132012-05-11 15:40:50 -070037
David James015af872012-06-19 15:24:36 -070038# How many times to retry uploads.
39_RETRIES = 10
40
41# Multiplier for how long to sleep (in seconds) between retries; will delay
42# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
43_SLEEP_TIME = 60
44
David James8c846492011-01-25 17:07:29 -080045_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080046_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070047_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080048_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080049_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080050_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
51_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080052# Private overlays to look at for builds to filter
53# relative to build path
54_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Scott Zawalskiab1bed32011-03-16 15:24:24 -070055_GOOGLESTORAGE_ACL_FILE = 'googlestorage_acl.xml'
Mike Frysinger5aba4dd2013-01-16 16:42:36 -050056_BINHOST_BASE_URL = gs.PUBLIC_BASE_HTTPS_URL + 'chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080057_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
58# Created in the event of new host targets becoming available
59_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
60 'make.conf.amd64-host')}
61_BINHOST_CONF_DIR = 'src/third_party/chromiumos-overlay/chromeos/binhost'
62
63
David James4058b0d2011-12-08 21:24:50 -080064class BuildTarget(object):
65 """A board/variant/profile tuple."""
66
67 def __init__(self, board_variant, profile=None):
68 self.board_variant = board_variant
69 self.board, _, self.variant = board_variant.partition('_')
70 self.profile = profile
71
72 def __str__(self):
73 if self.profile:
74 return '%s_%s' % (self.board_variant, self.profile)
75 else:
76 return self.board_variant
77
78 def __eq__(self, other):
79 return str(other) == str(self)
80
81 def __hash__(self):
82 return hash(str(self))
83
84
David James8c846492011-01-25 17:07:29 -080085def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
86 """Update the key in file with the value passed.
87 File format:
88 key="value"
89 Note quotes are added automatically
90
91 Args:
92 filename: Name of file to modify.
93 value: Value to write with the key.
94 key: The variable key to update. (Default: PORTAGE_BINHOST)
95 """
96 if os.path.exists(filename):
97 file_fh = open(filename)
98 else:
99 file_fh = open(filename, 'w+')
100 file_lines = []
101 found = False
102 keyval_str = '%(key)s=%(value)s'
103 for line in file_fh:
104 # Strip newlines from end of line. We already add newlines below.
105 line = line.rstrip("\n")
106
107 if len(line.split('=')) != 2:
108 # Skip any line that doesn't fit key=val.
109 file_lines.append(line)
110 continue
111
112 file_var, file_val = line.split('=')
113 if file_var == key:
114 found = True
David James20b2b6f2011-11-18 15:11:58 -0800115 print 'Updating %s=%s to %s="%s"' % (file_var, file_val, key, value)
David James8c846492011-01-25 17:07:29 -0800116 value = '"%s"' % value
117 file_lines.append(keyval_str % {'key': key, 'value': value})
118 else:
119 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
120
121 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700122 value = '"%s"' % value
David James8c846492011-01-25 17:07:29 -0800123 file_lines.append(keyval_str % {'key': key, 'value': value})
124
125 file_fh.close()
126 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700127 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David James8c846492011-01-25 17:07:29 -0800128
129
Mike Frysinger5b34d732013-01-17 15:11:58 -0500130def RevGitFile(filename, data, retries=5, dryrun=False):
David James8c846492011-01-25 17:07:29 -0800131 """Update and push the git file.
132
Mike Frysinger5b34d732013-01-17 15:11:58 -0500133 Args:
134 filename: file to modify that is in a git repo already
135 data: A dict of key/values to update in |filename|
136 retries: The number of times to retry before giving up, default: 5
David James8c846492011-01-25 17:07:29 -0800137 """
138 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700139 cwd = os.path.abspath(os.path.dirname(filename))
David James97d95872012-11-16 15:09:56 -0800140 commit = git.RunGit(cwd, ['rev-parse', 'HEAD']).output.rstrip()
Mike Frysinger5b34d732013-01-17 15:11:58 -0500141 description = '%s: updating %s' % (os.path.basename(filename),
142 ', '.join(data.keys()))
143 # UpdateLocalFile will print out the keys/values for us.
144 print 'Revving git file %s' % filename
David James66009462012-03-25 10:08:38 -0700145
David James8c846492011-01-25 17:07:29 -0800146 try:
David James97d95872012-11-16 15:09:56 -0800147 git.CreatePushBranch(prebuilt_branch, cwd)
Mike Frysinger5b34d732013-01-17 15:11:58 -0500148 for key, value in data.iteritems():
149 UpdateLocalFile(filename, value, key)
David James97d95872012-11-16 15:09:56 -0800150 git.RunGit(cwd, ['add', filename])
151 git.RunGit(cwd, ['commit', '-m', description])
152 git.PushWithRetry(prebuilt_branch, cwd, dryrun=dryrun, retries=retries)
David James8c846492011-01-25 17:07:29 -0800153 finally:
David James1b6e67a2011-05-19 21:32:38 -0700154 cros_build_lib.RunCommand(['git', 'checkout', commit], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800155
156
157def GetVersion():
158 """Get the version to put in LATEST and update the git version with."""
Mike Frysinger0b92cfe2012-12-13 02:13:45 -0500159 return datetime.datetime.now().strftime('%Y.%m.%d.%H%M%S')
David James8c846492011-01-25 17:07:29 -0800160
161
David James015af872012-06-19 15:24:36 -0700162def _GsUpload(local_file, remote_file, acl):
David James8c846492011-01-25 17:07:29 -0800163 """Upload to GS bucket.
164
165 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800166 args: a tuple of three arguments that contains local_file, remote_file, and
167 the acl used for uploading the file.
David James8c846492011-01-25 17:07:29 -0800168
169 Returns:
170 Return the arg tuple of two if the upload failed
171 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700172 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
173 'authenticated-read', 'bucket-owner-full-control',
174 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700175 if acl in CANNED_ACLS:
Brian Harring7904b482012-08-08 02:54:12 -0700176 cmd = [gs.GSUTIL_BIN, 'cp', '-a', acl, local_file, remote_file]
David James015af872012-06-19 15:24:36 -0700177 acl_cmd = None
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700178 else:
179 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700180 # and a googlestore_acl.xml is present. Otherwise, this script errors.
Brian Harring7904b482012-08-08 02:54:12 -0700181 cmd = [gs.GSUTIL_BIN, 'cp', '-a', 'private', local_file, remote_file]
182 acl_cmd = [gs.GSUTIL_BIN, 'setacl', acl, remote_file]
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700183
David Jamesc5cbd472012-06-19 16:25:45 -0700184 cros_build_lib.RunCommandWithRetries(_RETRIES, cmd, print_cmd=True,
185 sleep=_SLEEP_TIME,
186 redirect_stdout=True,
187 redirect_stderr=True)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700188 if acl_cmd:
189 # Apply the passed in ACL xml file to the uploaded object.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700190 cros_build_lib.RunCommandWithRetries(_RETRIES, acl_cmd, print_cmd=False,
David James015af872012-06-19 15:24:36 -0700191 sleep=_SLEEP_TIME)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700192
193
David Jamesfd0b0852011-02-23 11:15:36 -0800194def RemoteUpload(acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800195 """Upload to google storage.
196
197 Create a pool of process and call _GsUpload with the proper arguments.
198
199 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800200 acl: The canned acl used for uploading. acl can be one of: "public-read",
201 "public-read-write", "authenticated-read", "bucket-owner-read",
202 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800203 files: dictionary with keys to local files and values to remote path.
204 pool: integer of maximum proesses to have at the same time.
205
206 Returns:
207 Return a set of tuple arguments of the failed uploads
208 """
David James015af872012-06-19 15:24:36 -0700209 tasks = [[key, value, acl] for key, value in files.iteritems()]
David James6450a0a2012-12-04 07:59:53 -0800210 parallel.RunTasksInProcessPool(_GsUpload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800211
212
213def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
214 """Build a dictionary of local remote file key pairs to upload.
215
216 Args:
217 base_local_path: The base path to the files on the local hard drive.
218 remote_path: The base path to the remote paths.
219 pkgs: The packages to upload.
220
221 Returns:
222 Returns a dictionary of local_path/remote_path pairs
223 """
224 upload_files = {}
225 for pkg in pkgs:
226 suffix = pkg['CPV'] + '.tbz2'
227 local_path = os.path.join(base_local_path, suffix)
228 assert os.path.exists(local_path)
229 remote_path = '%s/%s' % (base_remote_path.rstrip('/'), suffix)
230 upload_files[local_path] = remote_path
231
232 return upload_files
233
David Jamese5867812012-10-19 12:02:20 -0700234def GetBoardOverlay(build_path, target):
235 """Get the path to the board variant.
David James8c846492011-01-25 17:07:29 -0800236 Args:
237 build_path: The path to the root of the build directory
David Jamese5867812012-10-19 12:02:20 -0700238 target: The target board as a BuildTarget object.
David James8c846492011-01-25 17:07:29 -0800239 Returns:
David Jamese5867812012-10-19 12:02:20 -0700240 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800241 """
David Jamese5867812012-10-19 12:02:20 -0700242 board = target.board_variant
243 overlays = portage_utilities.FindOverlays(constants.BOTH_OVERLAYS, board,
244 buildroot=build_path)
245 # We only care about the last entry.
246 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800247
248
249def DeterminePrebuiltConfFile(build_path, target):
250 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
251
252 Args:
253 build_path: The path to the root of the build directory
254 target: String representation of the board. This includes host and board
255 targets
256
257 Returns
258 A string path to a prebuilt.conf file to be updated.
259 """
David James4058b0d2011-12-08 21:24:50 -0800260 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800261 # We are host.
262 # Without more examples of hosts this is a kludge for now.
263 # TODO(Scottz): as new host targets come online expand this to
264 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800265 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800266 else:
267 # We are a board
David Jamese5867812012-10-19 12:02:20 -0700268 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800269 make_path = os.path.join(board, 'prebuilt.conf')
270
271 return make_path
272
273
274def UpdateBinhostConfFile(path, key, value):
275 """Update binhost config file file with key=value.
276
277 Args:
278 path: Filename to update.
279 key: Key to update.
280 value: New value for key.
281 """
282 cwd = os.path.dirname(os.path.abspath(path))
283 filename = os.path.basename(path)
Brian Harringaf019fb2012-05-10 15:06:13 -0700284 osutils.SafeMakedirs(cwd)
Brian Harring22edb442012-05-11 23:55:18 -0700285 osutils.WriteFile(path, '', mode='a')
David James8c846492011-01-25 17:07:29 -0800286 UpdateLocalFile(path, value, key)
Chris Sosac13bba52011-05-24 15:14:09 -0700287 cros_build_lib.RunCommand(['git', 'add', filename], cwd=cwd)
Mike Frysinger109cada2013-01-17 15:50:44 -0500288 description = '%s: updating %s' % (os.path.basename(filename), key)
Peter Mayo193f68f2011-04-19 19:08:21 -0400289 cros_build_lib.RunCommand(['git', 'commit', '-m', description], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800290
291
David Jamesce093af2011-02-23 15:21:58 -0800292def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800293 """Grab all of the packages files associated with a list of binhost_urls.
294
David James05bcb2b2011-02-09 09:25:47 -0800295 Args:
296 binhost_urls: The URLs for the directories containing the Packages files we
297 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800298
299 Returns:
300 A list of PackageIndex objects.
301 """
302 pkg_indexes = []
303 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700304 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800305 if pkg_index:
306 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800307 return pkg_indexes
308
309
David James05bcb2b2011-02-09 09:25:47 -0800310
David Jamesc0f158a2011-02-22 16:07:29 -0800311class PrebuiltUploader(object):
312 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800313
David James615e5b52011-06-03 11:10:15 -0700314 def __init__(self, upload_location, acl, binhost_base_url,
David James32b0b2f2011-07-13 20:56:50 -0700315 pkg_indexes, build_path, packages, skip_upload,
David James4058b0d2011-12-08 21:24:50 -0800316 binhost_conf_dir, debug, target, slave_targets):
David Jamesc0f158a2011-02-22 16:07:29 -0800317 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800318
David Jamesc0f158a2011-02-22 16:07:29 -0800319 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800320
David Jamesc0f158a2011-02-22 16:07:29 -0800321 Args:
322 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800323 acl: The canned acl used for uploading to Google Storage. acl can be one
324 of: "public-read", "public-read-write", "authenticated-read",
325 "bucket-owner-read", "bucket-owner-full-control", or "private". If
326 we are not uploading to Google Storage, this parameter is unused.
327 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800328 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
329 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700330 build_path: The path to the directory containing the chroot.
331 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700332 skip_upload: Don't actually upload the tarballs.
333 binhost_conf_dir: Directory where to store binhost.conf files.
334 debug: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800335 target: BuildTarget managed by this builder.
336 slave_targets: List of BuildTargets managed by slave builders.
David Jamesc0f158a2011-02-22 16:07:29 -0800337 """
338 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800339 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800340 self._binhost_base_url = binhost_base_url
341 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700342 self._build_path = build_path
343 self._packages = set(packages)
David James8ece7ee2011-06-29 16:02:30 -0700344 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700345 self._binhost_conf_dir = binhost_conf_dir
David James27fa7d12011-06-29 17:24:14 -0700346 self._debug = debug
David James4058b0d2011-12-08 21:24:50 -0800347 self._target = target
348 self._slave_targets = slave_targets
David James615e5b52011-06-03 11:10:15 -0700349
350 def _ShouldFilterPackage(self, pkg):
351 if not self._packages:
352 return False
353 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800354 sys.path.insert(0, pym_path)
David James615e5b52011-06-03 11:10:15 -0700355 import portage.versions
356 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
357 cp = '%s/%s' % (cat, pkgname)
358 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800359
David Jamesc0f158a2011-02-22 16:07:29 -0800360 def _UploadPrebuilt(self, package_path, url_suffix):
361 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800362
David Jamesc0f158a2011-02-22 16:07:29 -0800363 Args:
364 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800365 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesa3bba142011-05-26 21:24:20 -0700366
David Jamesc0f158a2011-02-22 16:07:29 -0800367 """
David James8c846492011-01-25 17:07:29 -0800368
David Jamesc0f158a2011-02-22 16:07:29 -0800369 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700370 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800371 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700372 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800373 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
David James05bcb2b2011-02-09 09:25:47 -0800374
David Jamesc0f158a2011-02-22 16:07:29 -0800375 # Write Packages file.
376 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800377
David Jamesc0f158a2011-02-22 16:07:29 -0800378 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700379 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800380
David James015af872012-06-19 15:24:36 -0700381 # Build list of files to upload.
382 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
383 remote_file = '%s/Packages' % remote_location.rstrip('/')
384 upload_files[tmp_packages_file.name] = remote_file
385
386 RemoteUpload(self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800387
Zdenek Behan62a57792012-08-31 15:09:08 +0200388 def _UploadBoardTarball(self, board_path, url_suffix, version, prepackaged):
David James8fa34ea2011-04-15 13:00:20 -0700389 """Upload a tarball of the board at the specified path to Google Storage.
390
391 Args:
392 board_path: The path to the board dir.
393 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200394 version: The version of the board.
Zdenek Behan62a57792012-08-31 15:09:08 +0200395 prepackaged: If given, a tarball that has been packaged outside of this
396 script and should be used.
David James8fa34ea2011-04-15 13:00:20 -0700397 """
398 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
399 assert remote_location.startswith('gs://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200400 boardname = os.path.basename(board_path.rstrip('/'))
401 # We do not upload non SDK board tarballs,
402 assert boardname == constants.CHROOT_BUILDER_BOARD
403 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200404
Zdenek Behan86c15e92012-10-13 00:55:47 +0200405 version_str = version[len('chroot-'):]
Mike Frysinger8e727a32013-01-16 16:57:53 -0500406 remote_tarfile = toolchain.GetSdkURL(
407 for_gsutil=True, suburl='cros-sdk-%s.tar.xz' % (version_str,))
Zdenek Behan86c15e92012-10-13 00:55:47 +0200408 # For SDK, also upload the manifest which is guaranteed to exist
409 # by the builderstage.
410 _GsUpload(prepackaged + '.Manifest', remote_tarfile + '.Manifest',
411 self._acl)
412 _GsUpload(prepackaged, remote_tarfile, self._acl)
413
414 # Finally, also update the pointer to the latest SDK on which polling
415 # scripts rely.
416 with osutils.TempDirContextManager() as tmpdir:
417 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
Mike Frysinger8e727a32013-01-16 16:57:53 -0500418 remote_pointerfile = toolchain.GetSdkURL(for_gsutil=True,
419 suburl='cros-sdk-latest.conf')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200420 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
421 _GsUpload(pointerfile, remote_pointerfile, self._acl)
Zdenek Behan33a34112012-09-10 21:07:51 +0200422
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700423 def _GetTargets(self):
424 """Retuns the list of targets to use."""
425 targets = self._slave_targets[:]
426 if self._target:
427 targets.append(self._target)
428
429 return targets
430
431 def SyncHostPrebuilts(self, version, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800432 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800433
David Jamesc0f158a2011-02-22 16:07:29 -0800434 This function will sync both the standard host packages, plus the host
435 packages associated with all targets that have been "setup" with the
436 current host's chroot. For instance, if this host has been used to build
437 x86-generic, it will sync the host packages associated with
438 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
439 it will also sync the host packages associated with
440 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800441
David Jamesc0f158a2011-02-22 16:07:29 -0800442 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800443 version: A unique string, intended to be included in the upload path,
444 which identifies the version number of the uploaded prebuilts.
445 key: The variable key to update in the git file.
446 git_sync: If set, update make.conf of target to reference the latest
447 prebuilt packages generated here.
448 sync_binhost_conf: If set, update binhost config file in
449 chromiumos-overlay for the host.
450 """
David Jamese2488642011-11-14 16:15:20 -0800451 # Slave boards are listed before the master board so that the master board
452 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
453 # over preflight host prebuilts from other builders.)
454 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700455 for target in self._GetTargets():
David James4058b0d2011-12-08 21:24:50 -0800456 url_suffix = _REL_HOST_PATH % {'version': version,
457 'host_arch': _HOST_ARCH,
458 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800459 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800460
David James4058b0d2011-12-08 21:24:50 -0800461 if self._target == target and not self._skip_upload and not self._debug:
David Jamese2488642011-11-14 16:15:20 -0800462 # Upload prebuilts.
463 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
464 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700465
David Jamese2488642011-11-14 16:15:20 -0800466 # Record URL where prebuilts were uploaded.
467 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
468 packages_url_suffix.rstrip('/')))
469
David James20b2b6f2011-11-18 15:11:58 -0800470 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700471 if git_sync:
472 git_file = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800473 _PREBUILT_MAKE_CONF[_HOST_ARCH])
Mike Frysinger5b34d732013-01-17 15:11:58 -0500474 RevGitFile(git_file, {key: binhost}, dryrun=self._debug)
David James8ece7ee2011-06-29 16:02:30 -0700475 if sync_binhost_conf:
David James32b0b2f2011-07-13 20:56:50 -0700476 binhost_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James4058b0d2011-12-08 21:24:50 -0800477 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800478 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800479
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700480 def SyncBoardPrebuilts(self, version, key, git_sync, sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200481 upload_board_tarball, prepackaged_board):
David Jamesc0f158a2011-02-22 16:07:29 -0800482 """Synchronize board prebuilt files.
483
484 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800485 version: A unique string, intended to be included in the upload path,
486 which identifies the version number of the uploaded prebuilts.
487 key: The variable key to update in the git file.
488 git_sync: If set, update make.conf of target to reference the latest
489 prebuilt packages generated here.
490 sync_binhost_conf: If set, update binhost config file in
491 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700492 upload_board_tarball: Include a tarball of the board in our upload.
Zdenek Behan62a57792012-08-31 15:09:08 +0200493 prepackaged_board: A tarball of the board built outside of this script.
David Jamesc0f158a2011-02-22 16:07:29 -0800494 """
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700495 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800496 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800497 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800498 package_path = os.path.join(board_path, 'packages')
David James4058b0d2011-12-08 21:24:50 -0800499 url_suffix = _REL_BOARD_PATH % {'target': target, 'version': version}
David Jamese2488642011-11-14 16:15:20 -0800500 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700501
David James4058b0d2011-12-08 21:24:50 -0800502 if self._target == target and not self._skip_upload and not self._debug:
David Jamese2488642011-11-14 16:15:20 -0800503 # Upload board tarballs in the background.
504 if upload_board_tarball:
505 tar_process = multiprocessing.Process(target=self._UploadBoardTarball,
506 args=(board_path, url_suffix,
Zdenek Behan62a57792012-08-31 15:09:08 +0200507 version,
508 prepackaged_board))
David Jamese2488642011-11-14 16:15:20 -0800509 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700510
David Jamese2488642011-11-14 16:15:20 -0800511 # Upload prebuilts.
512 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700513
David Jamese2488642011-11-14 16:15:20 -0800514 # Make sure we finished uploading the board tarballs.
515 if upload_board_tarball:
516 tar_process.join()
517 assert tar_process.exitcode == 0
518 # TODO(zbehan): This should be done cleaner.
Zdenek Behan33a34112012-09-10 21:07:51 +0200519 if target.board == constants.CHROOT_BUILDER_BOARD:
David Jamese2488642011-11-14 16:15:20 -0800520 sdk_conf = os.path.join(self._build_path, self._binhost_conf_dir,
Mike Frysinger5b34d732013-01-17 15:11:58 -0500521 'host/sdk_version.conf')
522 sdk_settings = {
523 'SDK_LATEST_VERSION': version[len('chroot-'):],
524 }
525 RevGitFile(sdk_conf, sdk_settings, dryrun=self._debug)
David Jamesc0f158a2011-02-22 16:07:29 -0800526
David Jamese2488642011-11-14 16:15:20 -0800527 # Record URL where prebuilts were uploaded.
528 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
529 packages_url_suffix.rstrip('/'))
530
531 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800532 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Mike Frysinger5b34d732013-01-17 15:11:58 -0500533 RevGitFile(git_file, {key: url_value}, dryrun=self._debug)
David Jamese2488642011-11-14 16:15:20 -0800534 if sync_binhost_conf:
535 binhost_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James4058b0d2011-12-08 21:24:50 -0800536 'target', '%s-%s.conf' % (target, key))
David Jamese2488642011-11-14 16:15:20 -0800537 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800538
539
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700540def Usage(parser, msg):
David James8c846492011-01-25 17:07:29 -0800541 """Display usage message and parser help then exit with 1."""
542 print >> sys.stderr, msg
543 parser.print_help()
544 sys.exit(1)
545
David James4058b0d2011-12-08 21:24:50 -0800546
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700547def _AddSlaveBoard(_option, _opt_str, value, parser):
548 """Callback that adds a slave board to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800549 parser.values.slave_targets.append(BuildTarget(value))
550
551
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700552def _AddSlaveProfile(_option, _opt_str, value, parser):
553 """Callback that adds a slave profile to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800554 if not parser.values.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700555 Usage(parser, 'Must specify --slave-board before --slave-profile')
David James4058b0d2011-12-08 21:24:50 -0800556 if parser.values.slave_targets[-1].profile is not None:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700557 Usage(parser, 'Cannot specify --slave-profile twice for same board')
David James4058b0d2011-12-08 21:24:50 -0800558 parser.values.slave_targets[-1].profile = value
559
560
David Jamesc0f158a2011-02-22 16:07:29 -0800561def ParseOptions():
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700562 """Returns options given by the user and the target specified.
563
564 Returns a tuple containing a parsed options object and BuildTarget.
565 target instance is None if no board is specified.
566 """
David James8c846492011-01-25 17:07:29 -0800567 parser = optparse.OptionParser()
568 parser.add_option('-H', '--binhost-base-url', dest='binhost_base_url',
569 default=_BINHOST_BASE_URL,
570 help='Base URL to use for binhost in make.conf updates')
571 parser.add_option('', '--previous-binhost-url', action='append',
572 default=[], dest='previous_binhost_url',
573 help='Previous binhost URL')
574 parser.add_option('-b', '--board', dest='board', default=None,
575 help='Board type that was built on this machine')
Zdenek Behan62a57792012-08-31 15:09:08 +0200576 parser.add_option('-B', '--prepackaged-tarball', dest='prepackaged_tarball',
577 default=None,
578 help='Board tarball prebuilt outside of this script.')
David James4058b0d2011-12-08 21:24:50 -0800579 parser.add_option('', '--profile', dest='profile', default=None,
580 help='Profile that was built on this machine')
581 parser.add_option('', '--slave-board', default=[], action='callback',
582 dest='slave_targets', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700583 callback=_AddSlaveBoard,
David James4058b0d2011-12-08 21:24:50 -0800584 help='Board type that was built on a slave machine. To '
585 'add a profile to this board, use --slave-profile.')
586 parser.add_option('', '--slave-profile', action='callback', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700587 callback=_AddSlaveProfile,
David James4058b0d2011-12-08 21:24:50 -0800588 help='Board profile that was built on a slave machine. '
589 'Applies to previous slave board.')
David James8c846492011-01-25 17:07:29 -0800590 parser.add_option('-p', '--build-path', dest='build_path',
David James05bcb2b2011-02-09 09:25:47 -0800591 help='Path to the directory containing the chroot')
David James615e5b52011-06-03 11:10:15 -0700592 parser.add_option('', '--packages', action='append',
593 default=[], dest='packages',
594 help='Only include the specified packages. '
595 '(Default is to include all packages.)')
David James8c846492011-01-25 17:07:29 -0800596 parser.add_option('-s', '--sync-host', dest='sync_host',
597 default=False, action='store_true',
598 help='Sync host prebuilts')
599 parser.add_option('-g', '--git-sync', dest='git_sync',
600 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800601 help='Enable git version sync (This commits to a repo.) '
602 'This is used by full builders to commit directly '
603 'to board overlays.')
David James8c846492011-01-25 17:07:29 -0800604 parser.add_option('-u', '--upload', dest='upload',
605 default=None,
606 help='Upload location')
607 parser.add_option('-V', '--prepend-version', dest='prepend_version',
608 default=None,
609 help='Add an identifier to the front of the version')
610 parser.add_option('-f', '--filters', dest='filters', action='store_true',
611 default=False,
612 help='Turn on filtering of private ebuild packages')
613 parser.add_option('-k', '--key', dest='key',
614 default='PORTAGE_BINHOST',
615 help='Key to update in make.conf / binhost.conf')
David James8ece7ee2011-06-29 16:02:30 -0700616 parser.add_option('', '--set-version', dest='set_version',
617 default=None,
618 help='Specify the version string')
David James8c846492011-01-25 17:07:29 -0800619 parser.add_option('', '--sync-binhost-conf', dest='sync_binhost_conf',
620 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800621 help='Update binhost.conf in chromiumos-overlay or '
622 'chromeos-overlay. Commit the changes, but don\'t '
623 'push them. This is used for preflight binhosts.')
David James32b0b2f2011-07-13 20:56:50 -0700624 parser.add_option('', '--binhost-conf-dir', dest='binhost_conf_dir',
625 default=_BINHOST_CONF_DIR,
626 help='Directory to commit binhost config with '
627 '--sync-binhost-conf.')
David Jamesfd0b0852011-02-23 11:15:36 -0800628 parser.add_option('-P', '--private', dest='private', action='store_true',
629 default=False, help='Mark gs:// uploads as private.')
David James8ece7ee2011-06-29 16:02:30 -0700630 parser.add_option('', '--skip-upload', dest='skip_upload',
631 action='store_true', default=False,
632 help='Skip upload step.')
David James8fa34ea2011-04-15 13:00:20 -0700633 parser.add_option('', '--upload-board-tarball', dest='upload_board_tarball',
634 action='store_true', default=False,
635 help='Upload board tarball to Google Storage.')
David James27fa7d12011-06-29 17:24:14 -0700636 parser.add_option('', '--debug', dest='debug',
637 action='store_true', default=False,
638 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800639
640 options, args = parser.parse_args()
David James8c846492011-01-25 17:07:29 -0800641 if not options.build_path:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700642 Usage(parser, 'Error: you need provide a chroot path')
David James8ece7ee2011-06-29 16:02:30 -0700643 if not options.upload and not options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700644 Usage(parser, 'Error: you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700645 if not options.set_version and options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700646 Usage(parser, 'Error: If you are using --skip-upload, you must specify a '
David James8ece7ee2011-06-29 16:02:30 -0700647 'version number using --set-version.')
David James9417f272011-05-26 13:24:47 -0700648 if args:
David Jamesc5cbd472012-06-19 16:25:45 -0700649 Usage(parser, 'Error: invalid arguments passed to upload_prebuilts: '
650 '%r' % args)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700651
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700652 target = None
653 if options.board:
654 target = BuildTarget(options.board, options.profile)
655
656 if target in options.slave_targets:
657 Usage(parser, 'Error: --board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800658
David James4058b0d2011-12-08 21:24:50 -0800659 if len(set(options.slave_targets)) != len(options.slave_targets):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700660 Usage(parser, 'Error: --slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800661
David James4058b0d2011-12-08 21:24:50 -0800662 if options.slave_targets and options.git_sync:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700663 Usage(parser, 'Error: --slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800664
David James8ece7ee2011-06-29 16:02:30 -0700665 if (options.upload_board_tarball and options.skip_upload and
666 options.board == 'amd64-host'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700667 Usage(parser, 'Error: --skip-upload is not compatible with '
David James8ece7ee2011-06-29 16:02:30 -0700668 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700669
David James8ece7ee2011-06-29 16:02:30 -0700670 if (options.upload_board_tarball and not options.skip_upload and
671 not options.upload.startswith('gs://')):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700672 Usage(parser, 'Error: --upload-board-tarball only works with gs:// URLs.\n'
David James8fa34ea2011-04-15 13:00:20 -0700673 '--upload must be a gs:// URL.')
674
Zdenek Behan86c15e92012-10-13 00:55:47 +0200675 if options.upload_board_tarball and options.prepackaged_tarball is None:
676 Usage(parser, 'Error: --upload-board-tarball requires '
677 '--prepackaged-tarball.')
678
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700679 if options.private:
680 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700681 Usage(parser, 'Error: --private and --sync-host/-s cannot be specified '
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700682 'together, we do not support private host prebuilts')
683
David James8ece7ee2011-06-29 16:02:30 -0700684 if not options.upload or not options.upload.startswith('gs://'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700685 Usage(parser, 'Error: --private is only valid for gs:// URLs.\n'
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700686 '--upload must be a gs:// URL.')
687
688 if options.binhost_base_url != _BINHOST_BASE_URL:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700689 Usage(parser, 'Error: when using --private the --binhost-base-url '
David Jamese2488642011-11-14 16:15:20 -0800690 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700691
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700692 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800693
David Jamesc5cbd472012-06-19 16:25:45 -0700694def main(_argv):
David Jamesdb401072011-06-10 12:17:16 -0700695 # Set umask to a sane value so that files created as root are readable.
696 os.umask(022)
697
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700698 options, target = ParseOptions()
David Jamesc0f158a2011-02-22 16:07:29 -0800699
David James05bcb2b2011-02-09 09:25:47 -0800700 # Calculate a list of Packages index files to compare against. Whenever we
701 # upload a package, we check to make sure it's not already stored in one of
702 # the packages files we uploaded. This list of packages files might contain
703 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800704 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800705
David James8ece7ee2011-06-29 16:02:30 -0700706 if options.set_version:
707 version = options.set_version
708 else:
709 version = GetVersion()
David Jamesc0f158a2011-02-22 16:07:29 -0800710 if options.prepend_version:
711 version = '%s-%s' % (options.prepend_version, version)
712
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700713 acl = 'public-read'
714 binhost_base_url = options.binhost_base_url
715
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700716 if target and options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700717 binhost_base_url = options.upload
David Jamese5867812012-10-19 12:02:20 -0700718 board_path = GetBoardOverlay(options.build_path, target)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700719 acl = os.path.join(board_path, _GOOGLESTORAGE_ACL_FILE)
720
721 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700722 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700723 options.packages, options.skip_upload,
David Jamese2488642011-11-14 16:15:20 -0800724 options.binhost_conf_dir, options.debug,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700725 target, options.slave_targets)
David Jamesc0f158a2011-02-22 16:07:29 -0800726
David James8c846492011-01-25 17:07:29 -0800727 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700728 uploader.SyncHostPrebuilts(version, options.key, options.git_sync,
729 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800730
Chris Sosa62c8ff52012-06-04 15:03:12 -0700731 if options.board or options.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700732 uploader.SyncBoardPrebuilts(version, options.key, options.git_sync,
733 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200734 options.upload_board_tarball,
735 options.prepackaged_tarball)