blob: 70e34480aa18472520b746dcc336e23407e0eabb [file] [log] [blame]
David James8c846492011-01-25 17:07:29 -08001#!/usr/bin/python
Chris Sosac13bba52011-05-24 15:14:09 -07002# Copyright (c) 2011 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:
15./prebuilt.py -p /b/cbuild/build -s -u gs://chromeos-prebuilt
16
17Example of uploading x86-dogfood binhosts to Google Storage:
David James015af872012-06-19 15:24:36 -070018./prebuilt.py -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
27import tempfile
28
29if __name__ == '__main__':
30 import constants
31 sys.path.insert(0, constants.SOURCE_ROOT)
32
David James015af872012-06-19 15:24:36 -070033from chromite.buildbot import cbuildbot_background as bg
Chris Sosa1dc96132012-05-11 15:40:50 -070034from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070035from chromite.lib import osutils
David James32faafe2012-06-08 14:25:03 -070036from chromite.lib import binpkg
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'
David Jamesce619292011-11-08 11:42:36 -080056_BINHOST_BASE_URL = 'https://commondatastorage.googleapis.com/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
David James27fa7d12011-06-29 17:24:14 -0700130def RevGitFile(filename, value, retries=5, key='PORTAGE_BINHOST', dryrun=False):
David James8c846492011-01-25 17:07:29 -0800131 """Update and push the git file.
132
133 Args:
134 filename: file to modify that is in a git repo already
135 value: string representing the version of the prebuilt that has been
136 uploaded.
137 retries: The number of times to retry before giving up, default: 5
138 key: The variable key to update in the git file.
139 (Default: PORTAGE_BINHOST)
140 """
141 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700142 cwd = os.path.abspath(os.path.dirname(filename))
Brian Harring609dc4e2012-05-07 02:17:44 -0700143 commit = cros_build_lib.RunGitCommand(
144 cwd, ['rev-parse', 'HEAD']).output.rstrip()
David James8c846492011-01-25 17:07:29 -0800145 description = 'Update %s="%s" in %s' % (key, value, filename)
146 print description
David James66009462012-03-25 10:08:38 -0700147
David James8c846492011-01-25 17:07:29 -0800148 try:
David James66009462012-03-25 10:08:38 -0700149 cros_build_lib.CreatePushBranch(prebuilt_branch, cwd)
David James8c846492011-01-25 17:07:29 -0800150 UpdateLocalFile(filename, value, key)
Brian Harring609dc4e2012-05-07 02:17:44 -0700151 cros_build_lib.RunGitCommand(cwd, ['add', filename])
152 cros_build_lib.RunGitCommand(cwd, ['commit', '-m', description])
153 cros_build_lib.GitPushWithRetry(prebuilt_branch, cwd, dryrun=dryrun,
David James66009462012-03-25 10:08:38 -0700154 retries=retries)
David James8c846492011-01-25 17:07:29 -0800155 finally:
David James1b6e67a2011-05-19 21:32:38 -0700156 cros_build_lib.RunCommand(['git', 'checkout', commit], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800157
158
159def GetVersion():
160 """Get the version to put in LATEST and update the git version with."""
161 return datetime.datetime.now().strftime('%d.%m.%y.%H%M%S')
162
163
David James015af872012-06-19 15:24:36 -0700164def _GsUpload(local_file, remote_file, acl):
David James8c846492011-01-25 17:07:29 -0800165 """Upload to GS bucket.
166
167 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800168 args: a tuple of three arguments that contains local_file, remote_file, and
169 the acl used for uploading the file.
David James8c846492011-01-25 17:07:29 -0800170
171 Returns:
172 Return the arg tuple of two if the upload failed
173 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700174 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
175 'authenticated-read', 'bucket-owner-full-control',
176 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700177 if acl in CANNED_ACLS:
David James32faafe2012-06-08 14:25:03 -0700178 cmd = [binpkg.GSUTIL_BIN, 'cp', '-a', acl, local_file, remote_file]
David James015af872012-06-19 15:24:36 -0700179 acl_cmd = None
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700180 else:
181 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700182 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James32faafe2012-06-08 14:25:03 -0700183 cmd = [binpkg.GSUTIL_BIN, 'cp', '-a', 'private', local_file, remote_file]
David James32faafe2012-06-08 14:25:03 -0700184 acl_cmd = [binpkg.GSUTIL_BIN, 'setacl', acl, remote_file]
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700185
David James015af872012-06-19 15:24:36 -0700186 cros_build_lib.RunCommandWithRetries(_RETRIES, cmd, print_cmd=False,
187 sleep=_SLEEP_TIME)
David James8c846492011-01-25 17:07:29 -0800188
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700189 if acl_cmd:
190 # Apply the passed in ACL xml file to the uploaded object.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700191 cros_build_lib.RunCommandWithRetries(_RETRIES, acl_cmd, print_cmd=False,
David James015af872012-06-19 15:24:36 -0700192 sleep=_SLEEP_TIME)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700193
194
David Jamesfd0b0852011-02-23 11:15:36 -0800195def RemoteUpload(acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800196 """Upload to google storage.
197
198 Create a pool of process and call _GsUpload with the proper arguments.
199
200 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800201 acl: The canned acl used for uploading. acl can be one of: "public-read",
202 "public-read-write", "authenticated-read", "bucket-owner-read",
203 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800204 files: dictionary with keys to local files and values to remote path.
205 pool: integer of maximum proesses to have at the same time.
206
207 Returns:
208 Return a set of tuple arguments of the failed uploads
209 """
David James015af872012-06-19 15:24:36 -0700210 tasks = [[key, value, acl] for key, value in files.iteritems()]
211 bg.RunTasksInProcessPool(_GsUpload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800212
213
214def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
215 """Build a dictionary of local remote file key pairs to upload.
216
217 Args:
218 base_local_path: The base path to the files on the local hard drive.
219 remote_path: The base path to the remote paths.
220 pkgs: The packages to upload.
221
222 Returns:
223 Returns a dictionary of local_path/remote_path pairs
224 """
225 upload_files = {}
226 for pkg in pkgs:
227 suffix = pkg['CPV'] + '.tbz2'
228 local_path = os.path.join(base_local_path, suffix)
229 assert os.path.exists(local_path)
230 remote_path = '%s/%s' % (base_remote_path.rstrip('/'), suffix)
231 upload_files[local_path] = remote_path
232
233 return upload_files
234
235def GetBoardPathFromCrosOverlayList(build_path, target):
236 """Use the cros_overlay_list to determine the path to the board overlay
237 Args:
238 build_path: The path to the root of the build directory
239 target: The target that we are looking for, could consist of board and
240 board_variant, we handle that properly
241 Returns:
242 The last line from cros_overlay_list as a string
243 """
Chris Sosa471532a2011-02-01 15:10:06 -0800244 script_dir = os.path.join(build_path, 'src/platform/dev/host')
David James4058b0d2011-12-08 21:24:50 -0800245 cmd = ['./cros_overlay_list', '--board', target.board]
246 if target.variant:
247 cmd += ['--variant', target.variant]
David James8c846492011-01-25 17:07:29 -0800248
249 cmd_output = cros_build_lib.RunCommand(cmd, redirect_stdout=True,
250 cwd=script_dir)
251 # We only care about the last entry
252 return cmd_output.output.splitlines().pop()
253
254
255def DeterminePrebuiltConfFile(build_path, target):
256 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
257
258 Args:
259 build_path: The path to the root of the build directory
260 target: String representation of the board. This includes host and board
261 targets
262
263 Returns
264 A string path to a prebuilt.conf file to be updated.
265 """
David James4058b0d2011-12-08 21:24:50 -0800266 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800267 # We are host.
268 # Without more examples of hosts this is a kludge for now.
269 # TODO(Scottz): as new host targets come online expand this to
270 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800271 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800272 else:
273 # We are a board
274 board = GetBoardPathFromCrosOverlayList(build_path, target)
275 make_path = os.path.join(board, 'prebuilt.conf')
276
277 return make_path
278
279
280def UpdateBinhostConfFile(path, key, value):
281 """Update binhost config file file with key=value.
282
283 Args:
284 path: Filename to update.
285 key: Key to update.
286 value: New value for key.
287 """
288 cwd = os.path.dirname(os.path.abspath(path))
289 filename = os.path.basename(path)
Brian Harringaf019fb2012-05-10 15:06:13 -0700290 osutils.SafeMakedirs(cwd)
Brian Harring22edb442012-05-11 23:55:18 -0700291 osutils.WriteFile(path, '', mode='a')
David James8c846492011-01-25 17:07:29 -0800292 UpdateLocalFile(path, value, key)
Chris Sosac13bba52011-05-24 15:14:09 -0700293 cros_build_lib.RunCommand(['git', 'add', filename], cwd=cwd)
David James20b2b6f2011-11-18 15:11:58 -0800294 description = 'Update %s="%s" in %s' % (key, value, filename)
Peter Mayo193f68f2011-04-19 19:08:21 -0400295 cros_build_lib.RunCommand(['git', 'commit', '-m', description], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800296
297
David Jamesce093af2011-02-23 15:21:58 -0800298def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800299 """Grab all of the packages files associated with a list of binhost_urls.
300
David James05bcb2b2011-02-09 09:25:47 -0800301 Args:
302 binhost_urls: The URLs for the directories containing the Packages files we
303 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800304
305 Returns:
306 A list of PackageIndex objects.
307 """
308 pkg_indexes = []
309 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700310 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800311 if pkg_index:
312 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800313 return pkg_indexes
314
315
David James05bcb2b2011-02-09 09:25:47 -0800316
David Jamesc0f158a2011-02-22 16:07:29 -0800317class PrebuiltUploader(object):
318 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800319
David James615e5b52011-06-03 11:10:15 -0700320 def __init__(self, upload_location, acl, binhost_base_url,
David James32b0b2f2011-07-13 20:56:50 -0700321 pkg_indexes, build_path, packages, skip_upload,
David James4058b0d2011-12-08 21:24:50 -0800322 binhost_conf_dir, debug, target, slave_targets):
David Jamesc0f158a2011-02-22 16:07:29 -0800323 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800324
David Jamesc0f158a2011-02-22 16:07:29 -0800325 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800326
David Jamesc0f158a2011-02-22 16:07:29 -0800327 Args:
328 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800329 acl: The canned acl used for uploading to Google Storage. acl can be one
330 of: "public-read", "public-read-write", "authenticated-read",
331 "bucket-owner-read", "bucket-owner-full-control", or "private". If
332 we are not uploading to Google Storage, this parameter is unused.
333 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800334 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
335 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700336 build_path: The path to the directory containing the chroot.
337 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700338 skip_upload: Don't actually upload the tarballs.
339 binhost_conf_dir: Directory where to store binhost.conf files.
340 debug: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800341 target: BuildTarget managed by this builder.
342 slave_targets: List of BuildTargets managed by slave builders.
David Jamesc0f158a2011-02-22 16:07:29 -0800343 """
344 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800345 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800346 self._binhost_base_url = binhost_base_url
347 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700348 self._build_path = build_path
349 self._packages = set(packages)
David James8ece7ee2011-06-29 16:02:30 -0700350 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700351 self._binhost_conf_dir = binhost_conf_dir
David James27fa7d12011-06-29 17:24:14 -0700352 self._debug = debug
David James4058b0d2011-12-08 21:24:50 -0800353 self._target = target
354 self._slave_targets = slave_targets
David James615e5b52011-06-03 11:10:15 -0700355
356 def _ShouldFilterPackage(self, pkg):
357 if not self._packages:
358 return False
359 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800360 sys.path.insert(0, pym_path)
David James615e5b52011-06-03 11:10:15 -0700361 import portage.versions
362 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
363 cp = '%s/%s' % (cat, pkgname)
364 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800365
David Jamesc0f158a2011-02-22 16:07:29 -0800366 def _UploadPrebuilt(self, package_path, url_suffix):
367 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800368
David Jamesc0f158a2011-02-22 16:07:29 -0800369 Args:
370 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800371 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesa3bba142011-05-26 21:24:20 -0700372
David Jamesc0f158a2011-02-22 16:07:29 -0800373 """
David James8c846492011-01-25 17:07:29 -0800374
David Jamesc0f158a2011-02-22 16:07:29 -0800375 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700376 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800377 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700378 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800379 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
David James05bcb2b2011-02-09 09:25:47 -0800380
David Jamesc0f158a2011-02-22 16:07:29 -0800381 # Write Packages file.
382 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800383
David Jamesc0f158a2011-02-22 16:07:29 -0800384 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700385 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800386
David James015af872012-06-19 15:24:36 -0700387 # Build list of files to upload.
388 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
389 remote_file = '%s/Packages' % remote_location.rstrip('/')
390 upload_files[tmp_packages_file.name] = remote_file
391
392 RemoteUpload(self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800393
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200394 def _UploadBoardTarball(self, board_path, url_suffix, version):
David James8fa34ea2011-04-15 13:00:20 -0700395 """Upload a tarball of the board at the specified path to Google Storage.
396
397 Args:
398 board_path: The path to the board dir.
399 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200400 version: The version of the board.
David James8fa34ea2011-04-15 13:00:20 -0700401 """
402 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
403 assert remote_location.startswith('gs://')
404 cwd, boardname = os.path.split(board_path.rstrip(os.path.sep))
405 tmpdir = tempfile.mkdtemp()
406 try:
407 tarfile = os.path.join(tmpdir, '%s.tbz2' % boardname)
Brian Harringd223a242012-02-03 20:12:10 -0800408 cmd = ['tar', '-I', 'pbzip2', '-cf', tarfile]
David James8fa34ea2011-04-15 13:00:20 -0700409 excluded_paths = ('usr/lib/debug', 'usr/local/autotest', 'packages',
410 'tmp')
411 for path in excluded_paths:
Zdenek Behane3ed3462011-06-16 00:36:08 +0200412 cmd.append('--exclude=%s/*' % path)
413 cmd.append('.')
Brian Harringd223a242012-02-03 20:12:10 -0800414 cros_build_lib.SudoRunCommand(cmd, cwd=os.path.join(cwd, boardname))
David James8fa34ea2011-04-15 13:00:20 -0700415 remote_tarfile = '%s/%s.tbz2' % (remote_location.rstrip('/'), boardname)
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200416 # FIXME(zbehan): Temporary hack to upload amd64-host chroots to a
417 # different gs bucket. The right way is to do the upload in a separate
418 # pass of this script.
419 if boardname == 'amd64-host':
Zdenek Behanaf3c9002011-06-24 10:07:40 +0200420 # FIXME(zbehan): Why does version contain the prefix "chroot-"?
421 remote_tarfile = \
422 'gs://chromiumos-sdk/cros-sdk-%s.tbz2' % version.strip('chroot-')
David James015af872012-06-19 15:24:36 -0700423 _GsUpload(tarfile, remote_tarfile, self._acl)
David James8fa34ea2011-04-15 13:00:20 -0700424 finally:
Brian Harringd223a242012-02-03 20:12:10 -0800425 cros_build_lib.SudoRunCommand(['rm', '-rf', tmpdir], cwd=cwd)
David James8fa34ea2011-04-15 13:00:20 -0700426
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700427 def _GetTargets(self):
428 """Retuns the list of targets to use."""
429 targets = self._slave_targets[:]
430 if self._target:
431 targets.append(self._target)
432
433 return targets
434
435 def SyncHostPrebuilts(self, version, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800436 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800437
David Jamesc0f158a2011-02-22 16:07:29 -0800438 This function will sync both the standard host packages, plus the host
439 packages associated with all targets that have been "setup" with the
440 current host's chroot. For instance, if this host has been used to build
441 x86-generic, it will sync the host packages associated with
442 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
443 it will also sync the host packages associated with
444 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800445
David Jamesc0f158a2011-02-22 16:07:29 -0800446 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800447 version: A unique string, intended to be included in the upload path,
448 which identifies the version number of the uploaded prebuilts.
449 key: The variable key to update in the git file.
450 git_sync: If set, update make.conf of target to reference the latest
451 prebuilt packages generated here.
452 sync_binhost_conf: If set, update binhost config file in
453 chromiumos-overlay for the host.
454 """
David Jamese2488642011-11-14 16:15:20 -0800455 # Slave boards are listed before the master board so that the master board
456 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
457 # over preflight host prebuilts from other builders.)
458 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700459 for target in self._GetTargets():
David James4058b0d2011-12-08 21:24:50 -0800460 url_suffix = _REL_HOST_PATH % {'version': version,
461 'host_arch': _HOST_ARCH,
462 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800463 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800464
David James4058b0d2011-12-08 21:24:50 -0800465 if self._target == target and not self._skip_upload and not self._debug:
David Jamese2488642011-11-14 16:15:20 -0800466 # Upload prebuilts.
467 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
468 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700469
David Jamese2488642011-11-14 16:15:20 -0800470 # Record URL where prebuilts were uploaded.
471 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
472 packages_url_suffix.rstrip('/')))
473
David James20b2b6f2011-11-18 15:11:58 -0800474 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700475 if git_sync:
476 git_file = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800477 _PREBUILT_MAKE_CONF[_HOST_ARCH])
David Jamese2488642011-11-14 16:15:20 -0800478 RevGitFile(git_file, binhost, key=key, dryrun=self._debug)
David James8ece7ee2011-06-29 16:02:30 -0700479 if sync_binhost_conf:
David James32b0b2f2011-07-13 20:56:50 -0700480 binhost_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James4058b0d2011-12-08 21:24:50 -0800481 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800482 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800483
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700484 def SyncBoardPrebuilts(self, version, key, git_sync, sync_binhost_conf,
485 upload_board_tarball):
David Jamesc0f158a2011-02-22 16:07:29 -0800486 """Synchronize board prebuilt files.
487
488 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800489 version: A unique string, intended to be included in the upload path,
490 which identifies the version number of the uploaded prebuilts.
491 key: The variable key to update in the git file.
492 git_sync: If set, update make.conf of target to reference the latest
493 prebuilt packages generated here.
494 sync_binhost_conf: If set, update binhost config file in
495 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700496 upload_board_tarball: Include a tarball of the board in our upload.
David Jamesc0f158a2011-02-22 16:07:29 -0800497 """
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700498 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800499 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800500 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800501 package_path = os.path.join(board_path, 'packages')
David James4058b0d2011-12-08 21:24:50 -0800502 url_suffix = _REL_BOARD_PATH % {'target': target, 'version': version}
David Jamese2488642011-11-14 16:15:20 -0800503 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700504
David James4058b0d2011-12-08 21:24:50 -0800505 if self._target == target and not self._skip_upload and not self._debug:
David Jamese2488642011-11-14 16:15:20 -0800506 # Upload board tarballs in the background.
507 if upload_board_tarball:
508 tar_process = multiprocessing.Process(target=self._UploadBoardTarball,
509 args=(board_path, url_suffix,
510 version))
511 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700512
David Jamese2488642011-11-14 16:15:20 -0800513 # Upload prebuilts.
514 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700515
David Jamese2488642011-11-14 16:15:20 -0800516 # Make sure we finished uploading the board tarballs.
517 if upload_board_tarball:
518 tar_process.join()
519 assert tar_process.exitcode == 0
520 # TODO(zbehan): This should be done cleaner.
David James4058b0d2011-12-08 21:24:50 -0800521 if target.board == 'amd64-host':
David Jamese2488642011-11-14 16:15:20 -0800522 sdk_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James8ece7ee2011-06-29 16:02:30 -0700523 'host/sdk_version.conf')
David Jamese2488642011-11-14 16:15:20 -0800524 RevGitFile(sdk_conf, version.strip('chroot-'),
525 key='SDK_LATEST_VERSION', 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)
David Jamese2488642011-11-14 16:15:20 -0800533 RevGitFile(git_file, url_value, key=key, dryrun=self._debug)
534 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')
David James4058b0d2011-12-08 21:24:50 -0800576 parser.add_option('', '--profile', dest='profile', default=None,
577 help='Profile that was built on this machine')
578 parser.add_option('', '--slave-board', default=[], action='callback',
579 dest='slave_targets', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700580 callback=_AddSlaveBoard,
David James4058b0d2011-12-08 21:24:50 -0800581 help='Board type that was built on a slave machine. To '
582 'add a profile to this board, use --slave-profile.')
583 parser.add_option('', '--slave-profile', action='callback', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700584 callback=_AddSlaveProfile,
David James4058b0d2011-12-08 21:24:50 -0800585 help='Board profile that was built on a slave machine. '
586 'Applies to previous slave board.')
David James8c846492011-01-25 17:07:29 -0800587 parser.add_option('-p', '--build-path', dest='build_path',
David James05bcb2b2011-02-09 09:25:47 -0800588 help='Path to the directory containing the chroot')
David James615e5b52011-06-03 11:10:15 -0700589 parser.add_option('', '--packages', action='append',
590 default=[], dest='packages',
591 help='Only include the specified packages. '
592 '(Default is to include all packages.)')
David James8c846492011-01-25 17:07:29 -0800593 parser.add_option('-s', '--sync-host', dest='sync_host',
594 default=False, action='store_true',
595 help='Sync host prebuilts')
596 parser.add_option('-g', '--git-sync', dest='git_sync',
597 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800598 help='Enable git version sync (This commits to a repo.) '
599 'This is used by full builders to commit directly '
600 'to board overlays.')
David James8c846492011-01-25 17:07:29 -0800601 parser.add_option('-u', '--upload', dest='upload',
602 default=None,
603 help='Upload location')
604 parser.add_option('-V', '--prepend-version', dest='prepend_version',
605 default=None,
606 help='Add an identifier to the front of the version')
607 parser.add_option('-f', '--filters', dest='filters', action='store_true',
608 default=False,
609 help='Turn on filtering of private ebuild packages')
610 parser.add_option('-k', '--key', dest='key',
611 default='PORTAGE_BINHOST',
612 help='Key to update in make.conf / binhost.conf')
David James8ece7ee2011-06-29 16:02:30 -0700613 parser.add_option('', '--set-version', dest='set_version',
614 default=None,
615 help='Specify the version string')
David James8c846492011-01-25 17:07:29 -0800616 parser.add_option('', '--sync-binhost-conf', dest='sync_binhost_conf',
617 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800618 help='Update binhost.conf in chromiumos-overlay or '
619 'chromeos-overlay. Commit the changes, but don\'t '
620 'push them. This is used for preflight binhosts.')
David James32b0b2f2011-07-13 20:56:50 -0700621 parser.add_option('', '--binhost-conf-dir', dest='binhost_conf_dir',
622 default=_BINHOST_CONF_DIR,
623 help='Directory to commit binhost config with '
624 '--sync-binhost-conf.')
David Jamesfd0b0852011-02-23 11:15:36 -0800625 parser.add_option('-P', '--private', dest='private', action='store_true',
626 default=False, help='Mark gs:// uploads as private.')
David James8ece7ee2011-06-29 16:02:30 -0700627 parser.add_option('', '--skip-upload', dest='skip_upload',
628 action='store_true', default=False,
629 help='Skip upload step.')
David James8fa34ea2011-04-15 13:00:20 -0700630 parser.add_option('', '--upload-board-tarball', dest='upload_board_tarball',
631 action='store_true', default=False,
632 help='Upload board tarball to Google Storage.')
David James27fa7d12011-06-29 17:24:14 -0700633 parser.add_option('', '--debug', dest='debug',
634 action='store_true', default=False,
635 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800636
637 options, args = parser.parse_args()
David James8c846492011-01-25 17:07:29 -0800638 if not options.build_path:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700639 Usage(parser, 'Error: you need provide a chroot path')
David James8ece7ee2011-06-29 16:02:30 -0700640 if not options.upload and not options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700641 Usage(parser, 'Error: you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700642 if not options.set_version and options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700643 Usage(parser, 'Error: If you are using --skip-upload, you must specify a '
David James8ece7ee2011-06-29 16:02:30 -0700644 'version number using --set-version.')
David James9417f272011-05-26 13:24:47 -0700645 if args:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700646 Usage(parser, 'Error: invalid arguments passed to prebuilt.py: %r' % args)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700647
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700648 target = None
649 if options.board:
650 target = BuildTarget(options.board, options.profile)
651
652 if target in options.slave_targets:
653 Usage(parser, 'Error: --board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800654
David James4058b0d2011-12-08 21:24:50 -0800655 if len(set(options.slave_targets)) != len(options.slave_targets):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700656 Usage(parser, 'Error: --slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800657
David James4058b0d2011-12-08 21:24:50 -0800658 if options.slave_targets and options.git_sync:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700659 Usage(parser, 'Error: --slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800660
David James8ece7ee2011-06-29 16:02:30 -0700661 if (options.upload_board_tarball and options.skip_upload and
662 options.board == 'amd64-host'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700663 Usage(parser, 'Error: --skip-upload is not compatible with '
David James8ece7ee2011-06-29 16:02:30 -0700664 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700665
David James8ece7ee2011-06-29 16:02:30 -0700666 if (options.upload_board_tarball and not options.skip_upload and
667 not options.upload.startswith('gs://')):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700668 Usage(parser, 'Error: --upload-board-tarball only works with gs:// URLs.\n'
David James8fa34ea2011-04-15 13:00:20 -0700669 '--upload must be a gs:// URL.')
670
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700671 if options.private:
672 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700673 Usage(parser, 'Error: --private and --sync-host/-s cannot be specified '
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700674 'together, we do not support private host prebuilts')
675
David James8ece7ee2011-06-29 16:02:30 -0700676 if not options.upload or not options.upload.startswith('gs://'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700677 Usage(parser, 'Error: --private is only valid for gs:// URLs.\n'
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700678 '--upload must be a gs:// URL.')
679
680 if options.binhost_base_url != _BINHOST_BASE_URL:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700681 Usage(parser, 'Error: when using --private the --binhost-base-url '
David Jamese2488642011-11-14 16:15:20 -0800682 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700683
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700684 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800685
686def main():
David Jamesdb401072011-06-10 12:17:16 -0700687 # Set umask to a sane value so that files created as root are readable.
688 os.umask(022)
689
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700690 options, target = ParseOptions()
David Jamesc0f158a2011-02-22 16:07:29 -0800691
David James05bcb2b2011-02-09 09:25:47 -0800692 # Calculate a list of Packages index files to compare against. Whenever we
693 # upload a package, we check to make sure it's not already stored in one of
694 # the packages files we uploaded. This list of packages files might contain
695 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800696 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800697
David James8ece7ee2011-06-29 16:02:30 -0700698 if options.set_version:
699 version = options.set_version
700 else:
701 version = GetVersion()
David Jamesc0f158a2011-02-22 16:07:29 -0800702 if options.prepend_version:
703 version = '%s-%s' % (options.prepend_version, version)
704
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700705 acl = 'public-read'
706 binhost_base_url = options.binhost_base_url
707
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700708 if target and options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700709 binhost_base_url = options.upload
710 board_path = GetBoardPathFromCrosOverlayList(options.build_path,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700711 target)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700712 acl = os.path.join(board_path, _GOOGLESTORAGE_ACL_FILE)
713
714 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700715 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700716 options.packages, options.skip_upload,
David Jamese2488642011-11-14 16:15:20 -0800717 options.binhost_conf_dir, options.debug,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700718 target, options.slave_targets)
David Jamesc0f158a2011-02-22 16:07:29 -0800719
David James8c846492011-01-25 17:07:29 -0800720 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700721 uploader.SyncHostPrebuilts(version, options.key, options.git_sync,
722 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800723
Chris Sosa62c8ff52012-06-04 15:03:12 -0700724 if options.board or options.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700725 uploader.SyncBoardPrebuilts(version, options.key, options.git_sync,
726 options.sync_binhost_conf,
727 options.upload_board_tarball)
David James8c846492011-01-25 17:07:29 -0800728
729if __name__ == '__main__':
730 main()