blob: 23b4af4c358f9af174863adf1b74f5f4ba2510a3 [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:
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
27import tempfile
28
David James015af872012-06-19 15:24:36 -070029from chromite.buildbot import cbuildbot_background as bg
Chris Sosa1dc96132012-05-11 15:40:50 -070030from chromite.lib import cros_build_lib
Brian Harringaf019fb2012-05-10 15:06:13 -070031from chromite.lib import osutils
David James32faafe2012-06-08 14:25:03 -070032from chromite.lib import binpkg
Chris Sosa1dc96132012-05-11 15:40:50 -070033
David James015af872012-06-19 15:24:36 -070034# How many times to retry uploads.
35_RETRIES = 10
36
37# Multiplier for how long to sleep (in seconds) between retries; will delay
38# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
39_SLEEP_TIME = 60
40
David James8c846492011-01-25 17:07:29 -080041_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080042_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070043_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080044_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080045_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080046_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
47_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080048# Private overlays to look at for builds to filter
49# relative to build path
50_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Scott Zawalskiab1bed32011-03-16 15:24:24 -070051_GOOGLESTORAGE_ACL_FILE = 'googlestorage_acl.xml'
David Jamesce619292011-11-08 11:42:36 -080052_BINHOST_BASE_URL = 'https://commondatastorage.googleapis.com/chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080053_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
54# Created in the event of new host targets becoming available
55_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
56 'make.conf.amd64-host')}
57_BINHOST_CONF_DIR = 'src/third_party/chromiumos-overlay/chromeos/binhost'
58
59
David James4058b0d2011-12-08 21:24:50 -080060class BuildTarget(object):
61 """A board/variant/profile tuple."""
62
63 def __init__(self, board_variant, profile=None):
64 self.board_variant = board_variant
65 self.board, _, self.variant = board_variant.partition('_')
66 self.profile = profile
67
68 def __str__(self):
69 if self.profile:
70 return '%s_%s' % (self.board_variant, self.profile)
71 else:
72 return self.board_variant
73
74 def __eq__(self, other):
75 return str(other) == str(self)
76
77 def __hash__(self):
78 return hash(str(self))
79
80
David James8c846492011-01-25 17:07:29 -080081def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
82 """Update the key in file with the value passed.
83 File format:
84 key="value"
85 Note quotes are added automatically
86
87 Args:
88 filename: Name of file to modify.
89 value: Value to write with the key.
90 key: The variable key to update. (Default: PORTAGE_BINHOST)
91 """
92 if os.path.exists(filename):
93 file_fh = open(filename)
94 else:
95 file_fh = open(filename, 'w+')
96 file_lines = []
97 found = False
98 keyval_str = '%(key)s=%(value)s'
99 for line in file_fh:
100 # Strip newlines from end of line. We already add newlines below.
101 line = line.rstrip("\n")
102
103 if len(line.split('=')) != 2:
104 # Skip any line that doesn't fit key=val.
105 file_lines.append(line)
106 continue
107
108 file_var, file_val = line.split('=')
109 if file_var == key:
110 found = True
David James20b2b6f2011-11-18 15:11:58 -0800111 print 'Updating %s=%s to %s="%s"' % (file_var, file_val, key, value)
David James8c846492011-01-25 17:07:29 -0800112 value = '"%s"' % value
113 file_lines.append(keyval_str % {'key': key, 'value': value})
114 else:
115 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
116
117 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700118 value = '"%s"' % value
David James8c846492011-01-25 17:07:29 -0800119 file_lines.append(keyval_str % {'key': key, 'value': value})
120
121 file_fh.close()
122 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700123 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David James8c846492011-01-25 17:07:29 -0800124
125
David James27fa7d12011-06-29 17:24:14 -0700126def RevGitFile(filename, value, retries=5, key='PORTAGE_BINHOST', dryrun=False):
David James8c846492011-01-25 17:07:29 -0800127 """Update and push the git file.
128
129 Args:
130 filename: file to modify that is in a git repo already
131 value: string representing the version of the prebuilt that has been
132 uploaded.
133 retries: The number of times to retry before giving up, default: 5
134 key: The variable key to update in the git file.
135 (Default: PORTAGE_BINHOST)
136 """
137 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700138 cwd = os.path.abspath(os.path.dirname(filename))
Brian Harring609dc4e2012-05-07 02:17:44 -0700139 commit = cros_build_lib.RunGitCommand(
140 cwd, ['rev-parse', 'HEAD']).output.rstrip()
David James8c846492011-01-25 17:07:29 -0800141 description = 'Update %s="%s" in %s' % (key, value, filename)
142 print description
David James66009462012-03-25 10:08:38 -0700143
David James8c846492011-01-25 17:07:29 -0800144 try:
David James66009462012-03-25 10:08:38 -0700145 cros_build_lib.CreatePushBranch(prebuilt_branch, cwd)
David James8c846492011-01-25 17:07:29 -0800146 UpdateLocalFile(filename, value, key)
Brian Harring609dc4e2012-05-07 02:17:44 -0700147 cros_build_lib.RunGitCommand(cwd, ['add', filename])
148 cros_build_lib.RunGitCommand(cwd, ['commit', '-m', description])
149 cros_build_lib.GitPushWithRetry(prebuilt_branch, cwd, dryrun=dryrun,
David James66009462012-03-25 10:08:38 -0700150 retries=retries)
David James8c846492011-01-25 17:07:29 -0800151 finally:
David James1b6e67a2011-05-19 21:32:38 -0700152 cros_build_lib.RunCommand(['git', 'checkout', commit], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800153
154
155def GetVersion():
156 """Get the version to put in LATEST and update the git version with."""
157 return datetime.datetime.now().strftime('%d.%m.%y.%H%M%S')
158
159
David James015af872012-06-19 15:24:36 -0700160def _GsUpload(local_file, remote_file, acl):
David James8c846492011-01-25 17:07:29 -0800161 """Upload to GS bucket.
162
163 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800164 args: a tuple of three arguments that contains local_file, remote_file, and
165 the acl used for uploading the file.
David James8c846492011-01-25 17:07:29 -0800166
167 Returns:
168 Return the arg tuple of two if the upload failed
169 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700170 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
171 'authenticated-read', 'bucket-owner-full-control',
172 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700173 if acl in CANNED_ACLS:
David James32faafe2012-06-08 14:25:03 -0700174 cmd = [binpkg.GSUTIL_BIN, 'cp', '-a', acl, local_file, remote_file]
David James015af872012-06-19 15:24:36 -0700175 acl_cmd = None
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700176 else:
177 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700178 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James32faafe2012-06-08 14:25:03 -0700179 cmd = [binpkg.GSUTIL_BIN, 'cp', '-a', 'private', local_file, remote_file]
David James32faafe2012-06-08 14:25:03 -0700180 acl_cmd = [binpkg.GSUTIL_BIN, 'setacl', acl, remote_file]
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700181
David Jamesc5cbd472012-06-19 16:25:45 -0700182 cros_build_lib.RunCommandWithRetries(_RETRIES, cmd, print_cmd=True,
183 sleep=_SLEEP_TIME,
184 redirect_stdout=True,
185 redirect_stderr=True)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700186 if acl_cmd:
187 # Apply the passed in ACL xml file to the uploaded object.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700188 cros_build_lib.RunCommandWithRetries(_RETRIES, acl_cmd, print_cmd=False,
David James015af872012-06-19 15:24:36 -0700189 sleep=_SLEEP_TIME)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700190
191
David Jamesfd0b0852011-02-23 11:15:36 -0800192def RemoteUpload(acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800193 """Upload to google storage.
194
195 Create a pool of process and call _GsUpload with the proper arguments.
196
197 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800198 acl: The canned acl used for uploading. acl can be one of: "public-read",
199 "public-read-write", "authenticated-read", "bucket-owner-read",
200 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800201 files: dictionary with keys to local files and values to remote path.
202 pool: integer of maximum proesses to have at the same time.
203
204 Returns:
205 Return a set of tuple arguments of the failed uploads
206 """
David James015af872012-06-19 15:24:36 -0700207 tasks = [[key, value, acl] for key, value in files.iteritems()]
208 bg.RunTasksInProcessPool(_GsUpload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800209
210
211def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
212 """Build a dictionary of local remote file key pairs to upload.
213
214 Args:
215 base_local_path: The base path to the files on the local hard drive.
216 remote_path: The base path to the remote paths.
217 pkgs: The packages to upload.
218
219 Returns:
220 Returns a dictionary of local_path/remote_path pairs
221 """
222 upload_files = {}
223 for pkg in pkgs:
224 suffix = pkg['CPV'] + '.tbz2'
225 local_path = os.path.join(base_local_path, suffix)
226 assert os.path.exists(local_path)
227 remote_path = '%s/%s' % (base_remote_path.rstrip('/'), suffix)
228 upload_files[local_path] = remote_path
229
230 return upload_files
231
232def GetBoardPathFromCrosOverlayList(build_path, target):
233 """Use the cros_overlay_list to determine the path to the board overlay
234 Args:
235 build_path: The path to the root of the build directory
236 target: The target that we are looking for, could consist of board and
237 board_variant, we handle that properly
238 Returns:
239 The last line from cros_overlay_list as a string
240 """
Chris Sosa471532a2011-02-01 15:10:06 -0800241 script_dir = os.path.join(build_path, 'src/platform/dev/host')
David James4058b0d2011-12-08 21:24:50 -0800242 cmd = ['./cros_overlay_list', '--board', target.board]
243 if target.variant:
244 cmd += ['--variant', target.variant]
David James8c846492011-01-25 17:07:29 -0800245
246 cmd_output = cros_build_lib.RunCommand(cmd, redirect_stdout=True,
247 cwd=script_dir)
248 # We only care about the last entry
249 return cmd_output.output.splitlines().pop()
250
251
252def DeterminePrebuiltConfFile(build_path, target):
253 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
254
255 Args:
256 build_path: The path to the root of the build directory
257 target: String representation of the board. This includes host and board
258 targets
259
260 Returns
261 A string path to a prebuilt.conf file to be updated.
262 """
David James4058b0d2011-12-08 21:24:50 -0800263 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800264 # We are host.
265 # Without more examples of hosts this is a kludge for now.
266 # TODO(Scottz): as new host targets come online expand this to
267 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800268 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800269 else:
270 # We are a board
271 board = GetBoardPathFromCrosOverlayList(build_path, target)
272 make_path = os.path.join(board, 'prebuilt.conf')
273
274 return make_path
275
276
277def UpdateBinhostConfFile(path, key, value):
278 """Update binhost config file file with key=value.
279
280 Args:
281 path: Filename to update.
282 key: Key to update.
283 value: New value for key.
284 """
285 cwd = os.path.dirname(os.path.abspath(path))
286 filename = os.path.basename(path)
Brian Harringaf019fb2012-05-10 15:06:13 -0700287 osutils.SafeMakedirs(cwd)
Brian Harring22edb442012-05-11 23:55:18 -0700288 osutils.WriteFile(path, '', mode='a')
David James8c846492011-01-25 17:07:29 -0800289 UpdateLocalFile(path, value, key)
Chris Sosac13bba52011-05-24 15:14:09 -0700290 cros_build_lib.RunCommand(['git', 'add', filename], cwd=cwd)
David James20b2b6f2011-11-18 15:11:58 -0800291 description = 'Update %s="%s" in %s' % (key, value, filename)
Peter Mayo193f68f2011-04-19 19:08:21 -0400292 cros_build_lib.RunCommand(['git', 'commit', '-m', description], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800293
294
David Jamesce093af2011-02-23 15:21:58 -0800295def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800296 """Grab all of the packages files associated with a list of binhost_urls.
297
David James05bcb2b2011-02-09 09:25:47 -0800298 Args:
299 binhost_urls: The URLs for the directories containing the Packages files we
300 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800301
302 Returns:
303 A list of PackageIndex objects.
304 """
305 pkg_indexes = []
306 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700307 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800308 if pkg_index:
309 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800310 return pkg_indexes
311
312
David James05bcb2b2011-02-09 09:25:47 -0800313
David Jamesc0f158a2011-02-22 16:07:29 -0800314class PrebuiltUploader(object):
315 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800316
David James615e5b52011-06-03 11:10:15 -0700317 def __init__(self, upload_location, acl, binhost_base_url,
David James32b0b2f2011-07-13 20:56:50 -0700318 pkg_indexes, build_path, packages, skip_upload,
David James4058b0d2011-12-08 21:24:50 -0800319 binhost_conf_dir, debug, target, slave_targets):
David Jamesc0f158a2011-02-22 16:07:29 -0800320 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800321
David Jamesc0f158a2011-02-22 16:07:29 -0800322 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800323
David Jamesc0f158a2011-02-22 16:07:29 -0800324 Args:
325 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800326 acl: The canned acl used for uploading to Google Storage. acl can be one
327 of: "public-read", "public-read-write", "authenticated-read",
328 "bucket-owner-read", "bucket-owner-full-control", or "private". If
329 we are not uploading to Google Storage, this parameter is unused.
330 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800331 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
332 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700333 build_path: The path to the directory containing the chroot.
334 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700335 skip_upload: Don't actually upload the tarballs.
336 binhost_conf_dir: Directory where to store binhost.conf files.
337 debug: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800338 target: BuildTarget managed by this builder.
339 slave_targets: List of BuildTargets managed by slave builders.
David Jamesc0f158a2011-02-22 16:07:29 -0800340 """
341 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800342 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800343 self._binhost_base_url = binhost_base_url
344 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700345 self._build_path = build_path
346 self._packages = set(packages)
David James8ece7ee2011-06-29 16:02:30 -0700347 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700348 self._binhost_conf_dir = binhost_conf_dir
David James27fa7d12011-06-29 17:24:14 -0700349 self._debug = debug
David James4058b0d2011-12-08 21:24:50 -0800350 self._target = target
351 self._slave_targets = slave_targets
David James615e5b52011-06-03 11:10:15 -0700352
353 def _ShouldFilterPackage(self, pkg):
354 if not self._packages:
355 return False
356 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800357 sys.path.insert(0, pym_path)
David James615e5b52011-06-03 11:10:15 -0700358 import portage.versions
359 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
360 cp = '%s/%s' % (cat, pkgname)
361 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800362
David Jamesc0f158a2011-02-22 16:07:29 -0800363 def _UploadPrebuilt(self, package_path, url_suffix):
364 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800365
David Jamesc0f158a2011-02-22 16:07:29 -0800366 Args:
367 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800368 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesa3bba142011-05-26 21:24:20 -0700369
David Jamesc0f158a2011-02-22 16:07:29 -0800370 """
David James8c846492011-01-25 17:07:29 -0800371
David Jamesc0f158a2011-02-22 16:07:29 -0800372 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700373 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800374 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700375 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800376 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
David James05bcb2b2011-02-09 09:25:47 -0800377
David Jamesc0f158a2011-02-22 16:07:29 -0800378 # Write Packages file.
379 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800380
David Jamesc0f158a2011-02-22 16:07:29 -0800381 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700382 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800383
David James015af872012-06-19 15:24:36 -0700384 # Build list of files to upload.
385 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
386 remote_file = '%s/Packages' % remote_location.rstrip('/')
387 upload_files[tmp_packages_file.name] = remote_file
388
389 RemoteUpload(self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800390
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200391 def _UploadBoardTarball(self, board_path, url_suffix, version):
David James8fa34ea2011-04-15 13:00:20 -0700392 """Upload a tarball of the board at the specified path to Google Storage.
393
394 Args:
395 board_path: The path to the board dir.
396 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200397 version: The version of the board.
David James8fa34ea2011-04-15 13:00:20 -0700398 """
399 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
400 assert remote_location.startswith('gs://')
401 cwd, boardname = os.path.split(board_path.rstrip(os.path.sep))
402 tmpdir = tempfile.mkdtemp()
403 try:
404 tarfile = os.path.join(tmpdir, '%s.tbz2' % boardname)
Brian Harringd223a242012-02-03 20:12:10 -0800405 cmd = ['tar', '-I', 'pbzip2', '-cf', tarfile]
David James8fa34ea2011-04-15 13:00:20 -0700406 excluded_paths = ('usr/lib/debug', 'usr/local/autotest', 'packages',
407 'tmp')
408 for path in excluded_paths:
Zdenek Behane3ed3462011-06-16 00:36:08 +0200409 cmd.append('--exclude=%s/*' % path)
410 cmd.append('.')
Brian Harringd223a242012-02-03 20:12:10 -0800411 cros_build_lib.SudoRunCommand(cmd, cwd=os.path.join(cwd, boardname))
David James8fa34ea2011-04-15 13:00:20 -0700412 remote_tarfile = '%s/%s.tbz2' % (remote_location.rstrip('/'), boardname)
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200413 # FIXME(zbehan): Temporary hack to upload amd64-host chroots to a
414 # different gs bucket. The right way is to do the upload in a separate
415 # pass of this script.
416 if boardname == 'amd64-host':
Zdenek Behanaf3c9002011-06-24 10:07:40 +0200417 # FIXME(zbehan): Why does version contain the prefix "chroot-"?
418 remote_tarfile = \
419 'gs://chromiumos-sdk/cros-sdk-%s.tbz2' % version.strip('chroot-')
David James015af872012-06-19 15:24:36 -0700420 _GsUpload(tarfile, remote_tarfile, self._acl)
David James8fa34ea2011-04-15 13:00:20 -0700421 finally:
Brian Harringd223a242012-02-03 20:12:10 -0800422 cros_build_lib.SudoRunCommand(['rm', '-rf', tmpdir], cwd=cwd)
David James8fa34ea2011-04-15 13:00:20 -0700423
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700424 def _GetTargets(self):
425 """Retuns the list of targets to use."""
426 targets = self._slave_targets[:]
427 if self._target:
428 targets.append(self._target)
429
430 return targets
431
432 def SyncHostPrebuilts(self, version, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800433 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800434
David Jamesc0f158a2011-02-22 16:07:29 -0800435 This function will sync both the standard host packages, plus the host
436 packages associated with all targets that have been "setup" with the
437 current host's chroot. For instance, if this host has been used to build
438 x86-generic, it will sync the host packages associated with
439 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
440 it will also sync the host packages associated with
441 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800442
David Jamesc0f158a2011-02-22 16:07:29 -0800443 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800444 version: A unique string, intended to be included in the upload path,
445 which identifies the version number of the uploaded prebuilts.
446 key: The variable key to update in the git file.
447 git_sync: If set, update make.conf of target to reference the latest
448 prebuilt packages generated here.
449 sync_binhost_conf: If set, update binhost config file in
450 chromiumos-overlay for the host.
451 """
David Jamese2488642011-11-14 16:15:20 -0800452 # Slave boards are listed before the master board so that the master board
453 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
454 # over preflight host prebuilts from other builders.)
455 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700456 for target in self._GetTargets():
David James4058b0d2011-12-08 21:24:50 -0800457 url_suffix = _REL_HOST_PATH % {'version': version,
458 'host_arch': _HOST_ARCH,
459 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800460 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800461
David James4058b0d2011-12-08 21:24:50 -0800462 if self._target == target and not self._skip_upload and not self._debug:
David Jamese2488642011-11-14 16:15:20 -0800463 # Upload prebuilts.
464 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
465 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700466
David Jamese2488642011-11-14 16:15:20 -0800467 # Record URL where prebuilts were uploaded.
468 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
469 packages_url_suffix.rstrip('/')))
470
David James20b2b6f2011-11-18 15:11:58 -0800471 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700472 if git_sync:
473 git_file = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800474 _PREBUILT_MAKE_CONF[_HOST_ARCH])
David Jamese2488642011-11-14 16:15:20 -0800475 RevGitFile(git_file, binhost, key=key, dryrun=self._debug)
David James8ece7ee2011-06-29 16:02:30 -0700476 if sync_binhost_conf:
David James32b0b2f2011-07-13 20:56:50 -0700477 binhost_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James4058b0d2011-12-08 21:24:50 -0800478 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800479 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800480
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700481 def SyncBoardPrebuilts(self, version, key, git_sync, sync_binhost_conf,
482 upload_board_tarball):
David Jamesc0f158a2011-02-22 16:07:29 -0800483 """Synchronize board prebuilt files.
484
485 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800486 version: A unique string, intended to be included in the upload path,
487 which identifies the version number of the uploaded prebuilts.
488 key: The variable key to update in the git file.
489 git_sync: If set, update make.conf of target to reference the latest
490 prebuilt packages generated here.
491 sync_binhost_conf: If set, update binhost config file in
492 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700493 upload_board_tarball: Include a tarball of the board in our upload.
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,
507 version))
508 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700509
David Jamese2488642011-11-14 16:15:20 -0800510 # Upload prebuilts.
511 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700512
David Jamese2488642011-11-14 16:15:20 -0800513 # Make sure we finished uploading the board tarballs.
514 if upload_board_tarball:
515 tar_process.join()
516 assert tar_process.exitcode == 0
517 # TODO(zbehan): This should be done cleaner.
David James4058b0d2011-12-08 21:24:50 -0800518 if target.board == 'amd64-host':
David Jamese2488642011-11-14 16:15:20 -0800519 sdk_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James8ece7ee2011-06-29 16:02:30 -0700520 'host/sdk_version.conf')
David Jamese2488642011-11-14 16:15:20 -0800521 RevGitFile(sdk_conf, version.strip('chroot-'),
522 key='SDK_LATEST_VERSION', dryrun=self._debug)
David Jamesc0f158a2011-02-22 16:07:29 -0800523
David Jamese2488642011-11-14 16:15:20 -0800524 # Record URL where prebuilts were uploaded.
525 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
526 packages_url_suffix.rstrip('/'))
527
528 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800529 git_file = DeterminePrebuiltConfFile(self._build_path, target)
David Jamese2488642011-11-14 16:15:20 -0800530 RevGitFile(git_file, url_value, key=key, dryrun=self._debug)
531 if sync_binhost_conf:
532 binhost_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James4058b0d2011-12-08 21:24:50 -0800533 'target', '%s-%s.conf' % (target, key))
David Jamese2488642011-11-14 16:15:20 -0800534 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800535
536
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700537def Usage(parser, msg):
David James8c846492011-01-25 17:07:29 -0800538 """Display usage message and parser help then exit with 1."""
539 print >> sys.stderr, msg
540 parser.print_help()
541 sys.exit(1)
542
David James4058b0d2011-12-08 21:24:50 -0800543
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700544def _AddSlaveBoard(_option, _opt_str, value, parser):
545 """Callback that adds a slave board to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800546 parser.values.slave_targets.append(BuildTarget(value))
547
548
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700549def _AddSlaveProfile(_option, _opt_str, value, parser):
550 """Callback that adds a slave profile to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800551 if not parser.values.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700552 Usage(parser, 'Must specify --slave-board before --slave-profile')
David James4058b0d2011-12-08 21:24:50 -0800553 if parser.values.slave_targets[-1].profile is not None:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700554 Usage(parser, 'Cannot specify --slave-profile twice for same board')
David James4058b0d2011-12-08 21:24:50 -0800555 parser.values.slave_targets[-1].profile = value
556
557
David Jamesc0f158a2011-02-22 16:07:29 -0800558def ParseOptions():
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700559 """Returns options given by the user and the target specified.
560
561 Returns a tuple containing a parsed options object and BuildTarget.
562 target instance is None if no board is specified.
563 """
David James8c846492011-01-25 17:07:29 -0800564 parser = optparse.OptionParser()
565 parser.add_option('-H', '--binhost-base-url', dest='binhost_base_url',
566 default=_BINHOST_BASE_URL,
567 help='Base URL to use for binhost in make.conf updates')
568 parser.add_option('', '--previous-binhost-url', action='append',
569 default=[], dest='previous_binhost_url',
570 help='Previous binhost URL')
571 parser.add_option('-b', '--board', dest='board', default=None,
572 help='Board type that was built on this machine')
David James4058b0d2011-12-08 21:24:50 -0800573 parser.add_option('', '--profile', dest='profile', default=None,
574 help='Profile that was built on this machine')
575 parser.add_option('', '--slave-board', default=[], action='callback',
576 dest='slave_targets', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700577 callback=_AddSlaveBoard,
David James4058b0d2011-12-08 21:24:50 -0800578 help='Board type that was built on a slave machine. To '
579 'add a profile to this board, use --slave-profile.')
580 parser.add_option('', '--slave-profile', action='callback', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700581 callback=_AddSlaveProfile,
David James4058b0d2011-12-08 21:24:50 -0800582 help='Board profile that was built on a slave machine. '
583 'Applies to previous slave board.')
David James8c846492011-01-25 17:07:29 -0800584 parser.add_option('-p', '--build-path', dest='build_path',
David James05bcb2b2011-02-09 09:25:47 -0800585 help='Path to the directory containing the chroot')
David James615e5b52011-06-03 11:10:15 -0700586 parser.add_option('', '--packages', action='append',
587 default=[], dest='packages',
588 help='Only include the specified packages. '
589 '(Default is to include all packages.)')
David James8c846492011-01-25 17:07:29 -0800590 parser.add_option('-s', '--sync-host', dest='sync_host',
591 default=False, action='store_true',
592 help='Sync host prebuilts')
593 parser.add_option('-g', '--git-sync', dest='git_sync',
594 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800595 help='Enable git version sync (This commits to a repo.) '
596 'This is used by full builders to commit directly '
597 'to board overlays.')
David James8c846492011-01-25 17:07:29 -0800598 parser.add_option('-u', '--upload', dest='upload',
599 default=None,
600 help='Upload location')
601 parser.add_option('-V', '--prepend-version', dest='prepend_version',
602 default=None,
603 help='Add an identifier to the front of the version')
604 parser.add_option('-f', '--filters', dest='filters', action='store_true',
605 default=False,
606 help='Turn on filtering of private ebuild packages')
607 parser.add_option('-k', '--key', dest='key',
608 default='PORTAGE_BINHOST',
609 help='Key to update in make.conf / binhost.conf')
David James8ece7ee2011-06-29 16:02:30 -0700610 parser.add_option('', '--set-version', dest='set_version',
611 default=None,
612 help='Specify the version string')
David James8c846492011-01-25 17:07:29 -0800613 parser.add_option('', '--sync-binhost-conf', dest='sync_binhost_conf',
614 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800615 help='Update binhost.conf in chromiumos-overlay or '
616 'chromeos-overlay. Commit the changes, but don\'t '
617 'push them. This is used for preflight binhosts.')
David James32b0b2f2011-07-13 20:56:50 -0700618 parser.add_option('', '--binhost-conf-dir', dest='binhost_conf_dir',
619 default=_BINHOST_CONF_DIR,
620 help='Directory to commit binhost config with '
621 '--sync-binhost-conf.')
David Jamesfd0b0852011-02-23 11:15:36 -0800622 parser.add_option('-P', '--private', dest='private', action='store_true',
623 default=False, help='Mark gs:// uploads as private.')
David James8ece7ee2011-06-29 16:02:30 -0700624 parser.add_option('', '--skip-upload', dest='skip_upload',
625 action='store_true', default=False,
626 help='Skip upload step.')
David James8fa34ea2011-04-15 13:00:20 -0700627 parser.add_option('', '--upload-board-tarball', dest='upload_board_tarball',
628 action='store_true', default=False,
629 help='Upload board tarball to Google Storage.')
David James27fa7d12011-06-29 17:24:14 -0700630 parser.add_option('', '--debug', dest='debug',
631 action='store_true', default=False,
632 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800633
634 options, args = parser.parse_args()
David James8c846492011-01-25 17:07:29 -0800635 if not options.build_path:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700636 Usage(parser, 'Error: you need provide a chroot path')
David James8ece7ee2011-06-29 16:02:30 -0700637 if not options.upload and not options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700638 Usage(parser, 'Error: you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700639 if not options.set_version and options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700640 Usage(parser, 'Error: If you are using --skip-upload, you must specify a '
David James8ece7ee2011-06-29 16:02:30 -0700641 'version number using --set-version.')
David James9417f272011-05-26 13:24:47 -0700642 if args:
David Jamesc5cbd472012-06-19 16:25:45 -0700643 Usage(parser, 'Error: invalid arguments passed to upload_prebuilts: '
644 '%r' % args)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700645
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700646 target = None
647 if options.board:
648 target = BuildTarget(options.board, options.profile)
649
650 if target in options.slave_targets:
651 Usage(parser, 'Error: --board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800652
David James4058b0d2011-12-08 21:24:50 -0800653 if len(set(options.slave_targets)) != len(options.slave_targets):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700654 Usage(parser, 'Error: --slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800655
David James4058b0d2011-12-08 21:24:50 -0800656 if options.slave_targets and options.git_sync:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700657 Usage(parser, 'Error: --slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800658
David James8ece7ee2011-06-29 16:02:30 -0700659 if (options.upload_board_tarball and options.skip_upload and
660 options.board == 'amd64-host'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700661 Usage(parser, 'Error: --skip-upload is not compatible with '
David James8ece7ee2011-06-29 16:02:30 -0700662 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700663
David James8ece7ee2011-06-29 16:02:30 -0700664 if (options.upload_board_tarball and not options.skip_upload and
665 not options.upload.startswith('gs://')):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700666 Usage(parser, 'Error: --upload-board-tarball only works with gs:// URLs.\n'
David James8fa34ea2011-04-15 13:00:20 -0700667 '--upload must be a gs:// URL.')
668
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700669 if options.private:
670 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700671 Usage(parser, 'Error: --private and --sync-host/-s cannot be specified '
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700672 'together, we do not support private host prebuilts')
673
David James8ece7ee2011-06-29 16:02:30 -0700674 if not options.upload or not options.upload.startswith('gs://'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700675 Usage(parser, 'Error: --private is only valid for gs:// URLs.\n'
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700676 '--upload must be a gs:// URL.')
677
678 if options.binhost_base_url != _BINHOST_BASE_URL:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700679 Usage(parser, 'Error: when using --private the --binhost-base-url '
David Jamese2488642011-11-14 16:15:20 -0800680 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700681
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700682 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800683
David Jamesc5cbd472012-06-19 16:25:45 -0700684def main(_argv):
David Jamesdb401072011-06-10 12:17:16 -0700685 # Set umask to a sane value so that files created as root are readable.
686 os.umask(022)
687
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700688 options, target = ParseOptions()
David Jamesc0f158a2011-02-22 16:07:29 -0800689
David James05bcb2b2011-02-09 09:25:47 -0800690 # Calculate a list of Packages index files to compare against. Whenever we
691 # upload a package, we check to make sure it's not already stored in one of
692 # the packages files we uploaded. This list of packages files might contain
693 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800694 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800695
David James8ece7ee2011-06-29 16:02:30 -0700696 if options.set_version:
697 version = options.set_version
698 else:
699 version = GetVersion()
David Jamesc0f158a2011-02-22 16:07:29 -0800700 if options.prepend_version:
701 version = '%s-%s' % (options.prepend_version, version)
702
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700703 acl = 'public-read'
704 binhost_base_url = options.binhost_base_url
705
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700706 if target and options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700707 binhost_base_url = options.upload
708 board_path = GetBoardPathFromCrosOverlayList(options.build_path,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700709 target)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700710 acl = os.path.join(board_path, _GOOGLESTORAGE_ACL_FILE)
711
712 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700713 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700714 options.packages, options.skip_upload,
David Jamese2488642011-11-14 16:15:20 -0800715 options.binhost_conf_dir, options.debug,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700716 target, options.slave_targets)
David Jamesc0f158a2011-02-22 16:07:29 -0800717
David James8c846492011-01-25 17:07:29 -0800718 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700719 uploader.SyncHostPrebuilts(version, options.key, options.git_sync,
720 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800721
Chris Sosa62c8ff52012-06-04 15:03:12 -0700722 if options.board or options.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700723 uploader.SyncBoardPrebuilts(version, options.key, options.git_sync,
724 options.sync_binhost_conf,
725 options.upload_board_tarball)