blob: 2dee97476b260075c5a3fa2e846ca360b1bbc95d [file] [log] [blame]
Don Garrett13cbbff2016-08-09 14:18:38 -07001 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
David James8c846492011-01-25 17:07:29 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Brian Harringaf019fb2012-05-10 15:06:13 -07005"""This script is used to upload host prebuilts as well as board BINHOSTS.
David James8c846492011-01-25 17:07:29 -08006
David James015af872012-06-19 15:24:36 -07007Prebuilts are uploaded using gsutil to Google Storage. After these prebuilts
8are successfully uploaded, a file is updated with the proper BINHOST version.
David James8c846492011-01-25 17:07:29 -08009
10To read more about prebuilts/binhost binary packages please refer to:
David James015af872012-06-19 15:24:36 -070011http://goto/chromeos-prebuilts
David James8c846492011-01-25 17:07:29 -080012
13Example of uploading prebuilt amd64 host files to Google Storage:
David Jamesc5cbd472012-06-19 16:25:45 -070014upload_prebuilts -p /b/cbuild/build -s -u gs://chromeos-prebuilt
David James8c846492011-01-25 17:07:29 -080015
16Example of uploading x86-dogfood binhosts to Google Storage:
David Jamesc5cbd472012-06-19 16:25:45 -070017upload_prebuilts -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
David James8c846492011-01-25 17:07:29 -080018"""
19
Mike Frysingerff441bf2014-05-24 13:47:21 -040020from __future__ import print_function
21
Mike Frysingerb87bb782015-06-04 02:46:50 -040022import argparse
Chris Sosa1dc96132012-05-11 15:40:50 -070023import datetime
Mike Frysinger540883b2014-05-24 13:46:16 -040024import functools
David Jamesb26b9312014-12-15 11:26:46 -080025import glob
Chris Sosa1dc96132012-05-11 15:40:50 -070026import multiprocessing
Chris Sosa1dc96132012-05-11 15:40:50 -070027import os
28import sys
Mike Frysinger212e4292014-05-24 15:15:44 -040029import tempfile
Chris Sosa1dc96132012-05-11 15:40:50 -070030
Aviv Keshetb7519e12016-10-04 00:50:00 -070031from chromite.lib import constants
David James14e97772014-06-04 18:44:49 -070032from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080033from chromite.lib import binpkg
Mike Frysinger86509232014-05-24 13:18:37 -040034from chromite.lib import commandline
Chris Sosa1dc96132012-05-11 15:40:50 -070035from chromite.lib import cros_build_lib
Ralph Nathan446aee92015-03-23 14:44:56 -070036from chromite.lib import cros_logging as logging
David James97d95872012-11-16 15:09:56 -080037from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070038from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070039from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080040from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070041from chromite.lib import portage_util
Mike Frysingercdd98762017-09-12 00:05:37 -040042from chromite.lib import retry_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050043from chromite.lib import toolchain
Chris Sosa1dc96132012-05-11 15:40:50 -070044
David James015af872012-06-19 15:24:36 -070045# How many times to retry uploads.
46_RETRIES = 10
47
48# Multiplier for how long to sleep (in seconds) between retries; will delay
49# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
50_SLEEP_TIME = 60
51
David James5ab67e32014-10-24 08:19:59 -070052# The length of time (in seconds) that Portage should wait before refetching
53# binpkgs from the same binhost. We don't ever modify binhosts, so this should
54# be something big.
55_BINPKG_TTL = 60 * 60 * 24 * 365
56
David James8c846492011-01-25 17:07:29 -080057_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080058_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070059_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080060_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080061_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080062_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
63_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080064# Private overlays to look at for builds to filter
65# relative to build path
66_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Gabe Black40169e62014-06-17 15:23:47 -070067_GOOGLESTORAGE_GSUTIL_FILE = 'googlestorage_acl.txt'
David James3753d942014-04-23 10:55:48 -070068_BINHOST_BASE_URL = 'gs://chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080069_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
70# Created in the event of new host targets becoming available
71_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
72 'make.conf.amd64-host')}
David James8c846492011-01-25 17:07:29 -080073
74
David James4058b0d2011-12-08 21:24:50 -080075class BuildTarget(object):
76 """A board/variant/profile tuple."""
77
78 def __init__(self, board_variant, profile=None):
79 self.board_variant = board_variant
80 self.board, _, self.variant = board_variant.partition('_')
81 self.profile = profile
82
83 def __str__(self):
84 if self.profile:
85 return '%s_%s' % (self.board_variant, self.profile)
86 else:
87 return self.board_variant
88
89 def __eq__(self, other):
90 return str(other) == str(self)
91
92 def __hash__(self):
93 return hash(str(self))
94
95
David James8c846492011-01-25 17:07:29 -080096def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
97 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -050098
David James8c846492011-01-25 17:07:29 -080099 File format:
100 key="value"
101 Note quotes are added automatically
102
103 Args:
104 filename: Name of file to modify.
105 value: Value to write with the key.
106 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800107
108 Returns:
109 True if changes were made to the file.
David James8c846492011-01-25 17:07:29 -0800110 """
111 if os.path.exists(filename):
112 file_fh = open(filename)
113 else:
114 file_fh = open(filename, 'w+')
115 file_lines = []
116 found = False
David Jamesb26b9312014-12-15 11:26:46 -0800117 made_changes = False
David James8c846492011-01-25 17:07:29 -0800118 keyval_str = '%(key)s=%(value)s'
119 for line in file_fh:
120 # Strip newlines from end of line. We already add newlines below.
121 line = line.rstrip("\n")
122
123 if len(line.split('=')) != 2:
124 # Skip any line that doesn't fit key=val.
125 file_lines.append(line)
126 continue
127
128 file_var, file_val = line.split('=')
129 if file_var == key:
130 found = True
Mike Frysingerff441bf2014-05-24 13:47:21 -0400131 print('Updating %s=%s to %s="%s"' % (file_var, file_val, key, value))
David James8c846492011-01-25 17:07:29 -0800132 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800133 made_changes |= (file_val != value)
David James8c846492011-01-25 17:07:29 -0800134 file_lines.append(keyval_str % {'key': key, 'value': value})
135 else:
136 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
137
138 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700139 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800140 made_changes = True
David James8c846492011-01-25 17:07:29 -0800141 file_lines.append(keyval_str % {'key': key, 'value': value})
142
143 file_fh.close()
144 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700145 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David Jamesb26b9312014-12-15 11:26:46 -0800146 return made_changes
David James8c846492011-01-25 17:07:29 -0800147
148
Mike Frysinger5b34d732013-01-17 15:11:58 -0500149def RevGitFile(filename, data, retries=5, dryrun=False):
David James8c846492011-01-25 17:07:29 -0800150 """Update and push the git file.
151
Mike Frysinger5b34d732013-01-17 15:11:58 -0500152 Args:
153 filename: file to modify that is in a git repo already
154 data: A dict of key/values to update in |filename|
155 retries: The number of times to retry before giving up, default: 5
Matt Tennante8179042013-10-01 15:47:32 -0700156 dryrun: If True, do not actually commit the change.
David James8c846492011-01-25 17:07:29 -0800157 """
158 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700159 cwd = os.path.abspath(os.path.dirname(filename))
David James97d95872012-11-16 15:09:56 -0800160 commit = git.RunGit(cwd, ['rev-parse', 'HEAD']).output.rstrip()
Mike Frysinger5b34d732013-01-17 15:11:58 -0500161 description = '%s: updating %s' % (os.path.basename(filename),
162 ', '.join(data.keys()))
163 # UpdateLocalFile will print out the keys/values for us.
Mike Frysingerff441bf2014-05-24 13:47:21 -0400164 print('Revving git file %s' % filename)
David James66009462012-03-25 10:08:38 -0700165
David James8c846492011-01-25 17:07:29 -0800166 try:
David James97d95872012-11-16 15:09:56 -0800167 git.CreatePushBranch(prebuilt_branch, cwd)
Mike Frysinger5b34d732013-01-17 15:11:58 -0500168 for key, value in data.iteritems():
169 UpdateLocalFile(filename, value, key)
David James97d95872012-11-16 15:09:56 -0800170 git.RunGit(cwd, ['add', filename])
171 git.RunGit(cwd, ['commit', '-m', description])
172 git.PushWithRetry(prebuilt_branch, cwd, dryrun=dryrun, retries=retries)
David James8c846492011-01-25 17:07:29 -0800173 finally:
Hidehiko Abecb26e282017-08-22 16:28:16 +0900174 # We reset the index and the working tree state in case there are any
175 # uncommitted or pending changes, but we don't change any existing commits.
176 git.RunGit(cwd, ['reset', '--hard'])
177
178 # Check out the last good commit as a sanity fallback.
David James67d73252013-09-19 17:33:12 -0700179 git.RunGit(cwd, ['checkout', commit])
David James8c846492011-01-25 17:07:29 -0800180
181
182def GetVersion():
183 """Get the version to put in LATEST and update the git version with."""
Mike Frysinger0b92cfe2012-12-13 02:13:45 -0500184 return datetime.datetime.now().strftime('%Y.%m.%d.%H%M%S')
David James8c846492011-01-25 17:07:29 -0800185
186
Mike Frysinger540883b2014-05-24 13:46:16 -0400187def _GsUpload(gs_context, acl, local_file, remote_file):
David James8c846492011-01-25 17:07:29 -0800188 """Upload to GS bucket.
189
190 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400191 gs_context: A lib.gs.GSContext instance.
192 acl: The ACL to use for uploading the file.
Matt Tennante8179042013-10-01 15:47:32 -0700193 local_file: The local file to be uploaded.
194 remote_file: The remote location to upload to.
David James8c846492011-01-25 17:07:29 -0800195 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700196 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
197 'authenticated-read', 'bucket-owner-full-control',
198 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700199 if acl in CANNED_ACLS:
David James9374aac2013-10-08 16:00:17 -0700200 gs_context.Copy(local_file, remote_file, acl=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700201 else:
202 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700203 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James7b7d7c32015-04-02 13:48:17 -0700204 # We set version=0 here to ensure that the ACL is set only once (see
205 # http://b/15883752#comment54).
206 try:
207 gs_context.Copy(local_file, remote_file, version=0)
208 except gs.GSContextPreconditionFailed as ex:
209 # If we received a GSContextPreconditionFailed error, we know that the
210 # file exists now, but we don't know whether our specific update
211 # succeeded. See http://b/15883752#comment62
212 logging.warning(
213 'Assuming upload succeeded despite PreconditionFailed errors: %s', ex)
214
Gabe Black40169e62014-06-17 15:23:47 -0700215 if acl.endswith('.xml'):
216 # Apply the passed in ACL xml file to the uploaded object.
217 gs_context.SetACL(remote_file, acl=acl)
218 else:
Mike Frysingercdd98762017-09-12 00:05:37 -0400219 # Some versions of gsutil bubble up precondition failures even when we
220 # didn't request it due to how ACL changes happen internally to gsutil.
221 # https://crbug.com/763450
222 retry_util.RetryException(
223 gs.GSContextPreconditionFailed, 3, gs_context.ChangeACL,
224 remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700225
Mike Frysingercc838832014-05-24 13:10:30 -0400226
Mike Frysinger540883b2014-05-24 13:46:16 -0400227def RemoteUpload(gs_context, acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800228 """Upload to google storage.
229
230 Create a pool of process and call _GsUpload with the proper arguments.
231
232 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400233 gs_context: A lib.gs.GSContext instance.
David Jamesfd0b0852011-02-23 11:15:36 -0800234 acl: The canned acl used for uploading. acl can be one of: "public-read",
235 "public-read-write", "authenticated-read", "bucket-owner-read",
236 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800237 files: dictionary with keys to local files and values to remote path.
238 pool: integer of maximum proesses to have at the same time.
239
240 Returns:
241 Return a set of tuple arguments of the failed uploads
242 """
Mike Frysinger540883b2014-05-24 13:46:16 -0400243 upload = functools.partial(_GsUpload, gs_context, acl)
244 tasks = [[key, value] for key, value in files.iteritems()]
245 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800246
247
248def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
249 """Build a dictionary of local remote file key pairs to upload.
250
251 Args:
252 base_local_path: The base path to the files on the local hard drive.
Matt Tennante8179042013-10-01 15:47:32 -0700253 base_remote_path: The base path to the remote paths.
David James8c846492011-01-25 17:07:29 -0800254 pkgs: The packages to upload.
255
256 Returns:
257 Returns a dictionary of local_path/remote_path pairs
258 """
259 upload_files = {}
260 for pkg in pkgs:
261 suffix = pkg['CPV'] + '.tbz2'
262 local_path = os.path.join(base_local_path, suffix)
David James7b7d7c32015-04-02 13:48:17 -0700263 assert os.path.exists(local_path), '%s does not exist' % local_path
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800264 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800265
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800266 if pkg.get('DEBUG_SYMBOLS') == 'yes':
267 debugsuffix = pkg['CPV'] + '.debug.tbz2'
268 local_path = os.path.join(base_local_path, debugsuffix)
269 assert os.path.exists(local_path)
270 upload_files[local_path] = os.path.join(base_remote_path, debugsuffix)
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800271
David James8c846492011-01-25 17:07:29 -0800272 return upload_files
273
Mike Frysingercc838832014-05-24 13:10:30 -0400274
Peter Mayo950e41a2014-02-06 21:07:33 +0000275def GetBoardOverlay(build_path, target):
David Jamese5867812012-10-19 12:02:20 -0700276 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500277
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400278 Args:
279 build_path: The path to the root of the build directory
280 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500281
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400282 Returns:
283 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800284 """
David Jamese5867812012-10-19 12:02:20 -0700285 board = target.board_variant
Alex Deymo075c2292014-09-04 18:31:50 -0700286 overlays = portage_util.FindOverlays(constants.BOTH_OVERLAYS, board,
287 buildroot=build_path)
David Jamese5867812012-10-19 12:02:20 -0700288 # We only care about the last entry.
289 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800290
291
292def DeterminePrebuiltConfFile(build_path, target):
293 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
294
Mike Frysinger1a736a82013-12-12 01:50:59 -0500295 Args:
296 build_path: The path to the root of the build directory
297 target: String representation of the board. This includes host and board
298 targets
David James8c846492011-01-25 17:07:29 -0800299
Mike Frysinger1a736a82013-12-12 01:50:59 -0500300 Returns:
301 A string path to a prebuilt.conf file to be updated.
David James8c846492011-01-25 17:07:29 -0800302 """
David James4058b0d2011-12-08 21:24:50 -0800303 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800304 # We are host.
305 # Without more examples of hosts this is a kludge for now.
306 # TODO(Scottz): as new host targets come online expand this to
307 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800308 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800309 else:
310 # We are a board
Peter Mayo950e41a2014-02-06 21:07:33 +0000311 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800312 make_path = os.path.join(board, 'prebuilt.conf')
313
314 return make_path
315
316
317def UpdateBinhostConfFile(path, key, value):
318 """Update binhost config file file with key=value.
319
320 Args:
321 path: Filename to update.
322 key: Key to update.
323 value: New value for key.
324 """
David Jamesb26b9312014-12-15 11:26:46 -0800325 cwd, filename = os.path.split(os.path.abspath(path))
Brian Harringaf019fb2012-05-10 15:06:13 -0700326 osutils.SafeMakedirs(cwd)
David Jamesf6e8fb72013-05-10 08:58:43 -0700327 if not git.GetCurrentBranch(cwd):
328 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
Brian Harring22edb442012-05-11 23:55:18 -0700329 osutils.WriteFile(path, '', mode='a')
David Jamesb26b9312014-12-15 11:26:46 -0800330 if UpdateLocalFile(path, value, key):
331 desc = '%s: %s %s' % (filename, 'updating' if value else 'clearing', key)
332 git.AddPath(path)
333 git.Commit(cwd, desc)
David James8c846492011-01-25 17:07:29 -0800334
Mike Frysinger212e4292014-05-24 15:15:44 -0400335def GenerateHtmlIndex(files, index, board, version):
336 """Given the list of |files|, generate an index.html at |index|.
337
338 Args:
339 files: The list of files to link to.
340 index: The path to the html index.
341 board: Name of the board this index is for.
342 version: Build version this index is for.
343 """
Don Garrett13cbbff2016-08-09 14:18:38 -0700344 title = 'Package Prebuilt Index: %s / %s' % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400345
346 files = files + [
347 '.|Google Storage Index',
348 '..|',
349 ]
Don Garrett13cbbff2016-08-09 14:18:38 -0700350 commands.GenerateHtmlIndex(index, files, title=title)
Mike Frysinger212e4292014-05-24 15:15:44 -0400351
352
David Jamesce093af2011-02-23 15:21:58 -0800353def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800354 """Grab all of the packages files associated with a list of binhost_urls.
355
David James05bcb2b2011-02-09 09:25:47 -0800356 Args:
357 binhost_urls: The URLs for the directories containing the Packages files we
358 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800359
360 Returns:
361 A list of PackageIndex objects.
362 """
363 pkg_indexes = []
364 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700365 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800366 if pkg_index:
367 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800368 return pkg_indexes
369
370
David Jamesc0f158a2011-02-22 16:07:29 -0800371class PrebuiltUploader(object):
372 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800373
Mike Frysinger86509232014-05-24 13:18:37 -0400374 def __init__(self, upload_location, acl, binhost_base_url, pkg_indexes,
375 build_path, packages, skip_upload, binhost_conf_dir, dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400376 target, slave_targets, version):
David Jamesc0f158a2011-02-22 16:07:29 -0800377 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800378
David Jamesc0f158a2011-02-22 16:07:29 -0800379 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800380
David Jamesc0f158a2011-02-22 16:07:29 -0800381 Args:
382 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800383 acl: The canned acl used for uploading to Google Storage. acl can be one
384 of: "public-read", "public-read-write", "authenticated-read",
Matt Tennante8179042013-10-01 15:47:32 -0700385 "bucket-owner-read", "bucket-owner-full-control", "project-private",
386 or "private" (see "gsutil help acls"). If we are not uploading to
387 Google Storage, this parameter is unused.
David Jamesfd0b0852011-02-23 11:15:36 -0800388 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800389 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
390 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700391 build_path: The path to the directory containing the chroot.
392 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700393 skip_upload: Don't actually upload the tarballs.
394 binhost_conf_dir: Directory where to store binhost.conf files.
Mike Frysinger86509232014-05-24 13:18:37 -0400395 dryrun: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800396 target: BuildTarget managed by this builder.
397 slave_targets: List of BuildTargets managed by slave builders.
Mike Frysinger8092a632014-05-24 13:25:46 -0400398 version: A unique string, intended to be included in the upload path,
399 which identifies the version number of the uploaded prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800400 """
401 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800402 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800403 self._binhost_base_url = binhost_base_url
404 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700405 self._build_path = build_path
406 self._packages = set(packages)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500407 self._found_packages = set()
David James8ece7ee2011-06-29 16:02:30 -0700408 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700409 self._binhost_conf_dir = binhost_conf_dir
Mike Frysinger86509232014-05-24 13:18:37 -0400410 self._dryrun = dryrun
David James4058b0d2011-12-08 21:24:50 -0800411 self._target = target
412 self._slave_targets = slave_targets
Mike Frysinger8092a632014-05-24 13:25:46 -0400413 self._version = version
Mike Frysinger540883b2014-05-24 13:46:16 -0400414 self._gs_context = gs.GSContext(retries=_RETRIES, sleep=_SLEEP_TIME,
415 dry_run=self._dryrun)
416
417 def _Upload(self, local_file, remote_file):
418 """Wrapper around _GsUpload"""
419 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700420
421 def _ShouldFilterPackage(self, pkg):
422 if not self._packages:
423 return False
424 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800425 sys.path.insert(0, pym_path)
Don Garrett25f309a2014-03-19 14:02:12 -0700426 # pylint: disable=F0401
David James615e5b52011-06-03 11:10:15 -0700427 import portage.versions
428 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
429 cp = '%s/%s' % (cat, pkgname)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500430 self._found_packages.add(cp)
David James615e5b52011-06-03 11:10:15 -0700431 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800432
David Jamesc0f158a2011-02-22 16:07:29 -0800433 def _UploadPrebuilt(self, package_path, url_suffix):
434 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800435
David Jamesc0f158a2011-02-22 16:07:29 -0800436 Args:
437 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800438 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesc0f158a2011-02-22 16:07:29 -0800439 """
David Jamesc0f158a2011-02-22 16:07:29 -0800440 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700441 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800442 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700443 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800444 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500445 unmatched_pkgs = self._packages - self._found_packages
446 if unmatched_pkgs:
Ralph Nathan446aee92015-03-23 14:44:56 -0700447 logging.warning('unable to match packages: %r' % unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800448
David Jamesc0f158a2011-02-22 16:07:29 -0800449 # Write Packages file.
David James5ab67e32014-10-24 08:19:59 -0700450 pkg_index.header['TTL'] = _BINPKG_TTL
David Jamesc0f158a2011-02-22 16:07:29 -0800451 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800452
David Jamesc0f158a2011-02-22 16:07:29 -0800453 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700454 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800455
Alex Deymo541ea6f2014-12-23 01:18:14 -0800456 # Build list of files to upload. Manually include the dev-only files but
457 # skip them if not present.
458 # TODO(deymo): Upload dev-only-extras.tbz2 as dev-only-extras.tar.bz2
459 # outside packages/ directory. See crbug.com/448178 for details.
460 if os.path.exists(os.path.join(package_path, 'dev-only-extras.tbz2')):
461 uploads.append({'CPV': 'dev-only-extras'})
David James015af872012-06-19 15:24:36 -0700462 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
463 remote_file = '%s/Packages' % remote_location.rstrip('/')
464 upload_files[tmp_packages_file.name] = remote_file
465
Mike Frysinger540883b2014-05-24 13:46:16 -0400466 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800467
Mike Frysinger212e4292014-05-24 15:15:44 -0400468 with tempfile.NamedTemporaryFile(
469 prefix='chromite.upload_prebuilts.index.') as index:
470 GenerateHtmlIndex(
471 [x[len(remote_location) + 1:] for x in upload_files.values()],
472 index.name, self._target, self._version)
473 self._Upload(index.name, '%s/index.html' % remote_location.rstrip('/'))
474
475 link_name = 'Prebuilts[%s]: %s' % (self._target, self._version)
476 url = '%s%s/index.html' % (gs.PUBLIC_BASE_HTTPS_URL,
477 remote_location[len(gs.BASE_GS_URL):])
Prathmesh Prabhu17f07422015-07-17 11:40:40 -0700478 logging.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400479
Mike Frysinger8092a632014-05-24 13:25:46 -0400480 def _UploadSdkTarball(self, board_path, url_suffix, prepackaged,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700481 toolchains_overlay_tarballs,
482 toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500483 toolchain_tarballs, toolchain_upload_path):
484 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700485
486 Args:
487 board_path: The path to the board dir.
488 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan62a57792012-08-31 15:09:08 +0200489 prepackaged: If given, a tarball that has been packaged outside of this
490 script and should be used.
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700491 toolchains_overlay_tarballs: List of toolchains overlay tarball
492 specifications to upload. Items take the form
493 "toolchains_spec:/path/to/tarball".
494 toolchains_overlay_upload_path: Path template under the bucket to place
495 toolchains overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500496 toolchain_tarballs: List of toolchain tarballs to upload.
497 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David James8fa34ea2011-04-15 13:00:20 -0700498 """
499 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
500 assert remote_location.startswith('gs://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200501 boardname = os.path.basename(board_path.rstrip('/'))
502 # We do not upload non SDK board tarballs,
503 assert boardname == constants.CHROOT_BUILDER_BOARD
504 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200505
Mike Frysinger8092a632014-05-24 13:25:46 -0400506 version_str = self._version[len('chroot-'):]
Mike Frysinger8e727a32013-01-16 16:57:53 -0500507 remote_tarfile = toolchain.GetSdkURL(
508 for_gsutil=True, suburl='cros-sdk-%s.tar.xz' % (version_str,))
Zdenek Behan86c15e92012-10-13 00:55:47 +0200509 # For SDK, also upload the manifest which is guaranteed to exist
510 # by the builderstage.
Mike Frysinger540883b2014-05-24 13:46:16 -0400511 self._Upload(prepackaged + '.Manifest', remote_tarfile + '.Manifest')
512 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200513
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700514 # Upload SDK toolchains overlays and toolchain tarballs, if given.
Gilad Arnoldad333182015-05-27 15:50:41 -0700515 for tarball_list, upload_path, qualifier_name in (
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700516 (toolchains_overlay_tarballs, toolchains_overlay_upload_path,
517 'toolchains'),
Gilad Arnoldad333182015-05-27 15:50:41 -0700518 (toolchain_tarballs, toolchain_upload_path, 'target')):
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700519 for tarball_spec in tarball_list:
520 qualifier_val, local_path = tarball_spec.split(':')
Gilad Arnoldad333182015-05-27 15:50:41 -0700521 suburl = upload_path % {qualifier_name: qualifier_val}
522 remote_path = toolchain.GetSdkURL(for_gsutil=True, suburl=suburl)
523 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500524
Zdenek Behan86c15e92012-10-13 00:55:47 +0200525 # Finally, also update the pointer to the latest SDK on which polling
526 # scripts rely.
David James4bc13702013-03-26 08:08:04 -0700527 with osutils.TempDir() as tmpdir:
Zdenek Behan86c15e92012-10-13 00:55:47 +0200528 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
Mike Frysinger8e727a32013-01-16 16:57:53 -0500529 remote_pointerfile = toolchain.GetSdkURL(for_gsutil=True,
530 suburl='cros-sdk-latest.conf')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200531 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
Mike Frysinger540883b2014-05-24 13:46:16 -0400532 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200533
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700534 def _GetTargets(self):
535 """Retuns the list of targets to use."""
536 targets = self._slave_targets[:]
537 if self._target:
538 targets.append(self._target)
539
540 return targets
541
Mike Frysinger8092a632014-05-24 13:25:46 -0400542 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800543 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800544
David Jamesc0f158a2011-02-22 16:07:29 -0800545 This function will sync both the standard host packages, plus the host
546 packages associated with all targets that have been "setup" with the
547 current host's chroot. For instance, if this host has been used to build
548 x86-generic, it will sync the host packages associated with
549 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
550 it will also sync the host packages associated with
551 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800552
David Jamesc0f158a2011-02-22 16:07:29 -0800553 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800554 key: The variable key to update in the git file.
555 git_sync: If set, update make.conf of target to reference the latest
556 prebuilt packages generated here.
557 sync_binhost_conf: If set, update binhost config file in
558 chromiumos-overlay for the host.
559 """
David Jamese2488642011-11-14 16:15:20 -0800560 # Slave boards are listed before the master board so that the master board
561 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
562 # over preflight host prebuilts from other builders.)
563 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700564 for target in self._GetTargets():
Mike Frysinger8092a632014-05-24 13:25:46 -0400565 url_suffix = _REL_HOST_PATH % {'version': self._version,
David James4058b0d2011-12-08 21:24:50 -0800566 'host_arch': _HOST_ARCH,
567 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800568 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800569
Mike Frysinger540883b2014-05-24 13:46:16 -0400570 if self._target == target and not self._skip_upload:
David Jamese2488642011-11-14 16:15:20 -0800571 # Upload prebuilts.
572 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
573 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700574
David Jamese2488642011-11-14 16:15:20 -0800575 # Record URL where prebuilts were uploaded.
576 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
577 packages_url_suffix.rstrip('/')))
578
David James20b2b6f2011-11-18 15:11:58 -0800579 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700580 if git_sync:
David Jamesb26b9312014-12-15 11:26:46 -0800581 git_file = os.path.join(self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH])
Mike Frysinger86509232014-05-24 13:18:37 -0400582 RevGitFile(git_file, {key: binhost}, dryrun=self._dryrun)
David James8ece7ee2011-06-29 16:02:30 -0700583 if sync_binhost_conf:
David Jamesb26b9312014-12-15 11:26:46 -0800584 binhost_conf = os.path.join(
585 self._binhost_conf_dir, 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800586 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800587
Mike Frysinger8092a632014-05-24 13:25:46 -0400588 def SyncBoardPrebuilts(self, key, git_sync, sync_binhost_conf,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500589 upload_board_tarball, prepackaged_board,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700590 toolchains_overlay_tarballs,
591 toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500592 toolchain_tarballs, toolchain_upload_path):
David Jamesc0f158a2011-02-22 16:07:29 -0800593 """Synchronize board prebuilt files.
594
595 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800596 key: The variable key to update in the git file.
597 git_sync: If set, update make.conf of target to reference the latest
598 prebuilt packages generated here.
599 sync_binhost_conf: If set, update binhost config file in
600 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700601 upload_board_tarball: Include a tarball of the board in our upload.
Zdenek Behan62a57792012-08-31 15:09:08 +0200602 prepackaged_board: A tarball of the board built outside of this script.
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700603 toolchains_overlay_tarballs: List of toolchains overlay tarball
604 specifications to upload. Items take the form
605 "toolchains_spec:/path/to/tarball".
606 toolchains_overlay_upload_path: Path template under the bucket to place
607 toolchains overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500608 toolchain_tarballs: A list of toolchain tarballs to upload.
609 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David Jamesc0f158a2011-02-22 16:07:29 -0800610 """
David Jamesb26b9312014-12-15 11:26:46 -0800611 updated_binhosts = set()
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700612 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800613 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800614 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800615 package_path = os.path.join(board_path, 'packages')
Mike Frysinger8092a632014-05-24 13:25:46 -0400616 url_suffix = _REL_BOARD_PATH % {'target': target,
617 'version': self._version}
David Jamese2488642011-11-14 16:15:20 -0800618 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700619
Matt Tennante8179042013-10-01 15:47:32 -0700620 # Process the target board differently if it is the main --board.
Mike Frysinger540883b2014-05-24 13:46:16 -0400621 if self._target == target and not self._skip_upload:
Matt Tennante8179042013-10-01 15:47:32 -0700622 # This strips "chroot" prefix because that is sometimes added as the
623 # --prepend-version argument (e.g. by chromiumos-sdk bot).
624 # TODO(build): Clean it up to be less hard-coded.
Mike Frysinger8092a632014-05-24 13:25:46 -0400625 version_str = self._version[len('chroot-'):]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500626
David Jamese2488642011-11-14 16:15:20 -0800627 # Upload board tarballs in the background.
628 if upload_board_tarball:
Mike Frysinger9e979b92012-11-29 02:55:09 -0500629 if toolchain_upload_path:
630 toolchain_upload_path %= {'version': version_str}
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700631 if toolchains_overlay_upload_path:
632 toolchains_overlay_upload_path %= {'version': version_str}
Mike Frysinger9e979b92012-11-29 02:55:09 -0500633 tar_process = multiprocessing.Process(
634 target=self._UploadSdkTarball,
Mike Frysinger8092a632014-05-24 13:25:46 -0400635 args=(board_path, url_suffix, prepackaged_board,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700636 toolchains_overlay_tarballs,
637 toolchains_overlay_upload_path, toolchain_tarballs,
638 toolchain_upload_path))
David Jamese2488642011-11-14 16:15:20 -0800639 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700640
David Jamese2488642011-11-14 16:15:20 -0800641 # Upload prebuilts.
642 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700643
David Jamese2488642011-11-14 16:15:20 -0800644 # Make sure we finished uploading the board tarballs.
645 if upload_board_tarball:
646 tar_process.join()
647 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800648
David Jamese2488642011-11-14 16:15:20 -0800649 # Record URL where prebuilts were uploaded.
650 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
651 packages_url_suffix.rstrip('/'))
652
653 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800654 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Mike Frysinger86509232014-05-24 13:18:37 -0400655 RevGitFile(git_file, {key: url_value}, dryrun=self._dryrun)
Matt Tennante8179042013-10-01 15:47:32 -0700656
David Jamese2488642011-11-14 16:15:20 -0800657 if sync_binhost_conf:
Matt Tennante8179042013-10-01 15:47:32 -0700658 # Update the binhost configuration file in git.
David Jamesb26b9312014-12-15 11:26:46 -0800659 binhost_conf = os.path.join(
660 self._binhost_conf_dir, 'target', '%s-%s.conf' % (target, key))
661 updated_binhosts.add(binhost_conf)
David Jamese2488642011-11-14 16:15:20 -0800662 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800663
David Jamesb26b9312014-12-15 11:26:46 -0800664 if sync_binhost_conf:
665 # Clear all old binhosts. The files must be left empty in case anybody
666 # is referring to them.
667 all_binhosts = set(glob.glob(os.path.join(
668 self._binhost_conf_dir, 'target', '*-%s.conf' % key)))
669 for binhost_conf in all_binhosts - updated_binhosts:
670 UpdateBinhostConfFile(binhost_conf, key, '')
671
David James05bcb2b2011-02-09 09:25:47 -0800672
Mike Frysingerb87bb782015-06-04 02:46:50 -0400673class _AddSlaveBoardAction(argparse.Action):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700674 """Callback that adds a slave board to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400675 def __call__(self, parser, namespace, values, option_string=None):
676 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800677
678
Mike Frysingerb87bb782015-06-04 02:46:50 -0400679class _AddSlaveProfileAction(argparse.Action):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700680 """Callback that adds a slave profile to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400681 def __call__(self, parser, namespace, values, option_string=None):
682 if not namespace.slave_targets:
683 parser.error('Must specify --slave-board before --slave-profile')
684 if namespace.slave_targets[-1].profile is not None:
685 parser.error('Cannot specify --slave-profile twice for same board')
686 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800687
688
Mike Frysinger86509232014-05-24 13:18:37 -0400689def ParseOptions(argv):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700690 """Returns options given by the user and the target specified.
691
Mike Frysinger86509232014-05-24 13:18:37 -0400692 Args:
693 argv: The args to parse.
694
Mike Frysinger1a736a82013-12-12 01:50:59 -0500695 Returns:
696 A tuple containing a parsed options object and BuildTarget.
697 The target instance is None if no board is specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700698 """
Mike Frysingerb87bb782015-06-04 02:46:50 -0400699 parser = commandline.ArgumentParser()
700 parser.add_argument('-H', '--binhost-base-url', default=_BINHOST_BASE_URL,
701 help='Base URL to use for binhost in make.conf updates')
702 parser.add_argument('--previous-binhost-url', action='append', default=[],
703 help='Previous binhost URL')
704 parser.add_argument('-b', '--board',
705 help='Board type that was built on this machine')
706 parser.add_argument('-B', '--prepackaged-tarball', type='path',
707 help='Board tarball prebuilt outside of this script.')
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700708 parser.add_argument('--toolchains-overlay-tarball',
709 dest='toolchains_overlay_tarballs',
Mike Frysingerb87bb782015-06-04 02:46:50 -0400710 action='append', default=[],
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700711 help='Toolchains overlay tarball specification to '
712 'upload. Takes the form '
713 '"toolchains_spec:/path/to/tarball".')
714 parser.add_argument('--toolchains-overlay-upload-path', default='',
715 help='Path template for uploading toolchains overlays.')
Mike Frysingerb87bb782015-06-04 02:46:50 -0400716 parser.add_argument('--toolchain-tarball', dest='toolchain_tarballs',
717 action='append', default=[],
718 help='Redistributable toolchain tarball.')
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700719 parser.add_argument('--toolchain-upload-path', default='',
Mike Frysingerb87bb782015-06-04 02:46:50 -0400720 help='Path to place toolchain tarballs in the sdk tree.')
721 parser.add_argument('--profile',
722 help='Profile that was built on this machine')
723 parser.add_argument('--slave-board', default=[], action=_AddSlaveBoardAction,
724 dest='slave_targets',
725 help='Board type that was built on a slave machine. To '
726 'add a profile to this board, use --slave-profile.')
727 parser.add_argument('--slave-profile', action=_AddSlaveProfileAction,
728 help='Board profile that was built on a slave machine. '
729 'Applies to previous slave board.')
730 parser.add_argument('-p', '--build-path', required=True,
731 help='Path to the directory containing the chroot')
732 parser.add_argument('--packages', action='append', default=[],
733 help='Only include the specified packages. '
734 '(Default is to include all packages.)')
735 parser.add_argument('-s', '--sync-host', default=False, action='store_true',
736 help='Sync host prebuilts')
737 parser.add_argument('-g', '--git-sync', default=False, action='store_true',
738 help='Enable git version sync (This commits to a repo.) '
739 'This is used by full builders to commit directly '
740 'to board overlays.')
741 parser.add_argument('-u', '--upload',
742 help='Upload location')
743 parser.add_argument('-V', '--prepend-version',
744 help='Add an identifier to the front of the version')
745 parser.add_argument('-f', '--filters', action='store_true', default=False,
746 help='Turn on filtering of private ebuild packages')
747 parser.add_argument('-k', '--key', default='PORTAGE_BINHOST',
748 help='Key to update in make.conf / binhost.conf')
749 parser.add_argument('--set-version',
750 help='Specify the version string')
751 parser.add_argument('--sync-binhost-conf', default=False, action='store_true',
752 help='Update binhost.conf in chromiumos-overlay or '
753 'chromeos-overlay. Commit the changes, but don\'t '
754 'push them. This is used for preflight binhosts.')
755 parser.add_argument('--binhost-conf-dir',
756 help='Directory to commit binhost config with '
757 '--sync-binhost-conf.')
758 parser.add_argument('-P', '--private', action='store_true', default=False,
759 help='Mark gs:// uploads as private.')
760 parser.add_argument('--skip-upload', action='store_true', default=False,
761 help='Skip upload step.')
762 parser.add_argument('--upload-board-tarball', action='store_true',
763 default=False,
764 help='Upload board tarball to Google Storage.')
765 parser.add_argument('-n', '--dry-run', dest='dryrun',
766 action='store_true', default=False,
767 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800768
Mike Frysingerb87bb782015-06-04 02:46:50 -0400769 options = parser.parse_args(argv)
David James8ece7ee2011-06-29 16:02:30 -0700770 if not options.upload and not options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400771 parser.error('you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700772 if not options.set_version and options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400773 parser.error('If you are using --skip-upload, you must specify a '
774 'version number using --set-version.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700775
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700776 target = None
777 if options.board:
778 target = BuildTarget(options.board, options.profile)
779
780 if target in options.slave_targets:
Mike Frysinger86509232014-05-24 13:18:37 -0400781 parser.error('--board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800782
David James4058b0d2011-12-08 21:24:50 -0800783 if len(set(options.slave_targets)) != len(options.slave_targets):
Mike Frysinger86509232014-05-24 13:18:37 -0400784 parser.error('--slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800785
David James4058b0d2011-12-08 21:24:50 -0800786 if options.slave_targets and options.git_sync:
Mike Frysinger86509232014-05-24 13:18:37 -0400787 parser.error('--slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800788
David James8ece7ee2011-06-29 16:02:30 -0700789 if (options.upload_board_tarball and options.skip_upload and
790 options.board == 'amd64-host'):
Mike Frysinger86509232014-05-24 13:18:37 -0400791 parser.error('--skip-upload is not compatible with '
792 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700793
David James8ece7ee2011-06-29 16:02:30 -0700794 if (options.upload_board_tarball and not options.skip_upload and
795 not options.upload.startswith('gs://')):
Mike Frysinger86509232014-05-24 13:18:37 -0400796 parser.error('--upload-board-tarball only works with gs:// URLs.\n'
797 '--upload must be a gs:// URL.')
David James8fa34ea2011-04-15 13:00:20 -0700798
Zdenek Behan86c15e92012-10-13 00:55:47 +0200799 if options.upload_board_tarball and options.prepackaged_tarball is None:
Mike Frysinger86509232014-05-24 13:18:37 -0400800 parser.error('--upload-board-tarball requires --prepackaged-tarball')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200801
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700802 if options.private:
803 if options.sync_host:
Mike Frysinger86509232014-05-24 13:18:37 -0400804 parser.error('--private and --sync-host/-s cannot be specified '
805 'together; we do not support private host prebuilts')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700806
David James8ece7ee2011-06-29 16:02:30 -0700807 if not options.upload or not options.upload.startswith('gs://'):
Mike Frysinger86509232014-05-24 13:18:37 -0400808 parser.error('--private is only valid for gs:// URLs; '
809 '--upload must be a gs:// URL.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700810
811 if options.binhost_base_url != _BINHOST_BASE_URL:
Mike Frysinger86509232014-05-24 13:18:37 -0400812 parser.error('when using --private the --binhost-base-url '
813 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700814
David Jamesc31168e2014-06-05 14:40:05 -0700815 if options.sync_binhost_conf and not options.binhost_conf_dir:
816 parser.error('--sync-binhost-conf requires --binhost-conf-dir')
817
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700818 if (options.toolchains_overlay_tarballs and
819 not options.toolchains_overlay_upload_path):
820 parser.error('--toolchains-overlay-tarball requires '
821 '--toolchains-overlay-upload-path')
Gilad Arnoldad333182015-05-27 15:50:41 -0700822
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700823 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800824
Mike Frysingercc838832014-05-24 13:10:30 -0400825
Mike Frysinger86509232014-05-24 13:18:37 -0400826def main(argv):
David Jamesdb401072011-06-10 12:17:16 -0700827 # Set umask to a sane value so that files created as root are readable.
Mike Frysinger60ec1012013-10-21 00:11:10 -0400828 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -0700829
Mike Frysinger86509232014-05-24 13:18:37 -0400830 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -0800831
David James05bcb2b2011-02-09 09:25:47 -0800832 # Calculate a list of Packages index files to compare against. Whenever we
833 # upload a package, we check to make sure it's not already stored in one of
834 # the packages files we uploaded. This list of packages files might contain
835 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800836 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800837
David James8ece7ee2011-06-29 16:02:30 -0700838 if options.set_version:
839 version = options.set_version
840 else:
841 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -0700842
David Jamesc0f158a2011-02-22 16:07:29 -0800843 if options.prepend_version:
844 version = '%s-%s' % (options.prepend_version, version)
845
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700846 acl = 'public-read'
847 binhost_base_url = options.binhost_base_url
848
David Jamesadd21432013-05-21 10:04:07 -0700849 if options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700850 binhost_base_url = options.upload
David Jamesadd21432013-05-21 10:04:07 -0700851 if target:
Prathmesh Prabhu421eef22014-10-16 17:13:19 -0700852 acl = portage_util.FindOverlayFile(_GOOGLESTORAGE_GSUTIL_FILE,
853 board=target.board_variant,
854 buildroot=options.build_path)
Ben Chan067de492015-01-06 17:19:13 -0800855 if acl is None:
856 cros_build_lib.Die('No Google Storage ACL file %s found in %s overlay.',
857 _GOOGLESTORAGE_GSUTIL_FILE, target.board_variant)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700858
David Jamesb26b9312014-12-15 11:26:46 -0800859 binhost_conf_dir = None
860 if options.binhost_conf_dir:
861 binhost_conf_dir = os.path.join(options.build_path,
862 options.binhost_conf_dir)
863
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700864 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700865 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700866 options.packages, options.skip_upload,
David Jamesb26b9312014-12-15 11:26:46 -0800867 binhost_conf_dir, options.dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400868 target, options.slave_targets, version)
David Jamesc0f158a2011-02-22 16:07:29 -0800869
David James8c846492011-01-25 17:07:29 -0800870 if options.sync_host:
Mike Frysinger8092a632014-05-24 13:25:46 -0400871 uploader.SyncHostPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700872 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800873
Chris Sosa62c8ff52012-06-04 15:03:12 -0700874 if options.board or options.slave_targets:
Mike Frysinger8092a632014-05-24 13:25:46 -0400875 uploader.SyncBoardPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700876 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200877 options.upload_board_tarball,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500878 options.prepackaged_tarball,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700879 options.toolchains_overlay_tarballs,
880 options.toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500881 options.toolchain_tarballs,
882 options.toolchain_upload_path)