blob: 51bcb8b387f798d942774df846e0bbd6ceaaa08e [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Don Garrett13cbbff2016-08-09 14:18:38 -07002 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
David James8c846492011-01-25 17:07:29 -08003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
Brian Harringaf019fb2012-05-10 15:06:13 -07006"""This script is used to upload host prebuilts as well as board BINHOSTS.
David James8c846492011-01-25 17:07:29 -08007
David James015af872012-06-19 15:24:36 -07008Prebuilts are uploaded using gsutil to Google Storage. After these prebuilts
9are successfully uploaded, a file is updated with the proper BINHOST version.
David James8c846492011-01-25 17:07:29 -080010
11To read more about prebuilts/binhost binary packages please refer to:
David James015af872012-06-19 15:24:36 -070012http://goto/chromeos-prebuilts
David James8c846492011-01-25 17:07:29 -080013
14Example of uploading prebuilt amd64 host files to Google Storage:
David Jamesc5cbd472012-06-19 16:25:45 -070015upload_prebuilts -p /b/cbuild/build -s -u gs://chromeos-prebuilt
David James8c846492011-01-25 17:07:29 -080016
17Example of uploading x86-dogfood binhosts to Google Storage:
David Jamesc5cbd472012-06-19 16:25:45 -070018upload_prebuilts -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
David James8c846492011-01-25 17:07:29 -080019"""
20
Mike Frysingerff441bf2014-05-24 13:47:21 -040021from __future__ import print_function
22
Mike Frysingerb87bb782015-06-04 02:46:50 -040023import argparse
Chris Sosa1dc96132012-05-11 15:40:50 -070024import datetime
Mike Frysinger540883b2014-05-24 13:46:16 -040025import functools
David Jamesb26b9312014-12-15 11:26:46 -080026import glob
Chris Sosa1dc96132012-05-11 15:40:50 -070027import multiprocessing
Chris Sosa1dc96132012-05-11 15:40:50 -070028import os
Mike Frysinger72b7cf92020-04-19 06:00:51 -040029import sys
Mike Frysinger212e4292014-05-24 15:15:44 -040030import tempfile
Chris Sosa1dc96132012-05-11 15:40:50 -070031
Aviv Keshetb7519e12016-10-04 00:50:00 -070032from chromite.lib import constants
David James14e97772014-06-04 18:44:49 -070033from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080034from chromite.lib import binpkg
Mike Frysinger86509232014-05-24 13:18:37 -040035from chromite.lib import commandline
Chris Sosa1dc96132012-05-11 15:40:50 -070036from chromite.lib import cros_build_lib
Ralph Nathan446aee92015-03-23 14:44:56 -070037from chromite.lib import cros_logging as logging
David James97d95872012-11-16 15:09:56 -080038from chromite.lib import git
Julio Hurtado0c7e3412021-04-29 21:36:26 +000039from chromite.lib import gerrit
Brian Harring7904b482012-08-08 02:54:12 -070040from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070041from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080042from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070043from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050044from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060045from chromite.lib.parser import package_info
Chris Sosa1dc96132012-05-11 15:40:50 -070046
Mike Frysinger72b7cf92020-04-19 06:00:51 -040047
48assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
49
50
David James015af872012-06-19 15:24:36 -070051# How many times to retry uploads.
52_RETRIES = 10
53
54# Multiplier for how long to sleep (in seconds) between retries; will delay
55# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
56_SLEEP_TIME = 60
57
David James5ab67e32014-10-24 08:19:59 -070058# The length of time (in seconds) that Portage should wait before refetching
59# binpkgs from the same binhost. We don't ever modify binhosts, so this should
60# be something big.
61_BINPKG_TTL = 60 * 60 * 24 * 365
62
David James8c846492011-01-25 17:07:29 -080063_HOST_PACKAGES_PATH = 'chroot/var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080064_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070065_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080066_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080067_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080068_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
69_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080070# Private overlays to look at for builds to filter
71# relative to build path
72_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Gabe Black40169e62014-06-17 15:23:47 -070073_GOOGLESTORAGE_GSUTIL_FILE = 'googlestorage_acl.txt'
David James3753d942014-04-23 10:55:48 -070074_BINHOST_BASE_URL = 'gs://chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080075_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
76# Created in the event of new host targets becoming available
77_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
78 'make.conf.amd64-host')}
David James8c846492011-01-25 17:07:29 -080079
80
David James4058b0d2011-12-08 21:24:50 -080081class BuildTarget(object):
82 """A board/variant/profile tuple."""
83
84 def __init__(self, board_variant, profile=None):
85 self.board_variant = board_variant
86 self.board, _, self.variant = board_variant.partition('_')
87 self.profile = profile
88
89 def __str__(self):
90 if self.profile:
91 return '%s_%s' % (self.board_variant, self.profile)
92 else:
93 return self.board_variant
94
95 def __eq__(self, other):
96 return str(other) == str(self)
97
98 def __hash__(self):
99 return hash(str(self))
100
101
David James8c846492011-01-25 17:07:29 -0800102def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
103 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500104
David James8c846492011-01-25 17:07:29 -0800105 File format:
106 key="value"
107 Note quotes are added automatically
108
109 Args:
110 filename: Name of file to modify.
111 value: Value to write with the key.
112 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800113
114 Returns:
115 True if changes were made to the file.
David James8c846492011-01-25 17:07:29 -0800116 """
117 if os.path.exists(filename):
118 file_fh = open(filename)
119 else:
120 file_fh = open(filename, 'w+')
121 file_lines = []
122 found = False
David Jamesb26b9312014-12-15 11:26:46 -0800123 made_changes = False
David James8c846492011-01-25 17:07:29 -0800124 keyval_str = '%(key)s=%(value)s'
125 for line in file_fh:
126 # Strip newlines from end of line. We already add newlines below.
Mike Frysinger80de5012019-08-01 14:10:53 -0400127 line = line.rstrip('\n')
David James8c846492011-01-25 17:07:29 -0800128
129 if len(line.split('=')) != 2:
130 # Skip any line that doesn't fit key=val.
131 file_lines.append(line)
132 continue
133
134 file_var, file_val = line.split('=')
135 if file_var == key:
136 found = True
Mike Frysingerff441bf2014-05-24 13:47:21 -0400137 print('Updating %s=%s to %s="%s"' % (file_var, file_val, key, value))
David James8c846492011-01-25 17:07:29 -0800138 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800139 made_changes |= (file_val != value)
David James8c846492011-01-25 17:07:29 -0800140 file_lines.append(keyval_str % {'key': key, 'value': value})
141 else:
142 file_lines.append(keyval_str % {'key': file_var, 'value': file_val})
143
144 if not found:
Brian Harring2a014302012-05-12 00:53:33 -0700145 value = '"%s"' % value
David Jamesb26b9312014-12-15 11:26:46 -0800146 made_changes = True
David James8c846492011-01-25 17:07:29 -0800147 file_lines.append(keyval_str % {'key': key, 'value': value})
148
149 file_fh.close()
150 # write out new file
Brian Harringaf019fb2012-05-10 15:06:13 -0700151 osutils.WriteFile(filename, '\n'.join(file_lines) + '\n')
David Jamesb26b9312014-12-15 11:26:46 -0800152 return made_changes
David James8c846492011-01-25 17:07:29 -0800153
154
Mike Nicholsa6818c52018-04-09 11:05:42 -0600155def RevGitFile(filename, data, dryrun=False):
David James8c846492011-01-25 17:07:29 -0800156 """Update and push the git file.
157
Mike Frysinger5b34d732013-01-17 15:11:58 -0500158 Args:
159 filename: file to modify that is in a git repo already
160 data: A dict of key/values to update in |filename|
Matt Tennante8179042013-10-01 15:47:32 -0700161 dryrun: If True, do not actually commit the change.
David James8c846492011-01-25 17:07:29 -0800162 """
163 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700164 cwd = os.path.abspath(os.path.dirname(filename))
Julio Hurtado0c7e3412021-04-29 21:36:26 +0000165 remote_name = git.RunGit(cwd, ['remote']).stdout.strip()
166 gerrit_helper = gerrit.GetGerritHelper(remote_name)
167 remote_url = git.RunGit(
168 cwd,['config', '--get', f'remote.{remote_name}.url']).stdout.strip()
Mike Frysinger5b34d732013-01-17 15:11:58 -0500169 description = '%s: updating %s' % (os.path.basename(filename),
170 ', '.join(data.keys()))
171 # UpdateLocalFile will print out the keys/values for us.
Mike Frysingerff441bf2014-05-24 13:47:21 -0400172 print('Revving git file %s' % filename)
Julio Hurtado0c7e3412021-04-29 21:36:26 +0000173 git.CreateBranch(cwd, prebuilt_branch, track=True)
174 for key, value in data.items():
175 UpdateLocalFile(filename, value, key)
176 git.RunGit(cwd, ['add', filename])
177 git.RunGit(cwd, ['commit', '-m', description])
Julio Hurtado2d4817d2021-04-29 16:03:58 +0000178
Julio Hurtado0c7e3412021-04-29 21:36:26 +0000179 tracking_info = git.GetTrackingBranch(
180 cwd, prebuilt_branch, for_push=True, for_checkout=False)
181 gpatch = gerrit_helper.CreateGerritPatch(
182 cwd, remote_url, ref=tracking_info.ref)
183 gerrit_helper.SetReview(gpatch, labels={'Bot-Commit': 1}, dryrun=dryrun)
184 gerrit_helper.SubmitChange(gpatch, dryrun=dryrun)
David James8c846492011-01-25 17:07:29 -0800185
186
187def GetVersion():
188 """Get the version to put in LATEST and update the git version with."""
Mike Frysinger0b92cfe2012-12-13 02:13:45 -0500189 return datetime.datetime.now().strftime('%Y.%m.%d.%H%M%S')
David James8c846492011-01-25 17:07:29 -0800190
191
Mike Frysinger540883b2014-05-24 13:46:16 -0400192def _GsUpload(gs_context, acl, local_file, remote_file):
David James8c846492011-01-25 17:07:29 -0800193 """Upload to GS bucket.
194
195 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400196 gs_context: A lib.gs.GSContext instance.
197 acl: The ACL to use for uploading the file.
Matt Tennante8179042013-10-01 15:47:32 -0700198 local_file: The local file to be uploaded.
199 remote_file: The remote location to upload to.
David James8c846492011-01-25 17:07:29 -0800200 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700201 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
202 'authenticated-read', 'bucket-owner-full-control',
203 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700204 if acl in CANNED_ACLS:
David James9374aac2013-10-08 16:00:17 -0700205 gs_context.Copy(local_file, remote_file, acl=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700206 else:
207 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700208 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James7b7d7c32015-04-02 13:48:17 -0700209 # We set version=0 here to ensure that the ACL is set only once (see
210 # http://b/15883752#comment54).
211 try:
212 gs_context.Copy(local_file, remote_file, version=0)
213 except gs.GSContextPreconditionFailed as ex:
214 # If we received a GSContextPreconditionFailed error, we know that the
215 # file exists now, but we don't know whether our specific update
216 # succeeded. See http://b/15883752#comment62
217 logging.warning(
218 'Assuming upload succeeded despite PreconditionFailed errors: %s', ex)
219
Gabe Black40169e62014-06-17 15:23:47 -0700220 if acl.endswith('.xml'):
221 # Apply the passed in ACL xml file to the uploaded object.
222 gs_context.SetACL(remote_file, acl=acl)
223 else:
Mike Frysinger5186f262017-09-13 10:26:19 -0400224 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700225
Mike Frysingercc838832014-05-24 13:10:30 -0400226
Mike Frysinger540883b2014-05-24 13:46:16 -0400227def RemoteUpload(gs_context, acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800228 """Upload to google storage.
229
230 Create a pool of process and call _GsUpload with the proper arguments.
231
232 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400233 gs_context: A lib.gs.GSContext instance.
David Jamesfd0b0852011-02-23 11:15:36 -0800234 acl: The canned acl used for uploading. acl can be one of: "public-read",
235 "public-read-write", "authenticated-read", "bucket-owner-read",
236 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800237 files: dictionary with keys to local files and values to remote path.
238 pool: integer of maximum proesses to have at the same time.
239
240 Returns:
241 Return a set of tuple arguments of the failed uploads
242 """
Mike Frysinger540883b2014-05-24 13:46:16 -0400243 upload = functools.partial(_GsUpload, gs_context, acl)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400244 tasks = [[key, value] for key, value in files.items()]
Mike Frysinger540883b2014-05-24 13:46:16 -0400245 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800246
247
248def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
249 """Build a dictionary of local remote file key pairs to upload.
250
251 Args:
252 base_local_path: The base path to the files on the local hard drive.
Matt Tennante8179042013-10-01 15:47:32 -0700253 base_remote_path: The base path to the remote paths.
David James8c846492011-01-25 17:07:29 -0800254 pkgs: The packages to upload.
255
256 Returns:
257 Returns a dictionary of local_path/remote_path pairs
258 """
259 upload_files = {}
260 for pkg in pkgs:
261 suffix = pkg['CPV'] + '.tbz2'
262 local_path = os.path.join(base_local_path, suffix)
David James7b7d7c32015-04-02 13:48:17 -0700263 assert os.path.exists(local_path), '%s does not exist' % local_path
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800264 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800265
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800266 if pkg.get('DEBUG_SYMBOLS') == 'yes':
267 debugsuffix = pkg['CPV'] + '.debug.tbz2'
268 local_path = os.path.join(base_local_path, debugsuffix)
269 assert os.path.exists(local_path)
270 upload_files[local_path] = os.path.join(base_remote_path, debugsuffix)
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800271
David James8c846492011-01-25 17:07:29 -0800272 return upload_files
273
Mike Frysingercc838832014-05-24 13:10:30 -0400274
Peter Mayo950e41a2014-02-06 21:07:33 +0000275def GetBoardOverlay(build_path, target):
David Jamese5867812012-10-19 12:02:20 -0700276 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500277
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400278 Args:
279 build_path: The path to the root of the build directory
280 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500281
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400282 Returns:
283 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800284 """
David Jamese5867812012-10-19 12:02:20 -0700285 board = target.board_variant
Alex Deymo075c2292014-09-04 18:31:50 -0700286 overlays = portage_util.FindOverlays(constants.BOTH_OVERLAYS, board,
287 buildroot=build_path)
David Jamese5867812012-10-19 12:02:20 -0700288 # We only care about the last entry.
289 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800290
291
292def DeterminePrebuiltConfFile(build_path, target):
293 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
294
Mike Frysinger1a736a82013-12-12 01:50:59 -0500295 Args:
296 build_path: The path to the root of the build directory
297 target: String representation of the board. This includes host and board
298 targets
David James8c846492011-01-25 17:07:29 -0800299
Mike Frysinger1a736a82013-12-12 01:50:59 -0500300 Returns:
301 A string path to a prebuilt.conf file to be updated.
David James8c846492011-01-25 17:07:29 -0800302 """
David James4058b0d2011-12-08 21:24:50 -0800303 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800304 # We are host.
305 # Without more examples of hosts this is a kludge for now.
306 # TODO(Scottz): as new host targets come online expand this to
307 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800308 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800309 else:
310 # We are a board
Peter Mayo950e41a2014-02-06 21:07:33 +0000311 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800312 make_path = os.path.join(board, 'prebuilt.conf')
313
314 return make_path
315
316
317def UpdateBinhostConfFile(path, key, value):
318 """Update binhost config file file with key=value.
319
320 Args:
321 path: Filename to update.
322 key: Key to update.
323 value: New value for key.
324 """
David Jamesb26b9312014-12-15 11:26:46 -0800325 cwd, filename = os.path.split(os.path.abspath(path))
Brian Harringaf019fb2012-05-10 15:06:13 -0700326 osutils.SafeMakedirs(cwd)
David Jamesf6e8fb72013-05-10 08:58:43 -0700327 if not git.GetCurrentBranch(cwd):
328 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
Brian Harring22edb442012-05-11 23:55:18 -0700329 osutils.WriteFile(path, '', mode='a')
David Jamesb26b9312014-12-15 11:26:46 -0800330 if UpdateLocalFile(path, value, key):
331 desc = '%s: %s %s' % (filename, 'updating' if value else 'clearing', key)
332 git.AddPath(path)
333 git.Commit(cwd, desc)
David James8c846492011-01-25 17:07:29 -0800334
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800335def GenerateHtmlIndex(files, index, board, version, remote_location):
Mike Frysinger212e4292014-05-24 15:15:44 -0400336 """Given the list of |files|, generate an index.html at |index|.
337
338 Args:
339 files: The list of files to link to.
340 index: The path to the html index.
341 board: Name of the board this index is for.
342 version: Build version this index is for.
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800343 remote_location: Remote gs location prebuilts are uploaded to.
Mike Frysinger212e4292014-05-24 15:15:44 -0400344 """
Don Garrett13cbbff2016-08-09 14:18:38 -0700345 title = 'Package Prebuilt Index: %s / %s' % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400346
347 files = files + [
348 '.|Google Storage Index',
349 '..|',
350 ]
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800351 commands.GenerateHtmlIndex(index, files, title=title,
352 url_base=gs.GsUrlToHttp(remote_location))
Mike Frysinger212e4292014-05-24 15:15:44 -0400353
354
David Jamesce093af2011-02-23 15:21:58 -0800355def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800356 """Grab all of the packages files associated with a list of binhost_urls.
357
David James05bcb2b2011-02-09 09:25:47 -0800358 Args:
359 binhost_urls: The URLs for the directories containing the Packages files we
360 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800361
362 Returns:
363 A list of PackageIndex objects.
364 """
365 pkg_indexes = []
366 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700367 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800368 if pkg_index:
369 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800370 return pkg_indexes
371
372
David Jamesc0f158a2011-02-22 16:07:29 -0800373class PrebuiltUploader(object):
374 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800375
Mike Frysinger86509232014-05-24 13:18:37 -0400376 def __init__(self, upload_location, acl, binhost_base_url, pkg_indexes,
377 build_path, packages, skip_upload, binhost_conf_dir, dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400378 target, slave_targets, version):
David Jamesc0f158a2011-02-22 16:07:29 -0800379 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800380
David Jamesc0f158a2011-02-22 16:07:29 -0800381 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800382
David Jamesc0f158a2011-02-22 16:07:29 -0800383 Args:
384 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800385 acl: The canned acl used for uploading to Google Storage. acl can be one
386 of: "public-read", "public-read-write", "authenticated-read",
Matt Tennante8179042013-10-01 15:47:32 -0700387 "bucket-owner-read", "bucket-owner-full-control", "project-private",
388 or "private" (see "gsutil help acls"). If we are not uploading to
389 Google Storage, this parameter is unused.
David Jamesfd0b0852011-02-23 11:15:36 -0800390 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800391 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
392 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700393 build_path: The path to the directory containing the chroot.
394 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700395 skip_upload: Don't actually upload the tarballs.
396 binhost_conf_dir: Directory where to store binhost.conf files.
Mike Frysinger86509232014-05-24 13:18:37 -0400397 dryrun: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800398 target: BuildTarget managed by this builder.
399 slave_targets: List of BuildTargets managed by slave builders.
Mike Frysinger8092a632014-05-24 13:25:46 -0400400 version: A unique string, intended to be included in the upload path,
401 which identifies the version number of the uploaded prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800402 """
403 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800404 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800405 self._binhost_base_url = binhost_base_url
406 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700407 self._build_path = build_path
408 self._packages = set(packages)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500409 self._found_packages = set()
David James8ece7ee2011-06-29 16:02:30 -0700410 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700411 self._binhost_conf_dir = binhost_conf_dir
Mike Frysinger86509232014-05-24 13:18:37 -0400412 self._dryrun = dryrun
David James4058b0d2011-12-08 21:24:50 -0800413 self._target = target
414 self._slave_targets = slave_targets
Mike Frysinger8092a632014-05-24 13:25:46 -0400415 self._version = version
Mike Frysinger540883b2014-05-24 13:46:16 -0400416 self._gs_context = gs.GSContext(retries=_RETRIES, sleep=_SLEEP_TIME,
417 dry_run=self._dryrun)
418
419 def _Upload(self, local_file, remote_file):
420 """Wrapper around _GsUpload"""
421 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700422
423 def _ShouldFilterPackage(self, pkg):
424 if not self._packages:
425 return False
Alex Klein18a60af2020-06-11 12:08:47 -0600426 cpv = package_info.SplitCPV(pkg['CPV'])
Alex Klein9f93b482018-10-01 09:26:51 -0600427 self._found_packages.add(cpv.cp)
428 return cpv.package not in self._packages and cpv.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:
Lann Martinffb95162018-08-28 12:02:54 -0600444 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
David James015af872012-06-19 15:24:36 -0700453 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
454 remote_file = '%s/Packages' % remote_location.rstrip('/')
455 upload_files[tmp_packages_file.name] = remote_file
456
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400457 # Build list of files to upload. Manually include the dev-only files but
458 # skip them if not present.
459 dev_only = os.path.join(package_path, 'dev-only-extras.tar.xz')
460 if os.path.exists(dev_only):
461 upload_files[dev_only] = '%s/%s' % (
462 remote_location.rstrip('/'), os.path.basename(dev_only))
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()],
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800470 index.name, self._target, self._version, remote_location)
Mike Frysinger212e4292014-05-24 15:15:44 -0400471 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):])
Prathmesh Prabhu17f07422015-07-17 11:40:40 -0700476 logging.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400477
Mike Frysinger8092a632014-05-24 13:25:46 -0400478 def _UploadSdkTarball(self, board_path, url_suffix, prepackaged,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700479 toolchains_overlay_tarballs,
480 toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500481 toolchain_tarballs, toolchain_upload_path):
482 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700483
484 Args:
485 board_path: The path to the board dir.
486 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan62a57792012-08-31 15:09:08 +0200487 prepackaged: If given, a tarball that has been packaged outside of this
488 script and should be used.
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700489 toolchains_overlay_tarballs: List of toolchains overlay tarball
490 specifications to upload. Items take the form
491 "toolchains_spec:/path/to/tarball".
492 toolchains_overlay_upload_path: Path template under the bucket to place
493 toolchains overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500494 toolchain_tarballs: List of toolchain tarballs to upload.
495 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David James8fa34ea2011-04-15 13:00:20 -0700496 """
497 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
498 assert remote_location.startswith('gs://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200499 boardname = os.path.basename(board_path.rstrip('/'))
500 # We do not upload non SDK board tarballs,
501 assert boardname == constants.CHROOT_BUILDER_BOARD
502 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200503
Mike Frysinger8092a632014-05-24 13:25:46 -0400504 version_str = self._version[len('chroot-'):]
Mike Frysinger8e727a32013-01-16 16:57:53 -0500505 remote_tarfile = toolchain.GetSdkURL(
506 for_gsutil=True, suburl='cros-sdk-%s.tar.xz' % (version_str,))
Zdenek Behan86c15e92012-10-13 00:55:47 +0200507 # For SDK, also upload the manifest which is guaranteed to exist
508 # by the builderstage.
Mike Frysinger540883b2014-05-24 13:46:16 -0400509 self._Upload(prepackaged + '.Manifest', remote_tarfile + '.Manifest')
510 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200511
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700512 # Upload SDK toolchains overlays and toolchain tarballs, if given.
Gilad Arnoldad333182015-05-27 15:50:41 -0700513 for tarball_list, upload_path, qualifier_name in (
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700514 (toolchains_overlay_tarballs, toolchains_overlay_upload_path,
515 'toolchains'),
Gilad Arnoldad333182015-05-27 15:50:41 -0700516 (toolchain_tarballs, toolchain_upload_path, 'target')):
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700517 for tarball_spec in tarball_list:
518 qualifier_val, local_path = tarball_spec.split(':')
Gilad Arnoldad333182015-05-27 15:50:41 -0700519 suburl = upload_path % {qualifier_name: qualifier_val}
520 remote_path = toolchain.GetSdkURL(for_gsutil=True, suburl=suburl)
521 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500522
Zdenek Behan86c15e92012-10-13 00:55:47 +0200523 # Finally, also update the pointer to the latest SDK on which polling
524 # scripts rely.
David James4bc13702013-03-26 08:08:04 -0700525 with osutils.TempDir() as tmpdir:
Zdenek Behan86c15e92012-10-13 00:55:47 +0200526 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
Mike Frysinger8e727a32013-01-16 16:57:53 -0500527 remote_pointerfile = toolchain.GetSdkURL(for_gsutil=True,
528 suburl='cros-sdk-latest.conf')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200529 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
Mike Frysinger540883b2014-05-24 13:46:16 -0400530 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200531
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700532 def _GetTargets(self):
533 """Retuns the list of targets to use."""
534 targets = self._slave_targets[:]
535 if self._target:
536 targets.append(self._target)
537
538 return targets
539
Mike Frysinger8092a632014-05-24 13:25:46 -0400540 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800541 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800542
David Jamesc0f158a2011-02-22 16:07:29 -0800543 This function will sync both the standard host packages, plus the host
544 packages associated with all targets that have been "setup" with the
545 current host's chroot. For instance, if this host has been used to build
546 x86-generic, it will sync the host packages associated with
547 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
548 it will also sync the host packages associated with
549 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800550
David Jamesc0f158a2011-02-22 16:07:29 -0800551 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800552 key: The variable key to update in the git file.
553 git_sync: If set, update make.conf of target to reference the latest
554 prebuilt packages generated here.
555 sync_binhost_conf: If set, update binhost config file in
556 chromiumos-overlay for the host.
557 """
Mike Nicholsa1414162021-04-22 20:07:22 +0000558 # Slave boards are listed before the master board so that the master board
David Jamese2488642011-11-14 16:15:20 -0800559 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
560 # over preflight host prebuilts from other builders.)
561 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700562 for target in self._GetTargets():
Mike Frysinger8092a632014-05-24 13:25:46 -0400563 url_suffix = _REL_HOST_PATH % {'version': self._version,
David James4058b0d2011-12-08 21:24:50 -0800564 'host_arch': _HOST_ARCH,
565 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800566 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800567
Mike Frysinger540883b2014-05-24 13:46:16 -0400568 if self._target == target and not self._skip_upload:
David Jamese2488642011-11-14 16:15:20 -0800569 # Upload prebuilts.
570 package_path = os.path.join(self._build_path, _HOST_PACKAGES_PATH)
571 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700572
David Jamese2488642011-11-14 16:15:20 -0800573 # Record URL where prebuilts were uploaded.
574 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
575 packages_url_suffix.rstrip('/')))
576
David James20b2b6f2011-11-18 15:11:58 -0800577 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700578 if git_sync:
David Jamesb26b9312014-12-15 11:26:46 -0800579 git_file = os.path.join(self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH])
Mike Frysinger86509232014-05-24 13:18:37 -0400580 RevGitFile(git_file, {key: binhost}, dryrun=self._dryrun)
David James8ece7ee2011-06-29 16:02:30 -0700581 if sync_binhost_conf:
David Jamesb26b9312014-12-15 11:26:46 -0800582 binhost_conf = os.path.join(
583 self._binhost_conf_dir, 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800584 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800585
Mike Frysinger8092a632014-05-24 13:25:46 -0400586 def SyncBoardPrebuilts(self, key, git_sync, sync_binhost_conf,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500587 upload_board_tarball, prepackaged_board,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700588 toolchains_overlay_tarballs,
589 toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500590 toolchain_tarballs, toolchain_upload_path):
David Jamesc0f158a2011-02-22 16:07:29 -0800591 """Synchronize board prebuilt files.
592
593 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800594 key: The variable key to update in the git file.
595 git_sync: If set, update make.conf of target to reference the latest
596 prebuilt packages generated here.
597 sync_binhost_conf: If set, update binhost config file in
598 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700599 upload_board_tarball: Include a tarball of the board in our upload.
Zdenek Behan62a57792012-08-31 15:09:08 +0200600 prepackaged_board: A tarball of the board built outside of this script.
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700601 toolchains_overlay_tarballs: List of toolchains overlay tarball
602 specifications to upload. Items take the form
603 "toolchains_spec:/path/to/tarball".
604 toolchains_overlay_upload_path: Path template under the bucket to place
605 toolchains overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500606 toolchain_tarballs: A list of toolchain tarballs to upload.
607 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David Jamesc0f158a2011-02-22 16:07:29 -0800608 """
David Jamesb26b9312014-12-15 11:26:46 -0800609 updated_binhosts = set()
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700610 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800611 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800612 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800613 package_path = os.path.join(board_path, 'packages')
Mike Frysinger8092a632014-05-24 13:25:46 -0400614 url_suffix = _REL_BOARD_PATH % {'target': target,
615 'version': self._version}
David Jamese2488642011-11-14 16:15:20 -0800616 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700617
Matt Tennante8179042013-10-01 15:47:32 -0700618 # Process the target board differently if it is the main --board.
Mike Frysinger540883b2014-05-24 13:46:16 -0400619 if self._target == target and not self._skip_upload:
Matt Tennante8179042013-10-01 15:47:32 -0700620 # This strips "chroot" prefix because that is sometimes added as the
621 # --prepend-version argument (e.g. by chromiumos-sdk bot).
622 # TODO(build): Clean it up to be less hard-coded.
Mike Frysinger8092a632014-05-24 13:25:46 -0400623 version_str = self._version[len('chroot-'):]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500624
David Jamese2488642011-11-14 16:15:20 -0800625 # Upload board tarballs in the background.
626 if upload_board_tarball:
Mike Frysinger9e979b92012-11-29 02:55:09 -0500627 if toolchain_upload_path:
628 toolchain_upload_path %= {'version': version_str}
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700629 if toolchains_overlay_upload_path:
630 toolchains_overlay_upload_path %= {'version': version_str}
Mike Frysinger9e979b92012-11-29 02:55:09 -0500631 tar_process = multiprocessing.Process(
632 target=self._UploadSdkTarball,
Mike Frysinger8092a632014-05-24 13:25:46 -0400633 args=(board_path, url_suffix, prepackaged_board,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700634 toolchains_overlay_tarballs,
635 toolchains_overlay_upload_path, toolchain_tarballs,
636 toolchain_upload_path))
David Jamese2488642011-11-14 16:15:20 -0800637 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700638
David Jamese2488642011-11-14 16:15:20 -0800639 # Upload prebuilts.
640 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700641
David Jamese2488642011-11-14 16:15:20 -0800642 # Make sure we finished uploading the board tarballs.
643 if upload_board_tarball:
644 tar_process.join()
645 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800646
David Jamese2488642011-11-14 16:15:20 -0800647 # Record URL where prebuilts were uploaded.
648 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
649 packages_url_suffix.rstrip('/'))
650
651 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800652 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Mike Frysinger86509232014-05-24 13:18:37 -0400653 RevGitFile(git_file, {key: url_value}, dryrun=self._dryrun)
Matt Tennante8179042013-10-01 15:47:32 -0700654
David Jamese2488642011-11-14 16:15:20 -0800655 if sync_binhost_conf:
Matt Tennante8179042013-10-01 15:47:32 -0700656 # Update the binhost configuration file in git.
David Jamesb26b9312014-12-15 11:26:46 -0800657 binhost_conf = os.path.join(
658 self._binhost_conf_dir, 'target', '%s-%s.conf' % (target, key))
659 updated_binhosts.add(binhost_conf)
David Jamese2488642011-11-14 16:15:20 -0800660 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800661
David Jamesb26b9312014-12-15 11:26:46 -0800662 if sync_binhost_conf:
663 # Clear all old binhosts. The files must be left empty in case anybody
664 # is referring to them.
665 all_binhosts = set(glob.glob(os.path.join(
666 self._binhost_conf_dir, 'target', '*-%s.conf' % key)))
667 for binhost_conf in all_binhosts - updated_binhosts:
668 UpdateBinhostConfFile(binhost_conf, key, '')
669
David James05bcb2b2011-02-09 09:25:47 -0800670
Mike Nicholsa1414162021-04-22 20:07:22 +0000671class _AddSlaveBoardAction(argparse.Action):
672 """Callback that adds a slave board to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400673 def __call__(self, parser, namespace, values, option_string=None):
674 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800675
676
Mike Nicholsa1414162021-04-22 20:07:22 +0000677class _AddSlaveProfileAction(argparse.Action):
678 """Callback that adds a slave profile to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400679 def __call__(self, parser, namespace, values, option_string=None):
680 if not namespace.slave_targets:
681 parser.error('Must specify --slave-board before --slave-profile')
682 if namespace.slave_targets[-1].profile is not None:
683 parser.error('Cannot specify --slave-profile twice for same board')
684 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800685
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 Frysingerb87bb782015-06-04 02:46:50 -0400697 parser = commandline.ArgumentParser()
698 parser.add_argument('-H', '--binhost-base-url', default=_BINHOST_BASE_URL,
699 help='Base URL to use for binhost in make.conf updates')
700 parser.add_argument('--previous-binhost-url', action='append', default=[],
701 help='Previous binhost URL')
702 parser.add_argument('-b', '--board',
703 help='Board type that was built on this machine')
704 parser.add_argument('-B', '--prepackaged-tarball', type='path',
705 help='Board tarball prebuilt outside of this script.')
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700706 parser.add_argument('--toolchains-overlay-tarball',
707 dest='toolchains_overlay_tarballs',
Mike Frysingerb87bb782015-06-04 02:46:50 -0400708 action='append', default=[],
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700709 help='Toolchains overlay tarball specification to '
710 'upload. Takes the form '
711 '"toolchains_spec:/path/to/tarball".')
712 parser.add_argument('--toolchains-overlay-upload-path', default='',
713 help='Path template for uploading toolchains overlays.')
Mike Frysingerb87bb782015-06-04 02:46:50 -0400714 parser.add_argument('--toolchain-tarball', dest='toolchain_tarballs',
715 action='append', default=[],
716 help='Redistributable toolchain tarball.')
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700717 parser.add_argument('--toolchain-upload-path', default='',
Mike Frysingerb87bb782015-06-04 02:46:50 -0400718 help='Path to place toolchain tarballs in the sdk tree.')
719 parser.add_argument('--profile',
720 help='Profile that was built on this machine')
Mike Nicholsa1414162021-04-22 20:07:22 +0000721 parser.add_argument('--slave-board', default=[], action=_AddSlaveBoardAction,
Mike Frysingerb87bb782015-06-04 02:46:50 -0400722 dest='slave_targets',
723 help='Board type that was built on a slave machine. To '
724 'add a profile to this board, use --slave-profile.')
Mike Nicholsa1414162021-04-22 20:07:22 +0000725 parser.add_argument('--slave-profile', action=_AddSlaveProfileAction,
Mike Frysingerb87bb782015-06-04 02:46:50 -0400726 help='Board profile that was built on a slave machine. '
727 'Applies to previous slave board.')
728 parser.add_argument('-p', '--build-path', required=True,
729 help='Path to the directory containing the chroot')
730 parser.add_argument('--packages', action='append', default=[],
731 help='Only include the specified packages. '
732 '(Default is to include all packages.)')
733 parser.add_argument('-s', '--sync-host', default=False, action='store_true',
734 help='Sync host prebuilts')
735 parser.add_argument('-g', '--git-sync', default=False, action='store_true',
736 help='Enable git version sync (This commits to a repo.) '
737 'This is used by full builders to commit directly '
738 'to board overlays.')
739 parser.add_argument('-u', '--upload',
740 help='Upload location')
741 parser.add_argument('-V', '--prepend-version',
742 help='Add an identifier to the front of the version')
743 parser.add_argument('-f', '--filters', action='store_true', default=False,
744 help='Turn on filtering of private ebuild packages')
745 parser.add_argument('-k', '--key', default='PORTAGE_BINHOST',
746 help='Key to update in make.conf / binhost.conf')
747 parser.add_argument('--set-version',
748 help='Specify the version string')
749 parser.add_argument('--sync-binhost-conf', default=False, action='store_true',
750 help='Update binhost.conf in chromiumos-overlay or '
Mike Frysinger80de5012019-08-01 14:10:53 -0400751 "chromeos-overlay. Commit the changes, but don't "
Mike Frysingerb87bb782015-06-04 02:46:50 -0400752 'push them. This is used for preflight binhosts.')
753 parser.add_argument('--binhost-conf-dir',
754 help='Directory to commit binhost config with '
755 '--sync-binhost-conf.')
756 parser.add_argument('-P', '--private', action='store_true', default=False,
757 help='Mark gs:// uploads as private.')
758 parser.add_argument('--skip-upload', action='store_true', default=False,
759 help='Skip upload step.')
760 parser.add_argument('--upload-board-tarball', action='store_true',
761 default=False,
762 help='Upload board tarball to Google Storage.')
763 parser.add_argument('-n', '--dry-run', dest='dryrun',
764 action='store_true', default=False,
Mike Frysinger80de5012019-08-01 14:10:53 -0400765 help="Don't push or upload prebuilts.")
David James8c846492011-01-25 17:07:29 -0800766
Mike Frysingerb87bb782015-06-04 02:46:50 -0400767 options = parser.parse_args(argv)
David James8ece7ee2011-06-29 16:02:30 -0700768 if not options.upload and not options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400769 parser.error('you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700770 if not options.set_version and options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400771 parser.error('If you are using --skip-upload, you must specify a '
772 'version number using --set-version.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700773
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700774 target = None
775 if options.board:
776 target = BuildTarget(options.board, options.profile)
777
778 if target in options.slave_targets:
Mike Frysinger86509232014-05-24 13:18:37 -0400779 parser.error('--board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800780
David James4058b0d2011-12-08 21:24:50 -0800781 if len(set(options.slave_targets)) != len(options.slave_targets):
Mike Frysinger86509232014-05-24 13:18:37 -0400782 parser.error('--slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800783
David James4058b0d2011-12-08 21:24:50 -0800784 if options.slave_targets and options.git_sync:
Mike Frysinger86509232014-05-24 13:18:37 -0400785 parser.error('--slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800786
David James8ece7ee2011-06-29 16:02:30 -0700787 if (options.upload_board_tarball and options.skip_upload and
788 options.board == 'amd64-host'):
Mike Frysinger86509232014-05-24 13:18:37 -0400789 parser.error('--skip-upload is not compatible with '
790 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700791
David James8ece7ee2011-06-29 16:02:30 -0700792 if (options.upload_board_tarball and not options.skip_upload and
793 not options.upload.startswith('gs://')):
Mike Frysinger86509232014-05-24 13:18:37 -0400794 parser.error('--upload-board-tarball only works with gs:// URLs.\n'
795 '--upload must be a gs:// URL.')
David James8fa34ea2011-04-15 13:00:20 -0700796
Zdenek Behan86c15e92012-10-13 00:55:47 +0200797 if options.upload_board_tarball and options.prepackaged_tarball is None:
Mike Frysinger86509232014-05-24 13:18:37 -0400798 parser.error('--upload-board-tarball requires --prepackaged-tarball')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200799
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700800 if options.private:
801 if options.sync_host:
Mike Frysinger86509232014-05-24 13:18:37 -0400802 parser.error('--private and --sync-host/-s cannot be specified '
803 'together; we do not support private host prebuilts')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700804
David James8ece7ee2011-06-29 16:02:30 -0700805 if not options.upload or not options.upload.startswith('gs://'):
Mike Frysinger86509232014-05-24 13:18:37 -0400806 parser.error('--private is only valid for gs:// URLs; '
807 '--upload must be a gs:// URL.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700808
809 if options.binhost_base_url != _BINHOST_BASE_URL:
Mike Frysinger86509232014-05-24 13:18:37 -0400810 parser.error('when using --private the --binhost-base-url '
811 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700812
David Jamesc31168e2014-06-05 14:40:05 -0700813 if options.sync_binhost_conf and not options.binhost_conf_dir:
814 parser.error('--sync-binhost-conf requires --binhost-conf-dir')
815
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700816 if (options.toolchains_overlay_tarballs and
817 not options.toolchains_overlay_upload_path):
818 parser.error('--toolchains-overlay-tarball requires '
819 '--toolchains-overlay-upload-path')
Gilad Arnoldad333182015-05-27 15:50:41 -0700820
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700821 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800822
Mike Frysingercc838832014-05-24 13:10:30 -0400823
Mike Frysinger86509232014-05-24 13:18:37 -0400824def main(argv):
David Jamesdb401072011-06-10 12:17:16 -0700825 # Set umask to a sane value so that files created as root are readable.
Mike Frysinger60ec1012013-10-21 00:11:10 -0400826 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -0700827
Mike Frysinger86509232014-05-24 13:18:37 -0400828 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -0800829
David James05bcb2b2011-02-09 09:25:47 -0800830 # Calculate a list of Packages index files to compare against. Whenever we
831 # upload a package, we check to make sure it's not already stored in one of
832 # the packages files we uploaded. This list of packages files might contain
833 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800834 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800835
David James8ece7ee2011-06-29 16:02:30 -0700836 if options.set_version:
837 version = options.set_version
838 else:
839 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -0700840
David Jamesc0f158a2011-02-22 16:07:29 -0800841 if options.prepend_version:
842 version = '%s-%s' % (options.prepend_version, version)
843
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700844 acl = 'public-read'
845 binhost_base_url = options.binhost_base_url
846
David Jamesadd21432013-05-21 10:04:07 -0700847 if options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700848 binhost_base_url = options.upload
David Jamesadd21432013-05-21 10:04:07 -0700849 if target:
Prathmesh Prabhu421eef22014-10-16 17:13:19 -0700850 acl = portage_util.FindOverlayFile(_GOOGLESTORAGE_GSUTIL_FILE,
851 board=target.board_variant,
852 buildroot=options.build_path)
Ben Chan067de492015-01-06 17:19:13 -0800853 if acl is None:
854 cros_build_lib.Die('No Google Storage ACL file %s found in %s overlay.',
855 _GOOGLESTORAGE_GSUTIL_FILE, target.board_variant)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700856
David Jamesb26b9312014-12-15 11:26:46 -0800857 binhost_conf_dir = None
858 if options.binhost_conf_dir:
859 binhost_conf_dir = os.path.join(options.build_path,
860 options.binhost_conf_dir)
861
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700862 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700863 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700864 options.packages, options.skip_upload,
David Jamesb26b9312014-12-15 11:26:46 -0800865 binhost_conf_dir, options.dryrun,
Mike Frysinger8092a632014-05-24 13:25:46 -0400866 target, options.slave_targets, version)
David Jamesc0f158a2011-02-22 16:07:29 -0800867
David James8c846492011-01-25 17:07:29 -0800868 if options.sync_host:
Mike Frysinger8092a632014-05-24 13:25:46 -0400869 uploader.SyncHostPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700870 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800871
Chris Sosa62c8ff52012-06-04 15:03:12 -0700872 if options.board or options.slave_targets:
Mike Frysinger8092a632014-05-24 13:25:46 -0400873 uploader.SyncBoardPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700874 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200875 options.upload_board_tarball,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500876 options.prepackaged_tarball,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700877 options.toolchains_overlay_tarballs,
878 options.toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500879 options.toolchain_tarballs,
880 options.toolchain_upload_path)