blob: 03d73f6d59eb3fa238322b837403210b581a3e65 [file] [log] [blame]
David Jamesb619a782012-07-25 19:37:57 -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
Chris Sosa1dc96132012-05-11 15:40:50 -070022import datetime
Mike Frysinger540883b2014-05-24 13:46:16 -040023import functools
David Jamesb26b9312014-12-15 11:26:46 -080024import glob
Chris Sosa1dc96132012-05-11 15:40:50 -070025import multiprocessing
Chris Sosa1dc96132012-05-11 15:40:50 -070026import os
27import sys
Mike Frysinger212e4292014-05-24 15:15:44 -040028import tempfile
Chris Sosa1dc96132012-05-11 15:40:50 -070029
Don Garrett88b8d782014-05-13 17:30:55 -070030from chromite.cbuildbot import constants
David James14e97772014-06-04 18:44:49 -070031from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080032from chromite.lib import binpkg
Mike Frysinger86509232014-05-24 13:18:37 -040033from chromite.lib import commandline
Chris Sosa1dc96132012-05-11 15:40:50 -070034from chromite.lib import cros_build_lib
Ralph Nathan446aee92015-03-23 14:44:56 -070035from chromite.lib import cros_logging as logging
David James97d95872012-11-16 15:09:56 -080036from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070037from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070038from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080039from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070040from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050041from chromite.lib import toolchain
Chris Sosa1dc96132012-05-11 15:40:50 -070042
David James015af872012-06-19 15:24:36 -070043# How many times to retry uploads.
44_RETRIES = 10
45
46# Multiplier for how long to sleep (in seconds) between retries; will delay
47# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
48_SLEEP_TIME = 60
49
David James5ab67e32014-10-24 08:19:59 -070050# The length of time (in seconds) that Portage should wait before refetching
51# binpkgs from the same binhost. We don't ever modify binhosts, so this should
52# be something big.
53_BINPKG_TTL = 60 * 60 * 24 * 365
54
David James8c846492011-01-25 17:07:29 -080055_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080056_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070057_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080058_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080059_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080060_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
61_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080062# Private overlays to look at for builds to filter
63# relative to build path
64_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Gabe Black40169e62014-06-17 15:23:47 -070065_GOOGLESTORAGE_GSUTIL_FILE = 'googlestorage_acl.txt'
David James3753d942014-04-23 10:55:48 -070066_BINHOST_BASE_URL = 'gs://chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080067_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
68# Created in the event of new host targets becoming available
69_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
70 'make.conf.amd64-host')}
David James8c846492011-01-25 17:07:29 -080071
72
David James4058b0d2011-12-08 21:24:50 -080073class BuildTarget(object):
74 """A board/variant/profile tuple."""
75
76 def __init__(self, board_variant, profile=None):
77 self.board_variant = board_variant
78 self.board, _, self.variant = board_variant.partition('_')
79 self.profile = profile
80
81 def __str__(self):
82 if self.profile:
83 return '%s_%s' % (self.board_variant, self.profile)
84 else:
85 return self.board_variant
86
87 def __eq__(self, other):
88 return str(other) == str(self)
89
90 def __hash__(self):
91 return hash(str(self))
92
93
David James8c846492011-01-25 17:07:29 -080094def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
95 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -050096
David James8c846492011-01-25 17:07:29 -080097 File format:
98 key="value"
99 Note quotes are added automatically
100
101 Args:
102 filename: Name of file to modify.
103 value: Value to write with the key.
104 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800105
106 Returns:
107 True if changes were made to the file.
David James8c846492011-01-25 17:07:29 -0800108 """
109 if os.path.exists(filename):
110 file_fh = open(filename)
111 else:
112 file_fh = open(filename, 'w+')
113 file_lines = []
114 found = False
David Jamesb26b9312014-12-15 11:26:46 -0800115 made_changes = False
David James8c846492011-01-25 17:07:29 -0800116 keyval_str = '%(key)s=%(value)s'
117 for line in file_fh:
118 # Strip newlines from end of line. We already add newlines below.
119 line = line.rstrip("\n")
120
121 if len(line.split('=')) != 2:
122 # Skip any line that doesn't fit key=val.
123 file_lines.append(line)
124 continue
125
126 file_var, file_val = line.split('=')
127 if file_var == key:
128 found = True
Mike Frysingerff441bf2014-05-24 13:47:21 -0400129 print('Updating %s=%s to %s="%s"' % (file_var, file_val, key, value))
David James8c846492011-01-25 17:07:29 -0800130 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800131 made_changes |= (file_val != value)
David James8c846492011-01-25 17:07:29 -0800132 file_lines.append(keyval_str % {'key': key, 'value': value})
133 else:
134 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
135
136 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700137 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800138 made_changes = True
David James8c846492011-01-25 17:07:29 -0800139 file_lines.append(keyval_str % {'key': key, 'value': value})
140
141 file_fh.close()
142 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700143 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David Jamesb26b9312014-12-15 11:26:46 -0800144 return made_changes
David James8c846492011-01-25 17:07:29 -0800145
146
Mike Frysinger5b34d732013-01-17 15:11:58 -0500147def RevGitFile(filename, data, retries=5, dryrun=False):
David James8c846492011-01-25 17:07:29 -0800148 """Update and push the git file.
149
Mike Frysinger5b34d732013-01-17 15:11:58 -0500150 Args:
151 filename: file to modify that is in a git repo already
152 data: A dict of key/values to update in |filename|
153 retries: The number of times to retry before giving up, default: 5
Matt Tennante8179042013-10-01 15:47:32 -0700154 dryrun: If True, do not actually commit the change.
David James8c846492011-01-25 17:07:29 -0800155 """
156 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700157 cwd = os.path.abspath(os.path.dirname(filename))
David James97d95872012-11-16 15:09:56 -0800158 commit = git.RunGit(cwd, ['rev-parse', 'HEAD']).output.rstrip()
Mike Frysinger5b34d732013-01-17 15:11:58 -0500159 description = '%s: updating %s' % (os.path.basename(filename),
160 ', '.join(data.keys()))
161 # UpdateLocalFile will print out the keys/values for us.
Mike Frysingerff441bf2014-05-24 13:47:21 -0400162 print('Revving git file %s' % filename)
David James66009462012-03-25 10:08:38 -0700163
David James8c846492011-01-25 17:07:29 -0800164 try:
David James97d95872012-11-16 15:09:56 -0800165 git.CreatePushBranch(prebuilt_branch, cwd)
Mike Frysinger5b34d732013-01-17 15:11:58 -0500166 for key, value in data.iteritems():
167 UpdateLocalFile(filename, value, key)
David James97d95872012-11-16 15:09:56 -0800168 git.RunGit(cwd, ['add', filename])
169 git.RunGit(cwd, ['commit', '-m', description])
170 git.PushWithRetry(prebuilt_branch, cwd, dryrun=dryrun, retries=retries)
David James8c846492011-01-25 17:07:29 -0800171 finally:
David James67d73252013-09-19 17:33:12 -0700172 git.RunGit(cwd, ['checkout', commit])
David James8c846492011-01-25 17:07:29 -0800173
174
175def GetVersion():
176 """Get the version to put in LATEST and update the git version with."""
Mike Frysinger0b92cfe2012-12-13 02:13:45 -0500177 return datetime.datetime.now().strftime('%Y.%m.%d.%H%M%S')
David James8c846492011-01-25 17:07:29 -0800178
179
Mike Frysinger540883b2014-05-24 13:46:16 -0400180def _GsUpload(gs_context, acl, local_file, remote_file):
David James8c846492011-01-25 17:07:29 -0800181 """Upload to GS bucket.
182
183 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400184 gs_context: A lib.gs.GSContext instance.
185 acl: The ACL to use for uploading the file.
Matt Tennante8179042013-10-01 15:47:32 -0700186 local_file: The local file to be uploaded.
187 remote_file: The remote location to upload to.
David James8c846492011-01-25 17:07:29 -0800188 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700189 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
190 'authenticated-read', 'bucket-owner-full-control',
191 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700192 if acl in CANNED_ACLS:
David James9374aac2013-10-08 16:00:17 -0700193 gs_context.Copy(local_file, remote_file, acl=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700194 else:
195 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700196 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James7b7d7c32015-04-02 13:48:17 -0700197 # We set version=0 here to ensure that the ACL is set only once (see
198 # http://b/15883752#comment54).
199 try:
200 gs_context.Copy(local_file, remote_file, version=0)
201 except gs.GSContextPreconditionFailed as ex:
202 # If we received a GSContextPreconditionFailed error, we know that the
203 # file exists now, but we don't know whether our specific update
204 # succeeded. See http://b/15883752#comment62
205 logging.warning(
206 'Assuming upload succeeded despite PreconditionFailed errors: %s', ex)
207
Gabe Black40169e62014-06-17 15:23:47 -0700208 if acl.endswith('.xml'):
209 # Apply the passed in ACL xml file to the uploaded object.
210 gs_context.SetACL(remote_file, acl=acl)
211 else:
212 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700213
Mike Frysingercc838832014-05-24 13:10:30 -0400214
Mike Frysinger540883b2014-05-24 13:46:16 -0400215def RemoteUpload(gs_context, acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800216 """Upload to google storage.
217
218 Create a pool of process and call _GsUpload with the proper arguments.
219
220 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400221 gs_context: A lib.gs.GSContext instance.
David Jamesfd0b0852011-02-23 11:15:36 -0800222 acl: The canned acl used for uploading. acl can be one of: "public-read",
223 "public-read-write", "authenticated-read", "bucket-owner-read",
224 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800225 files: dictionary with keys to local files and values to remote path.
226 pool: integer of maximum proesses to have at the same time.
227
228 Returns:
229 Return a set of tuple arguments of the failed uploads
230 """
Mike Frysinger540883b2014-05-24 13:46:16 -0400231 upload = functools.partial(_GsUpload, gs_context, acl)
232 tasks = [[key, value] for key, value in files.iteritems()]
233 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800234
235
236def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
237 """Build a dictionary of local remote file key pairs to upload.
238
239 Args:
240 base_local_path: The base path to the files on the local hard drive.
Matt Tennante8179042013-10-01 15:47:32 -0700241 base_remote_path: The base path to the remote paths.
David James8c846492011-01-25 17:07:29 -0800242 pkgs: The packages to upload.
243
244 Returns:
245 Returns a dictionary of local_path/remote_path pairs
246 """
247 upload_files = {}
248 for pkg in pkgs:
249 suffix = pkg['CPV'] + '.tbz2'
250 local_path = os.path.join(base_local_path, suffix)
David James7b7d7c32015-04-02 13:48:17 -0700251 assert os.path.exists(local_path), '%s does not exist' % local_path
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800252 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800253
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800254 if pkg.get('DEBUG_SYMBOLS') == 'yes':
255 debugsuffix = pkg['CPV'] + '.debug.tbz2'
256 local_path = os.path.join(base_local_path, debugsuffix)
257 assert os.path.exists(local_path)
258 upload_files[local_path] = os.path.join(base_remote_path, debugsuffix)
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800259
David James8c846492011-01-25 17:07:29 -0800260 return upload_files
261
Mike Frysingercc838832014-05-24 13:10:30 -0400262
Peter Mayo950e41a2014-02-06 21:07:33 +0000263def GetBoardOverlay(build_path, target):
David Jamese5867812012-10-19 12:02:20 -0700264 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500265
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400266 Args:
267 build_path: The path to the root of the build directory
268 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500269
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400270 Returns:
271 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800272 """
David Jamese5867812012-10-19 12:02:20 -0700273 board = target.board_variant
Alex Deymo075c2292014-09-04 18:31:50 -0700274 overlays = portage_util.FindOverlays(constants.BOTH_OVERLAYS, board,
275 buildroot=build_path)
David Jamese5867812012-10-19 12:02:20 -0700276 # We only care about the last entry.
277 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800278
279
280def DeterminePrebuiltConfFile(build_path, target):
281 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
282
Mike Frysinger1a736a82013-12-12 01:50:59 -0500283 Args:
284 build_path: The path to the root of the build directory
285 target: String representation of the board. This includes host and board
286 targets
David James8c846492011-01-25 17:07:29 -0800287
Mike Frysinger1a736a82013-12-12 01:50:59 -0500288 Returns:
289 A string path to a prebuilt.conf file to be updated.
David James8c846492011-01-25 17:07:29 -0800290 """
David James4058b0d2011-12-08 21:24:50 -0800291 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800292 # We are host.
293 # Without more examples of hosts this is a kludge for now.
294 # TODO(Scottz): as new host targets come online expand this to
295 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800296 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800297 else:
298 # We are a board
Peter Mayo950e41a2014-02-06 21:07:33 +0000299 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800300 make_path = os.path.join(board, 'prebuilt.conf')
301
302 return make_path
303
304
305def UpdateBinhostConfFile(path, key, value):
306 """Update binhost config file file with key=value.
307
308 Args:
309 path: Filename to update.
310 key: Key to update.
311 value: New value for key.
312 """
David Jamesb26b9312014-12-15 11:26:46 -0800313 cwd, filename = os.path.split(os.path.abspath(path))
Brian Harringaf019fb2012-05-10 15:06:13 -0700314 osutils.SafeMakedirs(cwd)
David Jamesf6e8fb72013-05-10 08:58:43 -0700315 if not git.GetCurrentBranch(cwd):
316 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
Brian Harring22edb442012-05-11 23:55:18 -0700317 osutils.WriteFile(path, '', mode='a')
David Jamesb26b9312014-12-15 11:26:46 -0800318 if UpdateLocalFile(path, value, key):
319 desc = '%s: %s %s' % (filename, 'updating' if value else 'clearing', key)
320 git.AddPath(path)
321 git.Commit(cwd, desc)
David James8c846492011-01-25 17:07:29 -0800322
Mike Frysinger212e4292014-05-24 15:15:44 -0400323def GenerateHtmlIndex(files, index, board, version):
324 """Given the list of |files|, generate an index.html at |index|.
325
326 Args:
327 files: The list of files to link to.
328 index: The path to the html index.
329 board: Name of the board this index is for.
330 version: Build version this index is for.
331 """
332 head = """<html>
333<head>
334 <title>Package Prebuilt Index: %(board)s / %(version)s</title>
335</head>
336<body>
337<h2>Package Prebuilt Index: %(board)s / %(version)s</h2>"""
338 head %= {
339 'board': board,
340 'version': version,
341 }
342
343 files = files + [
344 '.|Google Storage Index',
345 '..|',
346 ]
347 commands.GenerateHtmlIndex(index, files, head=head)
348
349
David Jamesce093af2011-02-23 15:21:58 -0800350def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800351 """Grab all of the packages files associated with a list of binhost_urls.
352
David James05bcb2b2011-02-09 09:25:47 -0800353 Args:
354 binhost_urls: The URLs for the directories containing the Packages files we
355 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800356
357 Returns:
358 A list of PackageIndex objects.
359 """
360 pkg_indexes = []
361 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700362 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800363 if pkg_index:
364 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800365 return pkg_indexes
366
367
David Jamesc0f158a2011-02-22 16:07:29 -0800368class PrebuiltUploader(object):
369 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800370
Mike Frysinger86509232014-05-24 13:18:37 -0400371 def __init__(self, upload_location, acl, binhost_base_url, pkg_indexes,
372 build_path, packages, skip_upload, binhost_conf_dir, dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400373 target, slave_targets, version):
David Jamesc0f158a2011-02-22 16:07:29 -0800374 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800375
David Jamesc0f158a2011-02-22 16:07:29 -0800376 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800377
David Jamesc0f158a2011-02-22 16:07:29 -0800378 Args:
379 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800380 acl: The canned acl used for uploading to Google Storage. acl can be one
381 of: "public-read", "public-read-write", "authenticated-read",
Matt Tennante8179042013-10-01 15:47:32 -0700382 "bucket-owner-read", "bucket-owner-full-control", "project-private",
383 or "private" (see "gsutil help acls"). If we are not uploading to
384 Google Storage, this parameter is unused.
David Jamesfd0b0852011-02-23 11:15:36 -0800385 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800386 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
387 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700388 build_path: The path to the directory containing the chroot.
389 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700390 skip_upload: Don't actually upload the tarballs.
391 binhost_conf_dir: Directory where to store binhost.conf files.
Mike Frysinger86509232014-05-24 13:18:37 -0400392 dryrun: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800393 target: BuildTarget managed by this builder.
394 slave_targets: List of BuildTargets managed by slave builders.
Mike Frysinger8092a632014-05-24 13:25:46 -0400395 version: A unique string, intended to be included in the upload path,
396 which identifies the version number of the uploaded prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800397 """
398 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800399 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800400 self._binhost_base_url = binhost_base_url
401 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700402 self._build_path = build_path
403 self._packages = set(packages)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500404 self._found_packages = set()
David James8ece7ee2011-06-29 16:02:30 -0700405 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700406 self._binhost_conf_dir = binhost_conf_dir
Mike Frysinger86509232014-05-24 13:18:37 -0400407 self._dryrun = dryrun
David James4058b0d2011-12-08 21:24:50 -0800408 self._target = target
409 self._slave_targets = slave_targets
Mike Frysinger8092a632014-05-24 13:25:46 -0400410 self._version = version
Mike Frysinger540883b2014-05-24 13:46:16 -0400411 self._gs_context = gs.GSContext(retries=_RETRIES, sleep=_SLEEP_TIME,
412 dry_run=self._dryrun)
413
414 def _Upload(self, local_file, remote_file):
415 """Wrapper around _GsUpload"""
416 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700417
418 def _ShouldFilterPackage(self, pkg):
419 if not self._packages:
420 return False
421 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800422 sys.path.insert(0, pym_path)
Don Garrett25f309a2014-03-19 14:02:12 -0700423 # pylint: disable=F0401
David James615e5b52011-06-03 11:10:15 -0700424 import portage.versions
425 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
426 cp = '%s/%s' % (cat, pkgname)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500427 self._found_packages.add(cp)
David James615e5b52011-06-03 11:10:15 -0700428 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800429
David Jamesc0f158a2011-02-22 16:07:29 -0800430 def _UploadPrebuilt(self, package_path, url_suffix):
431 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800432
David Jamesc0f158a2011-02-22 16:07:29 -0800433 Args:
434 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800435 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesc0f158a2011-02-22 16:07:29 -0800436 """
David Jamesc0f158a2011-02-22 16:07:29 -0800437 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700438 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800439 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700440 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800441 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500442 unmatched_pkgs = self._packages - self._found_packages
443 if unmatched_pkgs:
Ralph Nathan446aee92015-03-23 14:44:56 -0700444 logging.warning('unable to match packages: %r' % unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800445
David Jamesc0f158a2011-02-22 16:07:29 -0800446 # Write Packages file.
David James5ab67e32014-10-24 08:19:59 -0700447 pkg_index.header['TTL'] = _BINPKG_TTL
David Jamesc0f158a2011-02-22 16:07:29 -0800448 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800449
David Jamesc0f158a2011-02-22 16:07:29 -0800450 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700451 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800452
Alex Deymo541ea6f2014-12-23 01:18:14 -0800453 # Build list of files to upload. Manually include the dev-only files but
454 # skip them if not present.
455 # TODO(deymo): Upload dev-only-extras.tbz2 as dev-only-extras.tar.bz2
456 # outside packages/ directory. See crbug.com/448178 for details.
457 if os.path.exists(os.path.join(package_path, 'dev-only-extras.tbz2')):
458 uploads.append({'CPV': 'dev-only-extras'})
David James015af872012-06-19 15:24:36 -0700459 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
460 remote_file = '%s/Packages' % remote_location.rstrip('/')
461 upload_files[tmp_packages_file.name] = remote_file
462
Mike Frysinger540883b2014-05-24 13:46:16 -0400463 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800464
Mike Frysinger212e4292014-05-24 15:15:44 -0400465 with tempfile.NamedTemporaryFile(
466 prefix='chromite.upload_prebuilts.index.') as index:
467 GenerateHtmlIndex(
468 [x[len(remote_location) + 1:] for x in upload_files.values()],
469 index.name, self._target, self._version)
470 self._Upload(index.name, '%s/index.html' % remote_location.rstrip('/'))
471
472 link_name = 'Prebuilts[%s]: %s' % (self._target, self._version)
473 url = '%s%s/index.html' % (gs.PUBLIC_BASE_HTTPS_URL,
474 remote_location[len(gs.BASE_GS_URL):])
475 cros_build_lib.PrintBuildbotLink(link_name, url)
476
Mike Frysinger8092a632014-05-24 13:25:46 -0400477 def _UploadSdkTarball(self, board_path, url_suffix, prepackaged,
Gilad Arnoldad333182015-05-27 15:50:41 -0700478 board_overlay_tarballs, board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500479 toolchain_tarballs, toolchain_upload_path):
480 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700481
482 Args:
483 board_path: The path to the board dir.
484 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan62a57792012-08-31 15:09:08 +0200485 prepackaged: If given, a tarball that has been packaged outside of this
486 script and should be used.
Gilad Arnoldad333182015-05-27 15:50:41 -0700487 board_overlay_tarballs: List of board-specific overlay tarball
488 specifications to upload. Items take the form "board:/path/to/tarball".
489 board_overlay_upload_path: Path template under the bucket to place
490 board-specific overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500491 toolchain_tarballs: List of toolchain tarballs to upload.
492 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David James8fa34ea2011-04-15 13:00:20 -0700493 """
494 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
495 assert remote_location.startswith('gs://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200496 boardname = os.path.basename(board_path.rstrip('/'))
497 # We do not upload non SDK board tarballs,
498 assert boardname == constants.CHROOT_BUILDER_BOARD
499 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200500
Mike Frysinger8092a632014-05-24 13:25:46 -0400501 version_str = self._version[len('chroot-'):]
Mike Frysinger8e727a32013-01-16 16:57:53 -0500502 remote_tarfile = toolchain.GetSdkURL(
503 for_gsutil=True, suburl='cros-sdk-%s.tar.xz' % (version_str,))
Zdenek Behan86c15e92012-10-13 00:55:47 +0200504 # For SDK, also upload the manifest which is guaranteed to exist
505 # by the builderstage.
Mike Frysinger540883b2014-05-24 13:46:16 -0400506 self._Upload(prepackaged + '.Manifest', remote_tarfile + '.Manifest')
507 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200508
Gilad Arnoldad333182015-05-27 15:50:41 -0700509 # Upload board-specific SDK overlays and toolchain tarballs, if given.
510 for tarball_list, upload_path, qualifier_name in (
511 (board_overlay_tarballs, board_overlay_upload_path, 'board'),
512 (toolchain_tarballs, toolchain_upload_path, 'target')):
513 for tarball in tarball_list:
514 qualifier_val, local_path = tarball.split(':')
515 suburl = upload_path % {qualifier_name: qualifier_val}
516 remote_path = toolchain.GetSdkURL(for_gsutil=True, suburl=suburl)
517 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500518
Zdenek Behan86c15e92012-10-13 00:55:47 +0200519 # Finally, also update the pointer to the latest SDK on which polling
520 # scripts rely.
David James4bc13702013-03-26 08:08:04 -0700521 with osutils.TempDir() as tmpdir:
Zdenek Behan86c15e92012-10-13 00:55:47 +0200522 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
Mike Frysinger8e727a32013-01-16 16:57:53 -0500523 remote_pointerfile = toolchain.GetSdkURL(for_gsutil=True,
524 suburl='cros-sdk-latest.conf')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200525 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
Mike Frysinger540883b2014-05-24 13:46:16 -0400526 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200527
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700528 def _GetTargets(self):
529 """Retuns the list of targets to use."""
530 targets = self._slave_targets[:]
531 if self._target:
532 targets.append(self._target)
533
534 return targets
535
Mike Frysinger8092a632014-05-24 13:25:46 -0400536 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800537 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800538
David Jamesc0f158a2011-02-22 16:07:29 -0800539 This function will sync both the standard host packages, plus the host
540 packages associated with all targets that have been "setup" with the
541 current host's chroot. For instance, if this host has been used to build
542 x86-generic, it will sync the host packages associated with
543 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
544 it will also sync the host packages associated with
545 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800546
David Jamesc0f158a2011-02-22 16:07:29 -0800547 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800548 key: The variable key to update in the git file.
549 git_sync: If set, update make.conf of target to reference the latest
550 prebuilt packages generated here.
551 sync_binhost_conf: If set, update binhost config file in
552 chromiumos-overlay for the host.
553 """
David Jamese2488642011-11-14 16:15:20 -0800554 # Slave boards are listed before the master board so that the master board
555 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
556 # over preflight host prebuilts from other builders.)
557 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700558 for target in self._GetTargets():
Mike Frysinger8092a632014-05-24 13:25:46 -0400559 url_suffix = _REL_HOST_PATH % {'version': self._version,
David James4058b0d2011-12-08 21:24:50 -0800560 'host_arch': _HOST_ARCH,
561 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800562 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800563
Mike Frysinger540883b2014-05-24 13:46:16 -0400564 if self._target == target and not self._skip_upload:
David Jamese2488642011-11-14 16:15:20 -0800565 # Upload prebuilts.
566 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
567 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700568
David Jamese2488642011-11-14 16:15:20 -0800569 # Record URL where prebuilts were uploaded.
570 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
571 packages_url_suffix.rstrip('/')))
572
David James20b2b6f2011-11-18 15:11:58 -0800573 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700574 if git_sync:
David Jamesb26b9312014-12-15 11:26:46 -0800575 git_file = os.path.join(self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH])
Mike Frysinger86509232014-05-24 13:18:37 -0400576 RevGitFile(git_file, {key: binhost}, dryrun=self._dryrun)
David James8ece7ee2011-06-29 16:02:30 -0700577 if sync_binhost_conf:
David Jamesb26b9312014-12-15 11:26:46 -0800578 binhost_conf = os.path.join(
579 self._binhost_conf_dir, 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800580 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800581
Mike Frysinger8092a632014-05-24 13:25:46 -0400582 def SyncBoardPrebuilts(self, key, git_sync, sync_binhost_conf,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500583 upload_board_tarball, prepackaged_board,
Gilad Arnoldad333182015-05-27 15:50:41 -0700584 board_overlay_tarballs, board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500585 toolchain_tarballs, toolchain_upload_path):
David Jamesc0f158a2011-02-22 16:07:29 -0800586 """Synchronize board prebuilt files.
587
588 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800589 key: The variable key to update in the git file.
590 git_sync: If set, update make.conf of target to reference the latest
591 prebuilt packages generated here.
592 sync_binhost_conf: If set, update binhost config file in
593 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700594 upload_board_tarball: Include a tarball of the board in our upload.
Zdenek Behan62a57792012-08-31 15:09:08 +0200595 prepackaged_board: A tarball of the board built outside of this script.
Gilad Arnoldad333182015-05-27 15:50:41 -0700596 board_overlay_tarballs: List of board-specific overlay tarball
597 specifications to upload. Items take the form "board:/path/to/tarball".
598 board_overlay_upload_path: Path template under the bucket to place
599 board-specific overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500600 toolchain_tarballs: A list of toolchain tarballs to upload.
601 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David Jamesc0f158a2011-02-22 16:07:29 -0800602 """
David Jamesb26b9312014-12-15 11:26:46 -0800603 updated_binhosts = set()
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700604 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800605 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800606 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800607 package_path = os.path.join(board_path, 'packages')
Mike Frysinger8092a632014-05-24 13:25:46 -0400608 url_suffix = _REL_BOARD_PATH % {'target': target,
609 'version': self._version}
David Jamese2488642011-11-14 16:15:20 -0800610 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700611
Matt Tennante8179042013-10-01 15:47:32 -0700612 # Process the target board differently if it is the main --board.
Mike Frysinger540883b2014-05-24 13:46:16 -0400613 if self._target == target and not self._skip_upload:
Matt Tennante8179042013-10-01 15:47:32 -0700614 # This strips "chroot" prefix because that is sometimes added as the
615 # --prepend-version argument (e.g. by chromiumos-sdk bot).
616 # TODO(build): Clean it up to be less hard-coded.
Mike Frysinger8092a632014-05-24 13:25:46 -0400617 version_str = self._version[len('chroot-'):]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500618
David Jamese2488642011-11-14 16:15:20 -0800619 # Upload board tarballs in the background.
620 if upload_board_tarball:
Mike Frysinger9e979b92012-11-29 02:55:09 -0500621 if toolchain_upload_path:
622 toolchain_upload_path %= {'version': version_str}
Gilad Arnoldad333182015-05-27 15:50:41 -0700623 if board_overlay_upload_path:
624 board_overlay_upload_path %= {'version': version_str}
Mike Frysinger9e979b92012-11-29 02:55:09 -0500625 tar_process = multiprocessing.Process(
626 target=self._UploadSdkTarball,
Mike Frysinger8092a632014-05-24 13:25:46 -0400627 args=(board_path, url_suffix, prepackaged_board,
Gilad Arnoldad333182015-05-27 15:50:41 -0700628 board_overlay_tarballs, board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500629 toolchain_tarballs, toolchain_upload_path))
David Jamese2488642011-11-14 16:15:20 -0800630 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700631
David Jamese2488642011-11-14 16:15:20 -0800632 # Upload prebuilts.
633 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700634
David Jamese2488642011-11-14 16:15:20 -0800635 # Make sure we finished uploading the board tarballs.
636 if upload_board_tarball:
637 tar_process.join()
638 assert tar_process.exitcode == 0
639 # TODO(zbehan): This should be done cleaner.
Zdenek Behan33a34112012-09-10 21:07:51 +0200640 if target.board == constants.CHROOT_BUILDER_BOARD:
David Jamesb26b9312014-12-15 11:26:46 -0800641 sdk_conf = os.path.join(self._binhost_conf_dir,
Mike Frysinger5b34d732013-01-17 15:11:58 -0500642 'host/sdk_version.conf')
643 sdk_settings = {
Mike Frysinger9e979b92012-11-29 02:55:09 -0500644 'SDK_LATEST_VERSION': version_str,
645 'TC_PATH': toolchain_upload_path,
Mike Frysinger5b34d732013-01-17 15:11:58 -0500646 }
Mike Frysinger86509232014-05-24 13:18:37 -0400647 RevGitFile(sdk_conf, sdk_settings, dryrun=self._dryrun)
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
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700673def _AddSlaveBoard(_option, _opt_str, value, parser):
674 """Callback that adds a slave board to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800675 parser.values.slave_targets.append(BuildTarget(value))
676
677
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700678def _AddSlaveProfile(_option, _opt_str, value, parser):
679 """Callback that adds a slave profile to the list of slave targets."""
David James4058b0d2011-12-08 21:24:50 -0800680 if not parser.values.slave_targets:
Mike Frysinger86509232014-05-24 13:18:37 -0400681 parser.error('Must specify --slave-board before --slave-profile')
David James4058b0d2011-12-08 21:24:50 -0800682 if parser.values.slave_targets[-1].profile is not None:
Mike Frysinger86509232014-05-24 13:18:37 -0400683 parser.error('Cannot specify --slave-profile twice for same board')
David James4058b0d2011-12-08 21:24:50 -0800684 parser.values.slave_targets[-1].profile = value
685
686
Mike Frysinger86509232014-05-24 13:18:37 -0400687def ParseOptions(argv):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700688 """Returns options given by the user and the target specified.
689
Mike Frysinger86509232014-05-24 13:18:37 -0400690 Args:
691 argv: The args to parse.
692
Mike Frysinger1a736a82013-12-12 01:50:59 -0500693 Returns:
694 A tuple containing a parsed options object and BuildTarget.
695 The target instance is None if no board is specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700696 """
Mike Frysinger86509232014-05-24 13:18:37 -0400697 parser = commandline.OptionParser()
David James8c846492011-01-25 17:07:29 -0800698 parser.add_option('-H', '--binhost-base-url', dest='binhost_base_url',
699 default=_BINHOST_BASE_URL,
700 help='Base URL to use for binhost in make.conf updates')
701 parser.add_option('', '--previous-binhost-url', action='append',
702 default=[], dest='previous_binhost_url',
703 help='Previous binhost URL')
704 parser.add_option('-b', '--board', dest='board', default=None,
705 help='Board type that was built on this machine')
Zdenek Behan62a57792012-08-31 15:09:08 +0200706 parser.add_option('-B', '--prepackaged-tarball', dest='prepackaged_tarball',
707 default=None,
708 help='Board tarball prebuilt outside of this script.')
Gilad Arnoldad333182015-05-27 15:50:41 -0700709 parser.add_option('--board-overlay-tarball', dest='board_overlay_tarballs',
710 action='append', default=[],
711 help='Board-specific toolchain overlay tarball to upload. '
712 'Takes the form "board:/path/to/tarball".')
713 parser.add_option('--board-overlay-upload-path', default='',
714 help='Path template for uploading board overlays.')
Mike Frysinger9e979b92012-11-29 02:55:09 -0500715 parser.add_option('--toolchain-tarball', dest='toolchain_tarballs',
716 action='append', default=[],
717 help='Redistributable toolchain tarball.')
718 parser.add_option('--toolchain-upload-path', default='',
719 help='Path to place toolchain tarballs in the sdk tree.')
David James4058b0d2011-12-08 21:24:50 -0800720 parser.add_option('', '--profile', dest='profile', default=None,
721 help='Profile that was built on this machine')
722 parser.add_option('', '--slave-board', default=[], action='callback',
723 dest='slave_targets', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700724 callback=_AddSlaveBoard,
David James4058b0d2011-12-08 21:24:50 -0800725 help='Board type that was built on a slave machine. To '
726 'add a profile to this board, use --slave-profile.')
727 parser.add_option('', '--slave-profile', action='callback', type='string',
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700728 callback=_AddSlaveProfile,
David James4058b0d2011-12-08 21:24:50 -0800729 help='Board profile that was built on a slave machine. '
730 'Applies to previous slave board.')
David James8c846492011-01-25 17:07:29 -0800731 parser.add_option('-p', '--build-path', dest='build_path',
David James05bcb2b2011-02-09 09:25:47 -0800732 help='Path to the directory containing the chroot')
David James615e5b52011-06-03 11:10:15 -0700733 parser.add_option('', '--packages', action='append',
734 default=[], dest='packages',
735 help='Only include the specified packages. '
736 '(Default is to include all packages.)')
David James8c846492011-01-25 17:07:29 -0800737 parser.add_option('-s', '--sync-host', dest='sync_host',
738 default=False, action='store_true',
739 help='Sync host prebuilts')
740 parser.add_option('-g', '--git-sync', dest='git_sync',
741 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800742 help='Enable git version sync (This commits to a repo.) '
743 'This is used by full builders to commit directly '
744 'to board overlays.')
David James8c846492011-01-25 17:07:29 -0800745 parser.add_option('-u', '--upload', dest='upload',
746 default=None,
747 help='Upload location')
748 parser.add_option('-V', '--prepend-version', dest='prepend_version',
749 default=None,
750 help='Add an identifier to the front of the version')
751 parser.add_option('-f', '--filters', dest='filters', action='store_true',
752 default=False,
753 help='Turn on filtering of private ebuild packages')
754 parser.add_option('-k', '--key', dest='key',
755 default='PORTAGE_BINHOST',
756 help='Key to update in make.conf / binhost.conf')
David James8ece7ee2011-06-29 16:02:30 -0700757 parser.add_option('', '--set-version', dest='set_version',
758 default=None,
759 help='Specify the version string')
David James8c846492011-01-25 17:07:29 -0800760 parser.add_option('', '--sync-binhost-conf', dest='sync_binhost_conf',
761 default=False, action='store_true',
David Jamese2488642011-11-14 16:15:20 -0800762 help='Update binhost.conf in chromiumos-overlay or '
763 'chromeos-overlay. Commit the changes, but don\'t '
764 'push them. This is used for preflight binhosts.')
David James32b0b2f2011-07-13 20:56:50 -0700765 parser.add_option('', '--binhost-conf-dir', dest='binhost_conf_dir',
David James32b0b2f2011-07-13 20:56:50 -0700766 help='Directory to commit binhost config with '
767 '--sync-binhost-conf.')
David Jamesfd0b0852011-02-23 11:15:36 -0800768 parser.add_option('-P', '--private', dest='private', action='store_true',
769 default=False, help='Mark gs:// uploads as private.')
David James8ece7ee2011-06-29 16:02:30 -0700770 parser.add_option('', '--skip-upload', dest='skip_upload',
771 action='store_true', default=False,
772 help='Skip upload step.')
David James8fa34ea2011-04-15 13:00:20 -0700773 parser.add_option('', '--upload-board-tarball', dest='upload_board_tarball',
774 action='store_true', default=False,
775 help='Upload board tarball to Google Storage.')
Mike Frysinger86509232014-05-24 13:18:37 -0400776 parser.add_option('-n', '--dry-run', dest='dryrun',
David James27fa7d12011-06-29 17:24:14 -0700777 action='store_true', default=False,
778 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800779
Mike Frysinger86509232014-05-24 13:18:37 -0400780 options, args = parser.parse_args(argv)
David James8c846492011-01-25 17:07:29 -0800781 if not options.build_path:
Mike Frysinger86509232014-05-24 13:18:37 -0400782 parser.error('you need provide a chroot path')
David James8ece7ee2011-06-29 16:02:30 -0700783 if not options.upload and not options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400784 parser.error('you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700785 if not options.set_version and options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400786 parser.error('If you are using --skip-upload, you must specify a '
787 'version number using --set-version.')
David James9417f272011-05-26 13:24:47 -0700788 if args:
Mike Frysinger86509232014-05-24 13:18:37 -0400789 parser.error('invalid arguments passed to upload_prebuilts: %r' % args)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700790
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700791 target = None
792 if options.board:
793 target = BuildTarget(options.board, options.profile)
794
795 if target in options.slave_targets:
Mike Frysinger86509232014-05-24 13:18:37 -0400796 parser.error('--board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800797
David James4058b0d2011-12-08 21:24:50 -0800798 if len(set(options.slave_targets)) != len(options.slave_targets):
Mike Frysinger86509232014-05-24 13:18:37 -0400799 parser.error('--slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800800
David James4058b0d2011-12-08 21:24:50 -0800801 if options.slave_targets and options.git_sync:
Mike Frysinger86509232014-05-24 13:18:37 -0400802 parser.error('--slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800803
David James8ece7ee2011-06-29 16:02:30 -0700804 if (options.upload_board_tarball and options.skip_upload and
805 options.board == 'amd64-host'):
Mike Frysinger86509232014-05-24 13:18:37 -0400806 parser.error('--skip-upload is not compatible with '
807 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700808
David James8ece7ee2011-06-29 16:02:30 -0700809 if (options.upload_board_tarball and not options.skip_upload and
810 not options.upload.startswith('gs://')):
Mike Frysinger86509232014-05-24 13:18:37 -0400811 parser.error('--upload-board-tarball only works with gs:// URLs.\n'
812 '--upload must be a gs:// URL.')
David James8fa34ea2011-04-15 13:00:20 -0700813
Zdenek Behan86c15e92012-10-13 00:55:47 +0200814 if options.upload_board_tarball and options.prepackaged_tarball is None:
Mike Frysinger86509232014-05-24 13:18:37 -0400815 parser.error('--upload-board-tarball requires --prepackaged-tarball')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200816
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700817 if options.private:
818 if options.sync_host:
Mike Frysinger86509232014-05-24 13:18:37 -0400819 parser.error('--private and --sync-host/-s cannot be specified '
820 'together; we do not support private host prebuilts')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700821
David James8ece7ee2011-06-29 16:02:30 -0700822 if not options.upload or not options.upload.startswith('gs://'):
Mike Frysinger86509232014-05-24 13:18:37 -0400823 parser.error('--private is only valid for gs:// URLs; '
824 '--upload must be a gs:// URL.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700825
826 if options.binhost_base_url != _BINHOST_BASE_URL:
Mike Frysinger86509232014-05-24 13:18:37 -0400827 parser.error('when using --private the --binhost-base-url '
828 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700829
David Jamesc31168e2014-06-05 14:40:05 -0700830 if options.sync_binhost_conf and not options.binhost_conf_dir:
831 parser.error('--sync-binhost-conf requires --binhost-conf-dir')
832
Gilad Arnoldad333182015-05-27 15:50:41 -0700833 if options.board_overlay_tarballs and not options.board_overlay_upload_path:
834 parser.error('--board-overlay-tarball requires --board-overlay-upload-path')
835
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700836 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800837
Mike Frysingercc838832014-05-24 13:10:30 -0400838
Mike Frysinger86509232014-05-24 13:18:37 -0400839def main(argv):
David Jamesdb401072011-06-10 12:17:16 -0700840 # Set umask to a sane value so that files created as root are readable.
Mike Frysinger60ec1012013-10-21 00:11:10 -0400841 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -0700842
Mike Frysinger86509232014-05-24 13:18:37 -0400843 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -0800844
David James05bcb2b2011-02-09 09:25:47 -0800845 # Calculate a list of Packages index files to compare against. Whenever we
846 # upload a package, we check to make sure it's not already stored in one of
847 # the packages files we uploaded. This list of packages files might contain
848 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800849 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800850
David James8ece7ee2011-06-29 16:02:30 -0700851 if options.set_version:
852 version = options.set_version
853 else:
854 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -0700855
David Jamesc0f158a2011-02-22 16:07:29 -0800856 if options.prepend_version:
857 version = '%s-%s' % (options.prepend_version, version)
858
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700859 acl = 'public-read'
860 binhost_base_url = options.binhost_base_url
861
David Jamesadd21432013-05-21 10:04:07 -0700862 if options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700863 binhost_base_url = options.upload
David Jamesadd21432013-05-21 10:04:07 -0700864 if target:
Prathmesh Prabhu421eef22014-10-16 17:13:19 -0700865 acl = portage_util.FindOverlayFile(_GOOGLESTORAGE_GSUTIL_FILE,
866 board=target.board_variant,
867 buildroot=options.build_path)
Ben Chan067de492015-01-06 17:19:13 -0800868 if acl is None:
869 cros_build_lib.Die('No Google Storage ACL file %s found in %s overlay.',
870 _GOOGLESTORAGE_GSUTIL_FILE, target.board_variant)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700871
David Jamesb26b9312014-12-15 11:26:46 -0800872 binhost_conf_dir = None
873 if options.binhost_conf_dir:
874 binhost_conf_dir = os.path.join(options.build_path,
875 options.binhost_conf_dir)
876
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700877 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700878 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700879 options.packages, options.skip_upload,
David Jamesb26b9312014-12-15 11:26:46 -0800880 binhost_conf_dir, options.dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400881 target, options.slave_targets, version)
David Jamesc0f158a2011-02-22 16:07:29 -0800882
David James8c846492011-01-25 17:07:29 -0800883 if options.sync_host:
Mike Frysinger8092a632014-05-24 13:25:46 -0400884 uploader.SyncHostPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700885 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800886
Chris Sosa62c8ff52012-06-04 15:03:12 -0700887 if options.board or options.slave_targets:
Mike Frysinger8092a632014-05-24 13:25:46 -0400888 uploader.SyncBoardPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700889 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200890 options.upload_board_tarball,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500891 options.prepackaged_tarball,
Gilad Arnoldad333182015-05-27 15:50:41 -0700892 options.board_overlay_tarballs,
893 options.board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500894 options.toolchain_tarballs,
895 options.toolchain_upload_path)