blob: e13333c911c1e7eb7987109bd1cf255b11813f48 [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
David James015af872012-06-19 15:24:36 -070028from chromite.buildbot import cbuildbot_background as bg
Zdenek Behan33a34112012-09-10 21:07:51 +020029from chromite.buildbot import constants
David Jamese5867812012-10-19 12:02:20 -070030from chromite.buildbot import portage_utilities
Chris Sosa1dc96132012-05-11 15:40:50 -070031from chromite.lib import cros_build_lib
Brian Harring7904b482012-08-08 02:54:12 -070032from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070033from chromite.lib import osutils
David James32faafe2012-06-08 14:25:03 -070034from chromite.lib import binpkg
Chris Sosa1dc96132012-05-11 15:40:50 -070035
David James015af872012-06-19 15:24:36 -070036# How many times to retry uploads.
37_RETRIES = 10
38
39# Multiplier for how long to sleep (in seconds) between retries; will delay
40# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
41_SLEEP_TIME = 60
42
David James8c846492011-01-25 17:07:29 -080043_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080044_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070045_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080046_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080047_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080048_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
49_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
Zdenek Behan33a34112012-09-10 21:07:51 +020050_SDK_GS_BUCKET = 'gs://chromiumos-sdk'
David James8c846492011-01-25 17:07:29 -080051# Private overlays to look at for builds to filter
52# relative to build path
53_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Scott Zawalskiab1bed32011-03-16 15:24:24 -070054_GOOGLESTORAGE_ACL_FILE = 'googlestorage_acl.xml'
David Jamesce619292011-11-08 11:42:36 -080055_BINHOST_BASE_URL = 'https://commondatastorage.googleapis.com/chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080056_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
57# Created in the event of new host targets becoming available
58_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
59 'make.conf.amd64-host')}
60_BINHOST_CONF_DIR = 'src/third_party/chromiumos-overlay/chromeos/binhost'
61
62
David James4058b0d2011-12-08 21:24:50 -080063class BuildTarget(object):
64 """A board/variant/profile tuple."""
65
66 def __init__(self, board_variant, profile=None):
67 self.board_variant = board_variant
68 self.board, _, self.variant = board_variant.partition('_')
69 self.profile = profile
70
71 def __str__(self):
72 if self.profile:
73 return '%s_%s' % (self.board_variant, self.profile)
74 else:
75 return self.board_variant
76
77 def __eq__(self, other):
78 return str(other) == str(self)
79
80 def __hash__(self):
81 return hash(str(self))
82
83
David James8c846492011-01-25 17:07:29 -080084def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
85 """Update the key in file with the value passed.
86 File format:
87 key="value"
88 Note quotes are added automatically
89
90 Args:
91 filename: Name of file to modify.
92 value: Value to write with the key.
93 key: The variable key to update. (Default: PORTAGE_BINHOST)
94 """
95 if os.path.exists(filename):
96 file_fh = open(filename)
97 else:
98 file_fh = open(filename, 'w+')
99 file_lines = []
100 found = False
101 keyval_str = '%(key)s=%(value)s'
102 for line in file_fh:
103 # Strip newlines from end of line. We already add newlines below.
104 line = line.rstrip("\n")
105
106 if len(line.split('=')) != 2:
107 # Skip any line that doesn't fit key=val.
108 file_lines.append(line)
109 continue
110
111 file_var, file_val = line.split('=')
112 if file_var == key:
113 found = True
David James20b2b6f2011-11-18 15:11:58 -0800114 print 'Updating %s=%s to %s="%s"' % (file_var, file_val, key, value)
David James8c846492011-01-25 17:07:29 -0800115 value = '"%s"' % value
116 file_lines.append(keyval_str % {'key': key, 'value': value})
117 else:
118 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
119
120 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700121 value = '"%s"' % value
David James8c846492011-01-25 17:07:29 -0800122 file_lines.append(keyval_str % {'key': key, 'value': value})
123
124 file_fh.close()
125 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700126 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David James8c846492011-01-25 17:07:29 -0800127
128
David James27fa7d12011-06-29 17:24:14 -0700129def RevGitFile(filename, value, retries=5, key='PORTAGE_BINHOST', dryrun=False):
David James8c846492011-01-25 17:07:29 -0800130 """Update and push the git file.
131
132 Args:
133 filename: file to modify that is in a git repo already
134 value: string representing the version of the prebuilt that has been
135 uploaded.
136 retries: The number of times to retry before giving up, default: 5
137 key: The variable key to update in the git file.
138 (Default: PORTAGE_BINHOST)
139 """
140 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700141 cwd = os.path.abspath(os.path.dirname(filename))
Brian Harring609dc4e2012-05-07 02:17:44 -0700142 commit = cros_build_lib.RunGitCommand(
143 cwd, ['rev-parse', 'HEAD']).output.rstrip()
David James8c846492011-01-25 17:07:29 -0800144 description = 'Update %s="%s" in %s' % (key, value, filename)
145 print description
David James66009462012-03-25 10:08:38 -0700146
David James8c846492011-01-25 17:07:29 -0800147 try:
David James66009462012-03-25 10:08:38 -0700148 cros_build_lib.CreatePushBranch(prebuilt_branch, cwd)
David James8c846492011-01-25 17:07:29 -0800149 UpdateLocalFile(filename, value, key)
Brian Harring609dc4e2012-05-07 02:17:44 -0700150 cros_build_lib.RunGitCommand(cwd, ['add', filename])
151 cros_build_lib.RunGitCommand(cwd, ['commit', '-m', description])
152 cros_build_lib.GitPushWithRetry(prebuilt_branch, cwd, dryrun=dryrun,
David James66009462012-03-25 10:08:38 -0700153 retries=retries)
David James8c846492011-01-25 17:07:29 -0800154 finally:
David James1b6e67a2011-05-19 21:32:38 -0700155 cros_build_lib.RunCommand(['git', 'checkout', commit], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800156
157
158def GetVersion():
159 """Get the version to put in LATEST and update the git version with."""
160 return datetime.datetime.now().strftime('%d.%m.%y.%H%M%S')
161
162
David James015af872012-06-19 15:24:36 -0700163def _GsUpload(local_file, remote_file, acl):
David James8c846492011-01-25 17:07:29 -0800164 """Upload to GS bucket.
165
166 Args:
David Jamesfd0b0852011-02-23 11:15:36 -0800167 args: a tuple of three arguments that contains local_file, remote_file, and
168 the acl used for uploading the file.
David James8c846492011-01-25 17:07:29 -0800169
170 Returns:
171 Return the arg tuple of two if the upload failed
172 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700173 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
174 'authenticated-read', 'bucket-owner-full-control',
175 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700176 if acl in CANNED_ACLS:
Brian Harring7904b482012-08-08 02:54:12 -0700177 cmd = [gs.GSUTIL_BIN, 'cp', '-a', acl, local_file, remote_file]
David James015af872012-06-19 15:24:36 -0700178 acl_cmd = None
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700179 else:
180 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700181 # and a googlestore_acl.xml is present. Otherwise, this script errors.
Brian Harring7904b482012-08-08 02:54:12 -0700182 cmd = [gs.GSUTIL_BIN, 'cp', '-a', 'private', local_file, remote_file]
183 acl_cmd = [gs.GSUTIL_BIN, 'setacl', acl, remote_file]
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700184
David Jamesc5cbd472012-06-19 16:25:45 -0700185 cros_build_lib.RunCommandWithRetries(_RETRIES, cmd, print_cmd=True,
186 sleep=_SLEEP_TIME,
187 redirect_stdout=True,
188 redirect_stderr=True)
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
David Jamese5867812012-10-19 12:02:20 -0700235def GetBoardOverlay(build_path, target):
236 """Get the path to the board variant.
David James8c846492011-01-25 17:07:29 -0800237 Args:
238 build_path: The path to the root of the build directory
David Jamese5867812012-10-19 12:02:20 -0700239 target: The target board as a BuildTarget object.
David James8c846492011-01-25 17:07:29 -0800240 Returns:
David Jamese5867812012-10-19 12:02:20 -0700241 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800242 """
David Jamese5867812012-10-19 12:02:20 -0700243 board = target.board_variant
244 overlays = portage_utilities.FindOverlays(constants.BOTH_OVERLAYS, board,
245 buildroot=build_path)
246 # We only care about the last entry.
247 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800248
249
250def DeterminePrebuiltConfFile(build_path, target):
251 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
252
253 Args:
254 build_path: The path to the root of the build directory
255 target: String representation of the board. This includes host and board
256 targets
257
258 Returns
259 A string path to a prebuilt.conf file to be updated.
260 """
David James4058b0d2011-12-08 21:24:50 -0800261 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800262 # We are host.
263 # Without more examples of hosts this is a kludge for now.
264 # TODO(Scottz): as new host targets come online expand this to
265 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800266 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800267 else:
268 # We are a board
David Jamese5867812012-10-19 12:02:20 -0700269 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800270 make_path = os.path.join(board, 'prebuilt.conf')
271
272 return make_path
273
274
275def UpdateBinhostConfFile(path, key, value):
276 """Update binhost config file file with key=value.
277
278 Args:
279 path: Filename to update.
280 key: Key to update.
281 value: New value for key.
282 """
283 cwd = os.path.dirname(os.path.abspath(path))
284 filename = os.path.basename(path)
Brian Harringaf019fb2012-05-10 15:06:13 -0700285 osutils.SafeMakedirs(cwd)
Brian Harring22edb442012-05-11 23:55:18 -0700286 osutils.WriteFile(path, '', mode='a')
David James8c846492011-01-25 17:07:29 -0800287 UpdateLocalFile(path, value, key)
Chris Sosac13bba52011-05-24 15:14:09 -0700288 cros_build_lib.RunCommand(['git', 'add', filename], cwd=cwd)
David James20b2b6f2011-11-18 15:11:58 -0800289 description = 'Update %s="%s" in %s' % (key, value, filename)
Peter Mayo193f68f2011-04-19 19:08:21 -0400290 cros_build_lib.RunCommand(['git', 'commit', '-m', description], cwd=cwd)
David James8c846492011-01-25 17:07:29 -0800291
292
David Jamesce093af2011-02-23 15:21:58 -0800293def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800294 """Grab all of the packages files associated with a list of binhost_urls.
295
David James05bcb2b2011-02-09 09:25:47 -0800296 Args:
297 binhost_urls: The URLs for the directories containing the Packages files we
298 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800299
300 Returns:
301 A list of PackageIndex objects.
302 """
303 pkg_indexes = []
304 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700305 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800306 if pkg_index:
307 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800308 return pkg_indexes
309
310
David James05bcb2b2011-02-09 09:25:47 -0800311
David Jamesc0f158a2011-02-22 16:07:29 -0800312class PrebuiltUploader(object):
313 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800314
David James615e5b52011-06-03 11:10:15 -0700315 def __init__(self, upload_location, acl, binhost_base_url,
David James32b0b2f2011-07-13 20:56:50 -0700316 pkg_indexes, build_path, packages, skip_upload,
David James4058b0d2011-12-08 21:24:50 -0800317 binhost_conf_dir, debug, target, slave_targets):
David Jamesc0f158a2011-02-22 16:07:29 -0800318 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800319
David Jamesc0f158a2011-02-22 16:07:29 -0800320 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800321
David Jamesc0f158a2011-02-22 16:07:29 -0800322 Args:
323 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800324 acl: The canned acl used for uploading to Google Storage. acl can be one
325 of: "public-read", "public-read-write", "authenticated-read",
326 "bucket-owner-read", "bucket-owner-full-control", or "private". If
327 we are not uploading to Google Storage, this parameter is unused.
328 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800329 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
330 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700331 build_path: The path to the directory containing the chroot.
332 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700333 skip_upload: Don't actually upload the tarballs.
334 binhost_conf_dir: Directory where to store binhost.conf files.
335 debug: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800336 target: BuildTarget managed by this builder.
337 slave_targets: List of BuildTargets managed by slave builders.
David Jamesc0f158a2011-02-22 16:07:29 -0800338 """
339 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800340 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800341 self._binhost_base_url = binhost_base_url
342 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700343 self._build_path = build_path
344 self._packages = set(packages)
David James8ece7ee2011-06-29 16:02:30 -0700345 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700346 self._binhost_conf_dir = binhost_conf_dir
David James27fa7d12011-06-29 17:24:14 -0700347 self._debug = debug
David James4058b0d2011-12-08 21:24:50 -0800348 self._target = target
349 self._slave_targets = slave_targets
David James615e5b52011-06-03 11:10:15 -0700350
351 def _ShouldFilterPackage(self, pkg):
352 if not self._packages:
353 return False
354 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800355 sys.path.insert(0, pym_path)
David James615e5b52011-06-03 11:10:15 -0700356 import portage.versions
357 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
358 cp = '%s/%s' % (cat, pkgname)
359 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800360
David Jamesc0f158a2011-02-22 16:07:29 -0800361 def _UploadPrebuilt(self, package_path, url_suffix):
362 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800363
David Jamesc0f158a2011-02-22 16:07:29 -0800364 Args:
365 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800366 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesa3bba142011-05-26 21:24:20 -0700367
David Jamesc0f158a2011-02-22 16:07:29 -0800368 """
David James8c846492011-01-25 17:07:29 -0800369
David Jamesc0f158a2011-02-22 16:07:29 -0800370 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700371 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800372 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700373 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800374 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
David James05bcb2b2011-02-09 09:25:47 -0800375
David Jamesc0f158a2011-02-22 16:07:29 -0800376 # Write Packages file.
377 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800378
David Jamesc0f158a2011-02-22 16:07:29 -0800379 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700380 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800381
David James015af872012-06-19 15:24:36 -0700382 # Build list of files to upload.
383 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
384 remote_file = '%s/Packages' % remote_location.rstrip('/')
385 upload_files[tmp_packages_file.name] = remote_file
386
387 RemoteUpload(self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800388
Zdenek Behan62a57792012-08-31 15:09:08 +0200389 def _UploadBoardTarball(self, board_path, url_suffix, version, prepackaged):
David James8fa34ea2011-04-15 13:00:20 -0700390 """Upload a tarball of the board at the specified path to Google Storage.
391
392 Args:
393 board_path: The path to the board dir.
394 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan5ad96c02011-06-23 01:04:06 +0200395 version: The version of the board.
Zdenek Behan62a57792012-08-31 15:09:08 +0200396 prepackaged: If given, a tarball that has been packaged outside of this
397 script and should be used.
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://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200401 boardname = os.path.basename(board_path.rstrip('/'))
402 # We do not upload non SDK board tarballs,
403 assert boardname == constants.CHROOT_BUILDER_BOARD
404 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200405
Zdenek Behan86c15e92012-10-13 00:55:47 +0200406 version_str = version[len('chroot-'):]
407 remote_tarfile = \
408 '%s/cros-sdk-%s.tar.xz' % (_SDK_GS_BUCKET, version_str)
409 # For SDK, also upload the manifest which is guaranteed to exist
410 # by the builderstage.
411 _GsUpload(prepackaged + '.Manifest', remote_tarfile + '.Manifest',
412 self._acl)
413 _GsUpload(prepackaged, remote_tarfile, self._acl)
414
415 # Finally, also update the pointer to the latest SDK on which polling
416 # scripts rely.
417 with osutils.TempDirContextManager() as tmpdir:
418 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
419 remote_pointerfile = '%s/cros-sdk-latest.conf' % _SDK_GS_BUCKET
420 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])
David Jamese2488642011-11-14 16:15:20 -0800474 RevGitFile(git_file, binhost, key=key, 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,
David James8ece7ee2011-06-29 16:02:30 -0700521 'host/sdk_version.conf')
Zdenek Behan33a34112012-09-10 21:07:51 +0200522 RevGitFile(sdk_conf, version[len('chroot-'):],
David Jamese2488642011-11-14 16:15:20 -0800523 key='SDK_LATEST_VERSION', dryrun=self._debug)
David Jamesc0f158a2011-02-22 16:07:29 -0800524
David Jamese2488642011-11-14 16:15:20 -0800525 # Record URL where prebuilts were uploaded.
526 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
527 packages_url_suffix.rstrip('/'))
528
529 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800530 git_file = DeterminePrebuiltConfFile(self._build_path, target)
David Jamese2488642011-11-14 16:15:20 -0800531 RevGitFile(git_file, url_value, key=key, dryrun=self._debug)
532 if sync_binhost_conf:
533 binhost_conf = os.path.join(self._build_path, self._binhost_conf_dir,
David James4058b0d2011-12-08 21:24:50 -0800534 'target', '%s-%s.conf' % (target, key))
David Jamese2488642011-11-14 16:15:20 -0800535 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800536
537
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700538def Usage(parser, msg):
David James8c846492011-01-25 17:07:29 -0800539 """Display usage message and parser help then exit with 1."""
540 print >> sys.stderr, msg
541 parser.print_help()
542 sys.exit(1)
543
David James4058b0d2011-12-08 21:24:50 -0800544
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700545def _AddSlaveBoard(_option, _opt_str, value, parser):
546 """Callback that adds a slave board to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800547 parser.values.slave_targets.append(BuildTarget(value))
548
549
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700550def _AddSlaveProfile(_option, _opt_str, value, parser):
551 """Callback that adds a slave profile to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800552 if not parser.values.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700553 Usage(parser, 'Must specify --slave-board before --slave-profile')
David James4058b0d2011-12-08 21:24:50 -0800554 if parser.values.slave_targets[-1].profile is not None:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700555 Usage(parser, 'Cannot specify --slave-profile twice for same board')
David James4058b0d2011-12-08 21:24:50 -0800556 parser.values.slave_targets[-1].profile = value
557
558
David Jamesc0f158a2011-02-22 16:07:29 -0800559def ParseOptions():
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700560 """Returns options given by the user and the target specified.
561
562 Returns a tuple containing a parsed options object and BuildTarget.
563 target instance is None if no board is specified.
564 """
David James8c846492011-01-25 17:07:29 -0800565 parser = optparse.OptionParser()
566 parser.add_option('-H', '--binhost-base-url', dest='binhost_base_url',
567 default=_BINHOST_BASE_URL,
568 help='Base URL to use for binhost in make.conf updates')
569 parser.add_option('', '--previous-binhost-url', action='append',
570 default=[], dest='previous_binhost_url',
571 help='Previous binhost URL')
572 parser.add_option('-b', '--board', dest='board', default=None,
573 help='Board type that was built on this machine')
Zdenek Behan62a57792012-08-31 15:09:08 +0200574 parser.add_option('-B', '--prepackaged-tarball', dest='prepackaged_tarball',
575 default=None,
576 help='Board tarball prebuilt outside of this script.')
David James4058b0d2011-12-08 21:24:50 -0800577 parser.add_option('', '--profile', dest='profile', default=None,
578 help='Profile that was built on this machine')
579 parser.add_option('', '--slave-board', default=[], action='callback',
580 dest='slave_targets', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700581 callback=_AddSlaveBoard,
David James4058b0d2011-12-08 21:24:50 -0800582 help='Board type that was built on a slave machine. To '
583 'add a profile to this board, use --slave-profile.')
584 parser.add_option('', '--slave-profile', action='callback', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700585 callback=_AddSlaveProfile,
David James4058b0d2011-12-08 21:24:50 -0800586 help='Board profile that was built on a slave machine. '
587 'Applies to previous slave board.')
David James8c846492011-01-25 17:07:29 -0800588 parser.add_option('-p', '--build-path', dest='build_path',
David James05bcb2b2011-02-09 09:25:47 -0800589 help='Path to the directory containing the chroot')
David James615e5b52011-06-03 11:10:15 -0700590 parser.add_option('', '--packages', action='append',
591 default=[], dest='packages',
592 help='Only include the specified packages. '
593 '(Default is to include all packages.)')
David James8c846492011-01-25 17:07:29 -0800594 parser.add_option('-s', '--sync-host', dest='sync_host',
595 default=False, action='store_true',
596 help='Sync host prebuilts')
597 parser.add_option('-g', '--git-sync', dest='git_sync',
598 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800599 help='Enable git version sync (This commits to a repo.) '
600 'This is used by full builders to commit directly '
601 'to board overlays.')
David James8c846492011-01-25 17:07:29 -0800602 parser.add_option('-u', '--upload', dest='upload',
603 default=None,
604 help='Upload location')
605 parser.add_option('-V', '--prepend-version', dest='prepend_version',
606 default=None,
607 help='Add an identifier to the front of the version')
608 parser.add_option('-f', '--filters', dest='filters', action='store_true',
609 default=False,
610 help='Turn on filtering of private ebuild packages')
611 parser.add_option('-k', '--key', dest='key',
612 default='PORTAGE_BINHOST',
613 help='Key to update in make.conf / binhost.conf')
David James8ece7ee2011-06-29 16:02:30 -0700614 parser.add_option('', '--set-version', dest='set_version',
615 default=None,
616 help='Specify the version string')
David James8c846492011-01-25 17:07:29 -0800617 parser.add_option('', '--sync-binhost-conf', dest='sync_binhost_conf',
618 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800619 help='Update binhost.conf in chromiumos-overlay or '
620 'chromeos-overlay. Commit the changes, but don\'t '
621 'push them. This is used for preflight binhosts.')
David James32b0b2f2011-07-13 20:56:50 -0700622 parser.add_option('', '--binhost-conf-dir', dest='binhost_conf_dir',
623 default=_BINHOST_CONF_DIR,
624 help='Directory to commit binhost config with '
625 '--sync-binhost-conf.')
David Jamesfd0b0852011-02-23 11:15:36 -0800626 parser.add_option('-P', '--private', dest='private', action='store_true',
627 default=False, help='Mark gs:// uploads as private.')
David James8ece7ee2011-06-29 16:02:30 -0700628 parser.add_option('', '--skip-upload', dest='skip_upload',
629 action='store_true', default=False,
630 help='Skip upload step.')
David James8fa34ea2011-04-15 13:00:20 -0700631 parser.add_option('', '--upload-board-tarball', dest='upload_board_tarball',
632 action='store_true', default=False,
633 help='Upload board tarball to Google Storage.')
David James27fa7d12011-06-29 17:24:14 -0700634 parser.add_option('', '--debug', dest='debug',
635 action='store_true', default=False,
636 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800637
638 options, args = parser.parse_args()
David James8c846492011-01-25 17:07:29 -0800639 if not options.build_path:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700640 Usage(parser, 'Error: you need provide a chroot path')
David James8ece7ee2011-06-29 16:02:30 -0700641 if not options.upload and not options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700642 Usage(parser, 'Error: you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700643 if not options.set_version and options.skip_upload:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700644 Usage(parser, 'Error: If you are using --skip-upload, you must specify a '
David James8ece7ee2011-06-29 16:02:30 -0700645 'version number using --set-version.')
David James9417f272011-05-26 13:24:47 -0700646 if args:
David Jamesc5cbd472012-06-19 16:25:45 -0700647 Usage(parser, 'Error: invalid arguments passed to upload_prebuilts: '
648 '%r' % args)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700649
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700650 target = None
651 if options.board:
652 target = BuildTarget(options.board, options.profile)
653
654 if target in options.slave_targets:
655 Usage(parser, 'Error: --board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800656
David James4058b0d2011-12-08 21:24:50 -0800657 if len(set(options.slave_targets)) != len(options.slave_targets):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700658 Usage(parser, 'Error: --slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800659
David James4058b0d2011-12-08 21:24:50 -0800660 if options.slave_targets and options.git_sync:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700661 Usage(parser, 'Error: --slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800662
David James8ece7ee2011-06-29 16:02:30 -0700663 if (options.upload_board_tarball and options.skip_upload and
664 options.board == 'amd64-host'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700665 Usage(parser, 'Error: --skip-upload is not compatible with '
David James8ece7ee2011-06-29 16:02:30 -0700666 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700667
David James8ece7ee2011-06-29 16:02:30 -0700668 if (options.upload_board_tarball and not options.skip_upload and
669 not options.upload.startswith('gs://')):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700670 Usage(parser, 'Error: --upload-board-tarball only works with gs:// URLs.\n'
David James8fa34ea2011-04-15 13:00:20 -0700671 '--upload must be a gs:// URL.')
672
Zdenek Behan86c15e92012-10-13 00:55:47 +0200673 if options.upload_board_tarball and options.prepackaged_tarball is None:
674 Usage(parser, 'Error: --upload-board-tarball requires '
675 '--prepackaged-tarball.')
676
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700677 if options.private:
678 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700679 Usage(parser, 'Error: --private and --sync-host/-s cannot be specified '
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700680 'together, we do not support private host prebuilts')
681
David James8ece7ee2011-06-29 16:02:30 -0700682 if not options.upload or not options.upload.startswith('gs://'):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700683 Usage(parser, 'Error: --private is only valid for gs:// URLs.\n'
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700684 '--upload must be a gs:// URL.')
685
686 if options.binhost_base_url != _BINHOST_BASE_URL:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700687 Usage(parser, 'Error: when using --private the --binhost-base-url '
David Jamese2488642011-11-14 16:15:20 -0800688 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700689
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700690 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800691
David Jamesc5cbd472012-06-19 16:25:45 -0700692def main(_argv):
David Jamesdb401072011-06-10 12:17:16 -0700693 # Set umask to a sane value so that files created as root are readable.
694 os.umask(022)
695
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700696 options, target = ParseOptions()
David Jamesc0f158a2011-02-22 16:07:29 -0800697
David James05bcb2b2011-02-09 09:25:47 -0800698 # Calculate a list of Packages index files to compare against. Whenever we
699 # upload a package, we check to make sure it's not already stored in one of
700 # the packages files we uploaded. This list of packages files might contain
701 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800702 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800703
David James8ece7ee2011-06-29 16:02:30 -0700704 if options.set_version:
705 version = options.set_version
706 else:
707 version = GetVersion()
David Jamesc0f158a2011-02-22 16:07:29 -0800708 if options.prepend_version:
709 version = '%s-%s' % (options.prepend_version, version)
710
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700711 acl = 'public-read'
712 binhost_base_url = options.binhost_base_url
713
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700714 if target and options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700715 binhost_base_url = options.upload
David Jamese5867812012-10-19 12:02:20 -0700716 board_path = GetBoardOverlay(options.build_path, target)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700717 acl = os.path.join(board_path, _GOOGLESTORAGE_ACL_FILE)
718
719 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700720 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700721 options.packages, options.skip_upload,
David Jamese2488642011-11-14 16:15:20 -0800722 options.binhost_conf_dir, options.debug,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700723 target, options.slave_targets)
David Jamesc0f158a2011-02-22 16:07:29 -0800724
David James8c846492011-01-25 17:07:29 -0800725 if options.sync_host:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700726 uploader.SyncHostPrebuilts(version, options.key, options.git_sync,
727 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800728
Chris Sosa62c8ff52012-06-04 15:03:12 -0700729 if options.board or options.slave_targets:
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700730 uploader.SyncBoardPrebuilts(version, options.key, options.git_sync,
731 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200732 options.upload_board_tarball,
733 options.prepackaged_tarball)