blob: 10f7b55292d92034c73493dee188e14e89103fcd [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
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
Don Garrett88b8d782014-05-13 17:30:55 -070031from chromite.cbuildbot 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 Frysinger8e727a32013-01-16 16:57:53 -050042from chromite.lib import toolchain
Chris Sosa1dc96132012-05-11 15:40:50 -070043
David James015af872012-06-19 15:24:36 -070044# How many times to retry uploads.
45_RETRIES = 10
46
47# Multiplier for how long to sleep (in seconds) between retries; will delay
48# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
49_SLEEP_TIME = 60
50
David James5ab67e32014-10-24 08:19:59 -070051# The length of time (in seconds) that Portage should wait before refetching
52# binpkgs from the same binhost. We don't ever modify binhosts, so this should
53# be something big.
54_BINPKG_TTL = 60 * 60 * 24 * 365
55
David James8c846492011-01-25 17:07:29 -080056_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080057_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070058_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080059_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080060_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080061_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
62_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080063# Private overlays to look at for builds to filter
64# relative to build path
65_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Gabe Black40169e62014-06-17 15:23:47 -070066_GOOGLESTORAGE_GSUTIL_FILE = 'googlestorage_acl.txt'
David James3753d942014-04-23 10:55:48 -070067_BINHOST_BASE_URL = 'gs://chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080068_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
69# Created in the event of new host targets becoming available
70_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
71 'make.conf.amd64-host')}
David James8c846492011-01-25 17:07:29 -080072
73
David James4058b0d2011-12-08 21:24:50 -080074class BuildTarget(object):
75 """A board/variant/profile tuple."""
76
77 def __init__(self, board_variant, profile=None):
78 self.board_variant = board_variant
79 self.board, _, self.variant = board_variant.partition('_')
80 self.profile = profile
81
82 def __str__(self):
83 if self.profile:
84 return '%s_%s' % (self.board_variant, self.profile)
85 else:
86 return self.board_variant
87
88 def __eq__(self, other):
89 return str(other) == str(self)
90
91 def __hash__(self):
92 return hash(str(self))
93
94
David James8c846492011-01-25 17:07:29 -080095def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
96 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -050097
David James8c846492011-01-25 17:07:29 -080098 File format:
99 key="value"
100 Note quotes are added automatically
101
102 Args:
103 filename: Name of file to modify.
104 value: Value to write with the key.
105 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800106
107 Returns:
108 True if changes were made to the file.
David James8c846492011-01-25 17:07:29 -0800109 """
110 if os.path.exists(filename):
111 file_fh = open(filename)
112 else:
113 file_fh = open(filename, 'w+')
114 file_lines = []
115 found = False
David Jamesb26b9312014-12-15 11:26:46 -0800116 made_changes = False
David James8c846492011-01-25 17:07:29 -0800117 keyval_str = '%(key)s=%(value)s'
118 for line in file_fh:
119 # Strip newlines from end of line. We already add newlines below.
120 line = line.rstrip("\n")
121
122 if len(line.split('=')) != 2:
123 # Skip any line that doesn't fit key=val.
124 file_lines.append(line)
125 continue
126
127 file_var, file_val = line.split('=')
128 if file_var == key:
129 found = True
Mike Frysingerff441bf2014-05-24 13:47:21 -0400130 print('Updating %s=%s to %s="%s"' % (file_var, file_val, key, value))
David James8c846492011-01-25 17:07:29 -0800131 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800132 made_changes |= (file_val != value)
David James8c846492011-01-25 17:07:29 -0800133 file_lines.append(keyval_str % {'key': key, 'value': value})
134 else:
135 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
136
137 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700138 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800139 made_changes = True
David James8c846492011-01-25 17:07:29 -0800140 file_lines.append(keyval_str % {'key': key, 'value': value})
141
142 file_fh.close()
143 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700144 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David Jamesb26b9312014-12-15 11:26:46 -0800145 return made_changes
David James8c846492011-01-25 17:07:29 -0800146
147
Mike Frysinger5b34d732013-01-17 15:11:58 -0500148def RevGitFile(filename, data, retries=5, dryrun=False):
David James8c846492011-01-25 17:07:29 -0800149 """Update and push the git file.
150
Mike Frysinger5b34d732013-01-17 15:11:58 -0500151 Args:
152 filename: file to modify that is in a git repo already
153 data: A dict of key/values to update in |filename|
154 retries: The number of times to retry before giving up, default: 5
Matt Tennante8179042013-10-01 15:47:32 -0700155 dryrun: If True, do not actually commit the change.
David James8c846492011-01-25 17:07:29 -0800156 """
157 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700158 cwd = os.path.abspath(os.path.dirname(filename))
David James97d95872012-11-16 15:09:56 -0800159 commit = git.RunGit(cwd, ['rev-parse', 'HEAD']).output.rstrip()
Mike Frysinger5b34d732013-01-17 15:11:58 -0500160 description = '%s: updating %s' % (os.path.basename(filename),
161 ', '.join(data.keys()))
162 # UpdateLocalFile will print out the keys/values for us.
Mike Frysingerff441bf2014-05-24 13:47:21 -0400163 print('Revving git file %s' % filename)
David James66009462012-03-25 10:08:38 -0700164
David James8c846492011-01-25 17:07:29 -0800165 try:
David James97d95872012-11-16 15:09:56 -0800166 git.CreatePushBranch(prebuilt_branch, cwd)
Mike Frysinger5b34d732013-01-17 15:11:58 -0500167 for key, value in data.iteritems():
168 UpdateLocalFile(filename, value, key)
David James97d95872012-11-16 15:09:56 -0800169 git.RunGit(cwd, ['add', filename])
170 git.RunGit(cwd, ['commit', '-m', description])
171 git.PushWithRetry(prebuilt_branch, cwd, dryrun=dryrun, retries=retries)
David James8c846492011-01-25 17:07:29 -0800172 finally:
David James67d73252013-09-19 17:33:12 -0700173 git.RunGit(cwd, ['checkout', commit])
David James8c846492011-01-25 17:07:29 -0800174
175
176def GetVersion():
177 """Get the version to put in LATEST and update the git version with."""
Mike Frysinger0b92cfe2012-12-13 02:13:45 -0500178 return datetime.datetime.now().strftime('%Y.%m.%d.%H%M%S')
David James8c846492011-01-25 17:07:29 -0800179
180
Mike Frysinger540883b2014-05-24 13:46:16 -0400181def _GsUpload(gs_context, acl, local_file, remote_file):
David James8c846492011-01-25 17:07:29 -0800182 """Upload to GS bucket.
183
184 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400185 gs_context: A lib.gs.GSContext instance.
186 acl: The ACL to use for uploading the file.
Matt Tennante8179042013-10-01 15:47:32 -0700187 local_file: The local file to be uploaded.
188 remote_file: The remote location to upload to.
David James8c846492011-01-25 17:07:29 -0800189 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700190 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
191 'authenticated-read', 'bucket-owner-full-control',
192 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700193 if acl in CANNED_ACLS:
David James9374aac2013-10-08 16:00:17 -0700194 gs_context.Copy(local_file, remote_file, acl=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700195 else:
196 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700197 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James7b7d7c32015-04-02 13:48:17 -0700198 # We set version=0 here to ensure that the ACL is set only once (see
199 # http://b/15883752#comment54).
200 try:
201 gs_context.Copy(local_file, remote_file, version=0)
202 except gs.GSContextPreconditionFailed as ex:
203 # If we received a GSContextPreconditionFailed error, we know that the
204 # file exists now, but we don't know whether our specific update
205 # succeeded. See http://b/15883752#comment62
206 logging.warning(
207 'Assuming upload succeeded despite PreconditionFailed errors: %s', ex)
208
Gabe Black40169e62014-06-17 15:23:47 -0700209 if acl.endswith('.xml'):
210 # Apply the passed in ACL xml file to the uploaded object.
211 gs_context.SetACL(remote_file, acl=acl)
212 else:
213 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700214
Mike Frysingercc838832014-05-24 13:10:30 -0400215
Mike Frysinger540883b2014-05-24 13:46:16 -0400216def RemoteUpload(gs_context, acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800217 """Upload to google storage.
218
219 Create a pool of process and call _GsUpload with the proper arguments.
220
221 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400222 gs_context: A lib.gs.GSContext instance.
David Jamesfd0b0852011-02-23 11:15:36 -0800223 acl: The canned acl used for uploading. acl can be one of: "public-read",
224 "public-read-write", "authenticated-read", "bucket-owner-read",
225 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800226 files: dictionary with keys to local files and values to remote path.
227 pool: integer of maximum proesses to have at the same time.
228
229 Returns:
230 Return a set of tuple arguments of the failed uploads
231 """
Mike Frysinger540883b2014-05-24 13:46:16 -0400232 upload = functools.partial(_GsUpload, gs_context, acl)
233 tasks = [[key, value] for key, value in files.iteritems()]
234 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800235
236
237def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
238 """Build a dictionary of local remote file key pairs to upload.
239
240 Args:
241 base_local_path: The base path to the files on the local hard drive.
Matt Tennante8179042013-10-01 15:47:32 -0700242 base_remote_path: The base path to the remote paths.
David James8c846492011-01-25 17:07:29 -0800243 pkgs: The packages to upload.
244
245 Returns:
246 Returns a dictionary of local_path/remote_path pairs
247 """
248 upload_files = {}
249 for pkg in pkgs:
250 suffix = pkg['CPV'] + '.tbz2'
251 local_path = os.path.join(base_local_path, suffix)
David James7b7d7c32015-04-02 13:48:17 -0700252 assert os.path.exists(local_path), '%s does not exist' % local_path
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800253 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800254
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800255 if pkg.get('DEBUG_SYMBOLS') == 'yes':
256 debugsuffix = pkg['CPV'] + '.debug.tbz2'
257 local_path = os.path.join(base_local_path, debugsuffix)
258 assert os.path.exists(local_path)
259 upload_files[local_path] = os.path.join(base_remote_path, debugsuffix)
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800260
David James8c846492011-01-25 17:07:29 -0800261 return upload_files
262
Mike Frysingercc838832014-05-24 13:10:30 -0400263
Peter Mayo950e41a2014-02-06 21:07:33 +0000264def GetBoardOverlay(build_path, target):
David Jamese5867812012-10-19 12:02:20 -0700265 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500266
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400267 Args:
268 build_path: The path to the root of the build directory
269 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500270
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400271 Returns:
272 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800273 """
David Jamese5867812012-10-19 12:02:20 -0700274 board = target.board_variant
Alex Deymo075c2292014-09-04 18:31:50 -0700275 overlays = portage_util.FindOverlays(constants.BOTH_OVERLAYS, board,
276 buildroot=build_path)
David Jamese5867812012-10-19 12:02:20 -0700277 # We only care about the last entry.
278 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800279
280
281def DeterminePrebuiltConfFile(build_path, target):
282 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
283
Mike Frysinger1a736a82013-12-12 01:50:59 -0500284 Args:
285 build_path: The path to the root of the build directory
286 target: String representation of the board. This includes host and board
287 targets
David James8c846492011-01-25 17:07:29 -0800288
Mike Frysinger1a736a82013-12-12 01:50:59 -0500289 Returns:
290 A string path to a prebuilt.conf file to be updated.
David James8c846492011-01-25 17:07:29 -0800291 """
David James4058b0d2011-12-08 21:24:50 -0800292 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800293 # We are host.
294 # Without more examples of hosts this is a kludge for now.
295 # TODO(Scottz): as new host targets come online expand this to
296 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800297 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800298 else:
299 # We are a board
Peter Mayo950e41a2014-02-06 21:07:33 +0000300 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800301 make_path = os.path.join(board, 'prebuilt.conf')
302
303 return make_path
304
305
306def UpdateBinhostConfFile(path, key, value):
307 """Update binhost config file file with key=value.
308
309 Args:
310 path: Filename to update.
311 key: Key to update.
312 value: New value for key.
313 """
David Jamesb26b9312014-12-15 11:26:46 -0800314 cwd, filename = os.path.split(os.path.abspath(path))
Brian Harringaf019fb2012-05-10 15:06:13 -0700315 osutils.SafeMakedirs(cwd)
David Jamesf6e8fb72013-05-10 08:58:43 -0700316 if not git.GetCurrentBranch(cwd):
317 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
Brian Harring22edb442012-05-11 23:55:18 -0700318 osutils.WriteFile(path, '', mode='a')
David Jamesb26b9312014-12-15 11:26:46 -0800319 if UpdateLocalFile(path, value, key):
320 desc = '%s: %s %s' % (filename, 'updating' if value else 'clearing', key)
321 git.AddPath(path)
322 git.Commit(cwd, desc)
David James8c846492011-01-25 17:07:29 -0800323
Mike Frysinger212e4292014-05-24 15:15:44 -0400324def GenerateHtmlIndex(files, index, board, version):
325 """Given the list of |files|, generate an index.html at |index|.
326
327 Args:
328 files: The list of files to link to.
329 index: The path to the html index.
330 board: Name of the board this index is for.
331 version: Build version this index is for.
332 """
333 head = """<html>
334<head>
335 <title>Package Prebuilt Index: %(board)s / %(version)s</title>
336</head>
337<body>
338<h2>Package Prebuilt Index: %(board)s / %(version)s</h2>"""
339 head %= {
340 'board': board,
341 'version': version,
342 }
343
344 files = files + [
345 '.|Google Storage Index',
346 '..|',
347 ]
348 commands.GenerateHtmlIndex(index, files, head=head)
349
350
David Jamesce093af2011-02-23 15:21:58 -0800351def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800352 """Grab all of the packages files associated with a list of binhost_urls.
353
David James05bcb2b2011-02-09 09:25:47 -0800354 Args:
355 binhost_urls: The URLs for the directories containing the Packages files we
356 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800357
358 Returns:
359 A list of PackageIndex objects.
360 """
361 pkg_indexes = []
362 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700363 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800364 if pkg_index:
365 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800366 return pkg_indexes
367
368
David Jamesc0f158a2011-02-22 16:07:29 -0800369class PrebuiltUploader(object):
370 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800371
Mike Frysinger86509232014-05-24 13:18:37 -0400372 def __init__(self, upload_location, acl, binhost_base_url, pkg_indexes,
373 build_path, packages, skip_upload, binhost_conf_dir, dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400374 target, slave_targets, version):
David Jamesc0f158a2011-02-22 16:07:29 -0800375 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800376
David Jamesc0f158a2011-02-22 16:07:29 -0800377 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800378
David Jamesc0f158a2011-02-22 16:07:29 -0800379 Args:
380 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800381 acl: The canned acl used for uploading to Google Storage. acl can be one
382 of: "public-read", "public-read-write", "authenticated-read",
Matt Tennante8179042013-10-01 15:47:32 -0700383 "bucket-owner-read", "bucket-owner-full-control", "project-private",
384 or "private" (see "gsutil help acls"). If we are not uploading to
385 Google Storage, this parameter is unused.
David Jamesfd0b0852011-02-23 11:15:36 -0800386 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800387 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
388 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700389 build_path: The path to the directory containing the chroot.
390 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700391 skip_upload: Don't actually upload the tarballs.
392 binhost_conf_dir: Directory where to store binhost.conf files.
Mike Frysinger86509232014-05-24 13:18:37 -0400393 dryrun: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800394 target: BuildTarget managed by this builder.
395 slave_targets: List of BuildTargets managed by slave builders.
Mike Frysinger8092a632014-05-24 13:25:46 -0400396 version: A unique string, intended to be included in the upload path,
397 which identifies the version number of the uploaded prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800398 """
399 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800400 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800401 self._binhost_base_url = binhost_base_url
402 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700403 self._build_path = build_path
404 self._packages = set(packages)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500405 self._found_packages = set()
David James8ece7ee2011-06-29 16:02:30 -0700406 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700407 self._binhost_conf_dir = binhost_conf_dir
Mike Frysinger86509232014-05-24 13:18:37 -0400408 self._dryrun = dryrun
David James4058b0d2011-12-08 21:24:50 -0800409 self._target = target
410 self._slave_targets = slave_targets
Mike Frysinger8092a632014-05-24 13:25:46 -0400411 self._version = version
Mike Frysinger540883b2014-05-24 13:46:16 -0400412 self._gs_context = gs.GSContext(retries=_RETRIES, sleep=_SLEEP_TIME,
413 dry_run=self._dryrun)
414
415 def _Upload(self, local_file, remote_file):
416 """Wrapper around _GsUpload"""
417 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700418
419 def _ShouldFilterPackage(self, pkg):
420 if not self._packages:
421 return False
422 pym_path = os.path.abspath(os.path.join(self._build_path, _PYM_PATH))
David James710b7dc2012-02-07 16:49:59 -0800423 sys.path.insert(0, pym_path)
Don Garrett25f309a2014-03-19 14:02:12 -0700424 # pylint: disable=F0401
David James615e5b52011-06-03 11:10:15 -0700425 import portage.versions
426 cat, pkgname = portage.versions.catpkgsplit(pkg['CPV'])[0:2]
427 cp = '%s/%s' % (cat, pkgname)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500428 self._found_packages.add(cp)
David James615e5b52011-06-03 11:10:15 -0700429 return pkgname not in self._packages and cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800430
David Jamesc0f158a2011-02-22 16:07:29 -0800431 def _UploadPrebuilt(self, package_path, url_suffix):
432 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800433
David Jamesc0f158a2011-02-22 16:07:29 -0800434 Args:
435 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800436 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesc0f158a2011-02-22 16:07:29 -0800437 """
David Jamesc0f158a2011-02-22 16:07:29 -0800438 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700439 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800440 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700441 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800442 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500443 unmatched_pkgs = self._packages - self._found_packages
444 if unmatched_pkgs:
Ralph Nathan446aee92015-03-23 14:44:56 -0700445 logging.warning('unable to match packages: %r' % unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800446
David Jamesc0f158a2011-02-22 16:07:29 -0800447 # Write Packages file.
David James5ab67e32014-10-24 08:19:59 -0700448 pkg_index.header['TTL'] = _BINPKG_TTL
David Jamesc0f158a2011-02-22 16:07:29 -0800449 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800450
David Jamesc0f158a2011-02-22 16:07:29 -0800451 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700452 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800453
Alex Deymo541ea6f2014-12-23 01:18:14 -0800454 # Build list of files to upload. Manually include the dev-only files but
455 # skip them if not present.
456 # TODO(deymo): Upload dev-only-extras.tbz2 as dev-only-extras.tar.bz2
457 # outside packages/ directory. See crbug.com/448178 for details.
458 if os.path.exists(os.path.join(package_path, 'dev-only-extras.tbz2')):
459 uploads.append({'CPV': 'dev-only-extras'})
David James015af872012-06-19 15:24:36 -0700460 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
461 remote_file = '%s/Packages' % remote_location.rstrip('/')
462 upload_files[tmp_packages_file.name] = remote_file
463
Mike Frysinger540883b2014-05-24 13:46:16 -0400464 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800465
Mike Frysinger212e4292014-05-24 15:15:44 -0400466 with tempfile.NamedTemporaryFile(
467 prefix='chromite.upload_prebuilts.index.') as index:
468 GenerateHtmlIndex(
469 [x[len(remote_location) + 1:] for x in upload_files.values()],
470 index.name, self._target, self._version)
471 self._Upload(index.name, '%s/index.html' % remote_location.rstrip('/'))
472
473 link_name = 'Prebuilts[%s]: %s' % (self._target, self._version)
474 url = '%s%s/index.html' % (gs.PUBLIC_BASE_HTTPS_URL,
475 remote_location[len(gs.BASE_GS_URL):])
476 cros_build_lib.PrintBuildbotLink(link_name, url)
477
Mike Frysinger8092a632014-05-24 13:25:46 -0400478 def _UploadSdkTarball(self, board_path, url_suffix, prepackaged,
Gilad Arnoldad333182015-05-27 15:50:41 -0700479 board_overlay_tarballs, board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500480 toolchain_tarballs, toolchain_upload_path):
481 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700482
483 Args:
484 board_path: The path to the board dir.
485 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan62a57792012-08-31 15:09:08 +0200486 prepackaged: If given, a tarball that has been packaged outside of this
487 script and should be used.
Gilad Arnoldad333182015-05-27 15:50:41 -0700488 board_overlay_tarballs: List of board-specific overlay tarball
489 specifications to upload. Items take the form "board:/path/to/tarball".
490 board_overlay_upload_path: Path template under the bucket to place
491 board-specific overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500492 toolchain_tarballs: List of toolchain tarballs to upload.
493 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David James8fa34ea2011-04-15 13:00:20 -0700494 """
495 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
496 assert remote_location.startswith('gs://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200497 boardname = os.path.basename(board_path.rstrip('/'))
498 # We do not upload non SDK board tarballs,
499 assert boardname == constants.CHROOT_BUILDER_BOARD
500 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200501
Mike Frysinger8092a632014-05-24 13:25:46 -0400502 version_str = self._version[len('chroot-'):]
Mike Frysinger8e727a32013-01-16 16:57:53 -0500503 remote_tarfile = toolchain.GetSdkURL(
504 for_gsutil=True, suburl='cros-sdk-%s.tar.xz' % (version_str,))
Zdenek Behan86c15e92012-10-13 00:55:47 +0200505 # For SDK, also upload the manifest which is guaranteed to exist
506 # by the builderstage.
Mike Frysinger540883b2014-05-24 13:46:16 -0400507 self._Upload(prepackaged + '.Manifest', remote_tarfile + '.Manifest')
508 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200509
Gilad Arnoldad333182015-05-27 15:50:41 -0700510 # Upload board-specific SDK overlays and toolchain tarballs, if given.
511 for tarball_list, upload_path, qualifier_name in (
512 (board_overlay_tarballs, board_overlay_upload_path, 'board'),
513 (toolchain_tarballs, toolchain_upload_path, 'target')):
514 for tarball in tarball_list:
515 qualifier_val, local_path = tarball.split(':')
516 suburl = upload_path % {qualifier_name: qualifier_val}
517 remote_path = toolchain.GetSdkURL(for_gsutil=True, suburl=suburl)
518 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500519
Zdenek Behan86c15e92012-10-13 00:55:47 +0200520 # Finally, also update the pointer to the latest SDK on which polling
521 # scripts rely.
David James4bc13702013-03-26 08:08:04 -0700522 with osutils.TempDir() as tmpdir:
Zdenek Behan86c15e92012-10-13 00:55:47 +0200523 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
Mike Frysinger8e727a32013-01-16 16:57:53 -0500524 remote_pointerfile = toolchain.GetSdkURL(for_gsutil=True,
525 suburl='cros-sdk-latest.conf')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200526 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
Mike Frysinger540883b2014-05-24 13:46:16 -0400527 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200528
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700529 def _GetTargets(self):
530 """Retuns the list of targets to use."""
531 targets = self._slave_targets[:]
532 if self._target:
533 targets.append(self._target)
534
535 return targets
536
Mike Frysinger8092a632014-05-24 13:25:46 -0400537 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800538 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800539
David Jamesc0f158a2011-02-22 16:07:29 -0800540 This function will sync both the standard host packages, plus the host
541 packages associated with all targets that have been "setup" with the
542 current host's chroot. For instance, if this host has been used to build
543 x86-generic, it will sync the host packages associated with
544 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
545 it will also sync the host packages associated with
546 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800547
David Jamesc0f158a2011-02-22 16:07:29 -0800548 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800549 key: The variable key to update in the git file.
550 git_sync: If set, update make.conf of target to reference the latest
551 prebuilt packages generated here.
552 sync_binhost_conf: If set, update binhost config file in
553 chromiumos-overlay for the host.
554 """
David Jamese2488642011-11-14 16:15:20 -0800555 # Slave boards are listed before the master board so that the master board
556 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
557 # over preflight host prebuilts from other builders.)
558 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700559 for target in self._GetTargets():
Mike Frysinger8092a632014-05-24 13:25:46 -0400560 url_suffix = _REL_HOST_PATH % {'version': self._version,
David James4058b0d2011-12-08 21:24:50 -0800561 'host_arch': _HOST_ARCH,
562 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800563 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800564
Mike Frysinger540883b2014-05-24 13:46:16 -0400565 if self._target == target and not self._skip_upload:
David Jamese2488642011-11-14 16:15:20 -0800566 # Upload prebuilts.
567 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
568 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700569
David Jamese2488642011-11-14 16:15:20 -0800570 # Record URL where prebuilts were uploaded.
571 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
572 packages_url_suffix.rstrip('/')))
573
David James20b2b6f2011-11-18 15:11:58 -0800574 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700575 if git_sync:
David Jamesb26b9312014-12-15 11:26:46 -0800576 git_file = os.path.join(self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH])
Mike Frysinger86509232014-05-24 13:18:37 -0400577 RevGitFile(git_file, {key: binhost}, dryrun=self._dryrun)
David James8ece7ee2011-06-29 16:02:30 -0700578 if sync_binhost_conf:
David Jamesb26b9312014-12-15 11:26:46 -0800579 binhost_conf = os.path.join(
580 self._binhost_conf_dir, 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800581 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800582
Mike Frysinger8092a632014-05-24 13:25:46 -0400583 def SyncBoardPrebuilts(self, key, git_sync, sync_binhost_conf,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500584 upload_board_tarball, prepackaged_board,
Gilad Arnoldad333182015-05-27 15:50:41 -0700585 board_overlay_tarballs, board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500586 toolchain_tarballs, toolchain_upload_path):
David Jamesc0f158a2011-02-22 16:07:29 -0800587 """Synchronize board prebuilt files.
588
589 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800590 key: The variable key to update in the git file.
591 git_sync: If set, update make.conf of target to reference the latest
592 prebuilt packages generated here.
593 sync_binhost_conf: If set, update binhost config file in
594 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700595 upload_board_tarball: Include a tarball of the board in our upload.
Zdenek Behan62a57792012-08-31 15:09:08 +0200596 prepackaged_board: A tarball of the board built outside of this script.
Gilad Arnoldad333182015-05-27 15:50:41 -0700597 board_overlay_tarballs: List of board-specific overlay tarball
598 specifications to upload. Items take the form "board:/path/to/tarball".
599 board_overlay_upload_path: Path template under the bucket to place
600 board-specific overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500601 toolchain_tarballs: A list of toolchain tarballs to upload.
602 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David Jamesc0f158a2011-02-22 16:07:29 -0800603 """
David Jamesb26b9312014-12-15 11:26:46 -0800604 updated_binhosts = set()
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700605 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800606 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800607 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800608 package_path = os.path.join(board_path, 'packages')
Mike Frysinger8092a632014-05-24 13:25:46 -0400609 url_suffix = _REL_BOARD_PATH % {'target': target,
610 'version': self._version}
David Jamese2488642011-11-14 16:15:20 -0800611 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700612
Matt Tennante8179042013-10-01 15:47:32 -0700613 # Process the target board differently if it is the main --board.
Mike Frysinger540883b2014-05-24 13:46:16 -0400614 if self._target == target and not self._skip_upload:
Matt Tennante8179042013-10-01 15:47:32 -0700615 # This strips "chroot" prefix because that is sometimes added as the
616 # --prepend-version argument (e.g. by chromiumos-sdk bot).
617 # TODO(build): Clean it up to be less hard-coded.
Mike Frysinger8092a632014-05-24 13:25:46 -0400618 version_str = self._version[len('chroot-'):]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500619
David Jamese2488642011-11-14 16:15:20 -0800620 # Upload board tarballs in the background.
621 if upload_board_tarball:
Mike Frysinger9e979b92012-11-29 02:55:09 -0500622 if toolchain_upload_path:
623 toolchain_upload_path %= {'version': version_str}
Gilad Arnoldad333182015-05-27 15:50:41 -0700624 if board_overlay_upload_path:
625 board_overlay_upload_path %= {'version': version_str}
Mike Frysinger9e979b92012-11-29 02:55:09 -0500626 tar_process = multiprocessing.Process(
627 target=self._UploadSdkTarball,
Mike Frysinger8092a632014-05-24 13:25:46 -0400628 args=(board_path, url_suffix, prepackaged_board,
Gilad Arnoldad333182015-05-27 15:50:41 -0700629 board_overlay_tarballs, board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500630 toolchain_tarballs, toolchain_upload_path))
David Jamese2488642011-11-14 16:15:20 -0800631 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700632
David Jamese2488642011-11-14 16:15:20 -0800633 # Upload prebuilts.
634 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700635
David Jamese2488642011-11-14 16:15:20 -0800636 # Make sure we finished uploading the board tarballs.
637 if upload_board_tarball:
638 tar_process.join()
639 assert tar_process.exitcode == 0
640 # TODO(zbehan): This should be done cleaner.
Zdenek Behan33a34112012-09-10 21:07:51 +0200641 if target.board == constants.CHROOT_BUILDER_BOARD:
David Jamesb26b9312014-12-15 11:26:46 -0800642 sdk_conf = os.path.join(self._binhost_conf_dir,
Mike Frysinger5b34d732013-01-17 15:11:58 -0500643 'host/sdk_version.conf')
644 sdk_settings = {
Mike Frysinger9e979b92012-11-29 02:55:09 -0500645 'SDK_LATEST_VERSION': version_str,
646 'TC_PATH': toolchain_upload_path,
Mike Frysinger5b34d732013-01-17 15:11:58 -0500647 }
Mike Frysinger86509232014-05-24 13:18:37 -0400648 RevGitFile(sdk_conf, sdk_settings, dryrun=self._dryrun)
David Jamesc0f158a2011-02-22 16:07:29 -0800649
David Jamese2488642011-11-14 16:15:20 -0800650 # Record URL where prebuilts were uploaded.
651 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
652 packages_url_suffix.rstrip('/'))
653
654 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800655 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Mike Frysinger86509232014-05-24 13:18:37 -0400656 RevGitFile(git_file, {key: url_value}, dryrun=self._dryrun)
Matt Tennante8179042013-10-01 15:47:32 -0700657
David Jamese2488642011-11-14 16:15:20 -0800658 if sync_binhost_conf:
Matt Tennante8179042013-10-01 15:47:32 -0700659 # Update the binhost configuration file in git.
David Jamesb26b9312014-12-15 11:26:46 -0800660 binhost_conf = os.path.join(
661 self._binhost_conf_dir, 'target', '%s-%s.conf' % (target, key))
662 updated_binhosts.add(binhost_conf)
David Jamese2488642011-11-14 16:15:20 -0800663 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800664
David Jamesb26b9312014-12-15 11:26:46 -0800665 if sync_binhost_conf:
666 # Clear all old binhosts. The files must be left empty in case anybody
667 # is referring to them.
668 all_binhosts = set(glob.glob(os.path.join(
669 self._binhost_conf_dir, 'target', '*-%s.conf' % key)))
670 for binhost_conf in all_binhosts - updated_binhosts:
671 UpdateBinhostConfFile(binhost_conf, key, '')
672
David James05bcb2b2011-02-09 09:25:47 -0800673
Mike Frysingerb87bb782015-06-04 02:46:50 -0400674class _AddSlaveBoardAction(argparse.Action):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700675 """Callback that adds a slave board to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400676 def __call__(self, parser, namespace, values, option_string=None):
677 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800678
679
Mike Frysingerb87bb782015-06-04 02:46:50 -0400680class _AddSlaveProfileAction(argparse.Action):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700681 """Callback that adds a slave profile to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400682 def __call__(self, parser, namespace, values, option_string=None):
683 if not namespace.slave_targets:
684 parser.error('Must specify --slave-board before --slave-profile')
685 if namespace.slave_targets[-1].profile is not None:
686 parser.error('Cannot specify --slave-profile twice for same board')
687 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800688
689
Mike Frysinger86509232014-05-24 13:18:37 -0400690def ParseOptions(argv):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700691 """Returns options given by the user and the target specified.
692
Mike Frysinger86509232014-05-24 13:18:37 -0400693 Args:
694 argv: The args to parse.
695
Mike Frysinger1a736a82013-12-12 01:50:59 -0500696 Returns:
697 A tuple containing a parsed options object and BuildTarget.
698 The target instance is None if no board is specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700699 """
Mike Frysingerb87bb782015-06-04 02:46:50 -0400700 parser = commandline.ArgumentParser()
701 parser.add_argument('-H', '--binhost-base-url', default=_BINHOST_BASE_URL,
702 help='Base URL to use for binhost in make.conf updates')
703 parser.add_argument('--previous-binhost-url', action='append', default=[],
704 help='Previous binhost URL')
705 parser.add_argument('-b', '--board',
706 help='Board type that was built on this machine')
707 parser.add_argument('-B', '--prepackaged-tarball', type='path',
708 help='Board tarball prebuilt outside of this script.')
709 parser.add_argument('--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_argument('--board-overlay-upload-path', type='path',
714 help='Path template for uploading board overlays.')
715 parser.add_argument('--toolchain-tarball', dest='toolchain_tarballs',
716 action='append', default=[],
717 help='Redistributable toolchain tarball.')
718 parser.add_argument('--toolchain-upload-path', type='path',
719 help='Path to place toolchain tarballs in the sdk tree.')
720 parser.add_argument('--profile',
721 help='Profile that was built on this machine')
722 parser.add_argument('--slave-board', default=[], action=_AddSlaveBoardAction,
723 dest='slave_targets',
724 help='Board type that was built on a slave machine. To '
725 'add a profile to this board, use --slave-profile.')
726 parser.add_argument('--slave-profile', action=_AddSlaveProfileAction,
727 help='Board profile that was built on a slave machine. '
728 'Applies to previous slave board.')
729 parser.add_argument('-p', '--build-path', required=True,
730 help='Path to the directory containing the chroot')
731 parser.add_argument('--packages', action='append', default=[],
732 help='Only include the specified packages. '
733 '(Default is to include all packages.)')
734 parser.add_argument('-s', '--sync-host', default=False, action='store_true',
735 help='Sync host prebuilts')
736 parser.add_argument('-g', '--git-sync', default=False, action='store_true',
737 help='Enable git version sync (This commits to a repo.) '
738 'This is used by full builders to commit directly '
739 'to board overlays.')
740 parser.add_argument('-u', '--upload',
741 help='Upload location')
742 parser.add_argument('-V', '--prepend-version',
743 help='Add an identifier to the front of the version')
744 parser.add_argument('-f', '--filters', action='store_true', default=False,
745 help='Turn on filtering of private ebuild packages')
746 parser.add_argument('-k', '--key', default='PORTAGE_BINHOST',
747 help='Key to update in make.conf / binhost.conf')
748 parser.add_argument('--set-version',
749 help='Specify the version string')
750 parser.add_argument('--sync-binhost-conf', default=False, action='store_true',
751 help='Update binhost.conf in chromiumos-overlay or '
752 'chromeos-overlay. Commit the changes, but don\'t '
753 'push them. This is used for preflight binhosts.')
754 parser.add_argument('--binhost-conf-dir',
755 help='Directory to commit binhost config with '
756 '--sync-binhost-conf.')
757 parser.add_argument('-P', '--private', action='store_true', default=False,
758 help='Mark gs:// uploads as private.')
759 parser.add_argument('--skip-upload', action='store_true', default=False,
760 help='Skip upload step.')
761 parser.add_argument('--upload-board-tarball', action='store_true',
762 default=False,
763 help='Upload board tarball to Google Storage.')
764 parser.add_argument('-n', '--dry-run', dest='dryrun',
765 action='store_true', default=False,
766 help='Don\'t push or upload prebuilts.')
David James8c846492011-01-25 17:07:29 -0800767
Mike Frysingerb87bb782015-06-04 02:46:50 -0400768 options = parser.parse_args(argv)
David James8ece7ee2011-06-29 16:02:30 -0700769 if not options.upload and not options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400770 parser.error('you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700771 if not options.set_version and options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400772 parser.error('If you are using --skip-upload, you must specify a '
773 'version number using --set-version.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700774
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700775 target = None
776 if options.board:
777 target = BuildTarget(options.board, options.profile)
778
779 if target in options.slave_targets:
Mike Frysinger86509232014-05-24 13:18:37 -0400780 parser.error('--board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800781
David James4058b0d2011-12-08 21:24:50 -0800782 if len(set(options.slave_targets)) != len(options.slave_targets):
Mike Frysinger86509232014-05-24 13:18:37 -0400783 parser.error('--slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800784
David James4058b0d2011-12-08 21:24:50 -0800785 if options.slave_targets and options.git_sync:
Mike Frysinger86509232014-05-24 13:18:37 -0400786 parser.error('--slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800787
David James8ece7ee2011-06-29 16:02:30 -0700788 if (options.upload_board_tarball and options.skip_upload and
789 options.board == 'amd64-host'):
Mike Frysinger86509232014-05-24 13:18:37 -0400790 parser.error('--skip-upload is not compatible with '
791 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700792
David James8ece7ee2011-06-29 16:02:30 -0700793 if (options.upload_board_tarball and not options.skip_upload and
794 not options.upload.startswith('gs://')):
Mike Frysinger86509232014-05-24 13:18:37 -0400795 parser.error('--upload-board-tarball only works with gs:// URLs.\n'
796 '--upload must be a gs:// URL.')
David James8fa34ea2011-04-15 13:00:20 -0700797
Zdenek Behan86c15e92012-10-13 00:55:47 +0200798 if options.upload_board_tarball and options.prepackaged_tarball is None:
Mike Frysinger86509232014-05-24 13:18:37 -0400799 parser.error('--upload-board-tarball requires --prepackaged-tarball')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200800
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700801 if options.private:
802 if options.sync_host:
Mike Frysinger86509232014-05-24 13:18:37 -0400803 parser.error('--private and --sync-host/-s cannot be specified '
804 'together; we do not support private host prebuilts')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700805
David James8ece7ee2011-06-29 16:02:30 -0700806 if not options.upload or not options.upload.startswith('gs://'):
Mike Frysinger86509232014-05-24 13:18:37 -0400807 parser.error('--private is only valid for gs:// URLs; '
808 '--upload must be a gs:// URL.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700809
810 if options.binhost_base_url != _BINHOST_BASE_URL:
Mike Frysinger86509232014-05-24 13:18:37 -0400811 parser.error('when using --private the --binhost-base-url '
812 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700813
David Jamesc31168e2014-06-05 14:40:05 -0700814 if options.sync_binhost_conf and not options.binhost_conf_dir:
815 parser.error('--sync-binhost-conf requires --binhost-conf-dir')
816
Gilad Arnoldad333182015-05-27 15:50:41 -0700817 if options.board_overlay_tarballs and not options.board_overlay_upload_path:
818 parser.error('--board-overlay-tarball requires --board-overlay-upload-path')
819
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700820 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800821
Mike Frysingercc838832014-05-24 13:10:30 -0400822
Mike Frysinger86509232014-05-24 13:18:37 -0400823def main(argv):
David Jamesdb401072011-06-10 12:17:16 -0700824 # Set umask to a sane value so that files created as root are readable.
Mike Frysinger60ec1012013-10-21 00:11:10 -0400825 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -0700826
Mike Frysinger86509232014-05-24 13:18:37 -0400827 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -0800828
David James05bcb2b2011-02-09 09:25:47 -0800829 # Calculate a list of Packages index files to compare against. Whenever we
830 # upload a package, we check to make sure it's not already stored in one of
831 # the packages files we uploaded. This list of packages files might contain
832 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800833 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800834
David James8ece7ee2011-06-29 16:02:30 -0700835 if options.set_version:
836 version = options.set_version
837 else:
838 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -0700839
David Jamesc0f158a2011-02-22 16:07:29 -0800840 if options.prepend_version:
841 version = '%s-%s' % (options.prepend_version, version)
842
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700843 acl = 'public-read'
844 binhost_base_url = options.binhost_base_url
845
David Jamesadd21432013-05-21 10:04:07 -0700846 if options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700847 binhost_base_url = options.upload
David Jamesadd21432013-05-21 10:04:07 -0700848 if target:
Prathmesh Prabhu421eef22014-10-16 17:13:19 -0700849 acl = portage_util.FindOverlayFile(_GOOGLESTORAGE_GSUTIL_FILE,
850 board=target.board_variant,
851 buildroot=options.build_path)
Ben Chan067de492015-01-06 17:19:13 -0800852 if acl is None:
853 cros_build_lib.Die('No Google Storage ACL file %s found in %s overlay.',
854 _GOOGLESTORAGE_GSUTIL_FILE, target.board_variant)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700855
David Jamesb26b9312014-12-15 11:26:46 -0800856 binhost_conf_dir = None
857 if options.binhost_conf_dir:
858 binhost_conf_dir = os.path.join(options.build_path,
859 options.binhost_conf_dir)
860
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700861 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700862 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700863 options.packages, options.skip_upload,
David Jamesb26b9312014-12-15 11:26:46 -0800864 binhost_conf_dir, options.dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400865 target, options.slave_targets, version)
David Jamesc0f158a2011-02-22 16:07:29 -0800866
David James8c846492011-01-25 17:07:29 -0800867 if options.sync_host:
Mike Frysinger8092a632014-05-24 13:25:46 -0400868 uploader.SyncHostPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700869 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800870
Chris Sosa62c8ff52012-06-04 15:03:12 -0700871 if options.board or options.slave_targets:
Mike Frysinger8092a632014-05-24 13:25:46 -0400872 uploader.SyncBoardPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700873 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200874 options.upload_board_tarball,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500875 options.prepackaged_tarball,
Gilad Arnoldad333182015-05-27 15:50:41 -0700876 options.board_overlay_tarballs,
877 options.board_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500878 options.toolchain_tarballs,
879 options.toolchain_upload_path)