blob: 2b0e36be624012109b105eabff84b3179f1f9f03 [file] [log] [blame]
Bob Haarmand1225ea2022-01-19 22:01:42 +00001# 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:
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000014upload_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:
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000017upload_prebuilts -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
David James8c846492011-01-25 17:07:29 -080018"""
19
Mike Frysingerb87bb782015-06-04 02:46:50 -040020import argparse
Chris Sosa1dc96132012-05-11 15:40:50 -070021import datetime
Mike Frysinger540883b2014-05-24 13:46:16 -040022import functools
David Jamesb26b9312014-12-15 11:26:46 -080023import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060024import logging
Chris Sosa1dc96132012-05-11 15:40:50 -070025import multiprocessing
Chris Sosa1dc96132012-05-11 15:40:50 -070026import os
Mike Frysinger212e4292014-05-24 15:15:44 -040027import tempfile
Chris Sosa1dc96132012-05-11 15:40:50 -070028
Chris McDonaldb55b7032021-06-17 16:41:32 -060029from chromite.cbuildbot import cbuildbot_alerts
David James14e97772014-06-04 18:44:49 -070030from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080031from chromite.lib import binpkg
Mike Frysinger86509232014-05-24 13:18:37 -040032from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060033from chromite.lib import constants
Chris Sosa1dc96132012-05-11 15:40:50 -070034from chromite.lib import cros_build_lib
Julio Hurtadofad17992021-05-20 21:24:24 +000035from chromite.lib import gerrit
Mike Frysinger807d8282022-04-28 22:45:17 -040036from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070037from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070038from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080039from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070040from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050041from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060042from chromite.lib.parser import package_info
Chris Sosa1dc96132012-05-11 15:40:50 -070043
Mike Frysinger72b7cf92020-04-19 06:00:51 -040044
David James015af872012-06-19 15:24:36 -070045# How many times to retry uploads.
46_RETRIES = 10
47
48# Multiplier for how long to sleep (in seconds) between retries; will delay
49# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
50_SLEEP_TIME = 60
51
David James5ab67e32014-10-24 08:19:59 -070052# The length of time (in seconds) that Portage should wait before refetching
53# binpkgs from the same binhost. We don't ever modify binhosts, so this should
54# be something big.
55_BINPKG_TTL = 60 * 60 * 24 * 365
56
Bob Haarmand1225ea2022-01-19 22:01:42 +000057_HOST_PACKAGES_PATH = 'var/lib/portage/pkgs'
David James05bcb2b2011-02-09 09:25:47 -080058_CATEGORIES_PATH = 'chroot/etc/portage/categories'
David James615e5b52011-06-03 11:10:15 -070059_PYM_PATH = 'chroot/usr/lib/portage/pym'
David James4058b0d2011-12-08 21:24:50 -080060_HOST_ARCH = 'amd64'
David James8c846492011-01-25 17:07:29 -080061_BOARD_PATH = 'chroot/build/%(board)s'
David James4058b0d2011-12-08 21:24:50 -080062_REL_BOARD_PATH = 'board/%(target)s/%(version)s'
63_REL_HOST_PATH = 'host/%(host_arch)s/%(target)s/%(version)s'
David James8c846492011-01-25 17:07:29 -080064# Private overlays to look at for builds to filter
65# relative to build path
66_PRIVATE_OVERLAY_DIR = 'src/private-overlays'
Gabe Black40169e62014-06-17 15:23:47 -070067_GOOGLESTORAGE_GSUTIL_FILE = 'googlestorage_acl.txt'
David James3753d942014-04-23 10:55:48 -070068_BINHOST_BASE_URL = 'gs://chromeos-prebuilt'
David James8c846492011-01-25 17:07:29 -080069_PREBUILT_BASE_DIR = 'src/third_party/chromiumos-overlay/chromeos/config/'
70# Created in the event of new host targets becoming available
71_PREBUILT_MAKE_CONF = {'amd64': os.path.join(_PREBUILT_BASE_DIR,
72 'make.conf.amd64-host')}
David James8c846492011-01-25 17:07:29 -080073
74
David James4058b0d2011-12-08 21:24:50 -080075class BuildTarget(object):
76 """A board/variant/profile tuple."""
77
78 def __init__(self, board_variant, profile=None):
79 self.board_variant = board_variant
80 self.board, _, self.variant = board_variant.partition('_')
81 self.profile = profile
82
83 def __str__(self):
84 if self.profile:
85 return '%s_%s' % (self.board_variant, self.profile)
86 else:
87 return self.board_variant
88
89 def __eq__(self, other):
90 return str(other) == str(self)
91
92 def __hash__(self):
93 return hash(str(self))
94
95
David James8c846492011-01-25 17:07:29 -080096def UpdateLocalFile(filename, value, key='PORTAGE_BINHOST'):
97 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -050098
David James8c846492011-01-25 17:07:29 -080099 File format:
100 key="value"
101 Note quotes are added automatically
102
103 Args:
104 filename: Name of file to modify.
105 value: Value to write with the key.
106 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800107
108 Returns:
109 True if changes were made to the file.
David James8c846492011-01-25 17:07:29 -0800110 """
Sergey Frolov09280f12022-06-06 17:47:32 -0600111
Jack Neusf30f6722022-06-09 16:23:49 +0000112 keyval_str = '%(key)s=%(value)s'
Sergey Frolov09280f12022-06-06 17:47:32 -0600113
114 # Add quotes around the value, if missing.
115 if not value or value[0] != '"' or value[-1] != '"':
116 value = f'"{value}"'
117
118 # new_lines is the content to be used to overwrite/create the config file
119 # at the end of this function.
120 made_changes = False
121 new_lines = []
122
123 # Read current lines.
124 try:
125 current_lines = osutils.ReadFile(filename).splitlines()
126 except FileNotFoundError:
127 current_lines = []
128 print(f'Creating new file {filename}')
129
130 # Scan current lines, copy all vars to new_lines, change the line with |key|.
131 found = False
132 for line in current_lines:
Jack Neusf30f6722022-06-09 16:23:49 +0000133 # Strip newlines from end of line. We already add newlines below.
134 line = line.rstrip('\n')
Jack Neusf30f6722022-06-09 16:23:49 +0000135 if len(line.split('=')) != 2:
136 # Skip any line that doesn't fit key=val.
Sergey Frolov09280f12022-06-06 17:47:32 -0600137 new_lines.append(line)
Jack Neusf30f6722022-06-09 16:23:49 +0000138 continue
Jack Neusf30f6722022-06-09 16:23:49 +0000139 file_var, file_val = line.split('=')
140 if file_var == key:
141 found = True
Sergey Frolov09280f12022-06-06 17:47:32 -0600142 print(f'Updating {file_var}={file_val} to {key}={value}')
Jack Neusf30f6722022-06-09 16:23:49 +0000143 made_changes |= (file_val != value)
Sergey Frolov09280f12022-06-06 17:47:32 -0600144 new_lines.append(keyval_str % {'key': key, 'value': value})
Jack Neusf30f6722022-06-09 16:23:49 +0000145 else:
Sergey Frolov09280f12022-06-06 17:47:32 -0600146 new_lines.append(keyval_str % {'key': file_var, 'value': file_val})
Jack Neusf30f6722022-06-09 16:23:49 +0000147 if not found:
Sergey Frolov09280f12022-06-06 17:47:32 -0600148 print(f'Adding new variable {key}={value}')
Jack Neusf30f6722022-06-09 16:23:49 +0000149 made_changes = True
Sergey Frolov09280f12022-06-06 17:47:32 -0600150 new_lines.append(keyval_str % {'key': key, 'value': value})
Jack Neusf30f6722022-06-09 16:23:49 +0000151
Sergey Frolov09280f12022-06-06 17:47:32 -0600152 # Write out new file.
153 osutils.WriteFile(filename, '\n'.join(new_lines) + '\n')
David Jamesb26b9312014-12-15 11:26:46 -0800154 return made_changes
David James8c846492011-01-25 17:07:29 -0800155
156
Mike Nicholsa6818c52018-04-09 11:05:42 -0600157def RevGitFile(filename, data, dryrun=False):
David James8c846492011-01-25 17:07:29 -0800158 """Update and push the git file.
159
Mike Frysinger5b34d732013-01-17 15:11:58 -0500160 Args:
161 filename: file to modify that is in a git repo already
162 data: A dict of key/values to update in |filename|
Matt Tennante8179042013-10-01 15:47:32 -0700163 dryrun: If True, do not actually commit the change.
David James8c846492011-01-25 17:07:29 -0800164 """
165 prebuilt_branch = 'prebuilt_branch'
David James1b6e67a2011-05-19 21:32:38 -0700166 cwd = os.path.abspath(os.path.dirname(filename))
Julio Hurtadofad17992021-05-20 21:24:24 +0000167 remote_name = git.RunGit(cwd, ['remote']).stdout.strip()
168 gerrit_helper = gerrit.GetGerritHelper(remote_name)
169 remote_url = git.RunGit(
Julio Hurtadoef27d992021-06-29 15:51:13 +0000170 cwd, ['config', '--get', f'remote.{remote_name}.url']).stdout.strip()
Mike Frysinger5b34d732013-01-17 15:11:58 -0500171 description = '%s: updating %s' % (os.path.basename(filename),
172 ', '.join(data.keys()))
173 # UpdateLocalFile will print out the keys/values for us.
Mike Frysingerff441bf2014-05-24 13:47:21 -0400174 print('Revving git file %s' % filename)
Julio Hurtadofad17992021-05-20 21:24:24 +0000175 git.CreatePushBranch(prebuilt_branch, cwd)
176 for key, value in data.items():
177 UpdateLocalFile(filename, value, key)
178 git.RunGit(cwd, ['add', filename])
179 git.RunGit(cwd, ['commit', '-m', description])
Julio Hurtado2d4817d2021-04-29 16:03:58 +0000180
Julio Hurtadofad17992021-05-20 21:24:24 +0000181 tracking_info = git.GetTrackingBranch(
182 cwd, prebuilt_branch, for_push=True, for_checkout=False)
183 gpatch = gerrit_helper.CreateGerritPatch(
Julio Hurtado8f716a32021-07-14 21:32:13 +0000184 cwd, remote_url, ref=tracking_info.ref, notify='NONE')
Julio Hurtadoef27d992021-06-29 15:51:13 +0000185 gerrit_helper.SetReview(
186 gpatch, labels={'Bot-Commit': 1}, dryrun=dryrun, notify='NONE')
187 gerrit_helper.SubmitChange(gpatch, dryrun=dryrun, notify='NONE')
David James8c846492011-01-25 17:07:29 -0800188
189
190def GetVersion():
191 """Get the version to put in LATEST and update the git version with."""
Mike Frysinger0b92cfe2012-12-13 02:13:45 -0500192 return datetime.datetime.now().strftime('%Y.%m.%d.%H%M%S')
David James8c846492011-01-25 17:07:29 -0800193
194
Mike Frysinger540883b2014-05-24 13:46:16 -0400195def _GsUpload(gs_context, acl, local_file, remote_file):
David James8c846492011-01-25 17:07:29 -0800196 """Upload to GS bucket.
197
198 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400199 gs_context: A lib.gs.GSContext instance.
200 acl: The ACL to use for uploading the file.
Matt Tennante8179042013-10-01 15:47:32 -0700201 local_file: The local file to be uploaded.
202 remote_file: The remote location to upload to.
David James8c846492011-01-25 17:07:29 -0800203 """
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700204 CANNED_ACLS = ['public-read', 'private', 'bucket-owner-read',
205 'authenticated-read', 'bucket-owner-full-control',
206 'public-read-write']
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700207 if acl in CANNED_ACLS:
David James9374aac2013-10-08 16:00:17 -0700208 gs_context.Copy(local_file, remote_file, acl=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700209 else:
210 # For private uploads we assume that the overlay board is set up properly
David James015af872012-06-19 15:24:36 -0700211 # and a googlestore_acl.xml is present. Otherwise, this script errors.
David James7b7d7c32015-04-02 13:48:17 -0700212 # We set version=0 here to ensure that the ACL is set only once (see
213 # http://b/15883752#comment54).
214 try:
215 gs_context.Copy(local_file, remote_file, version=0)
216 except gs.GSContextPreconditionFailed as ex:
217 # If we received a GSContextPreconditionFailed error, we know that the
218 # file exists now, but we don't know whether our specific update
219 # succeeded. See http://b/15883752#comment62
220 logging.warning(
221 'Assuming upload succeeded despite PreconditionFailed errors: %s', ex)
222
Gabe Black40169e62014-06-17 15:23:47 -0700223 if acl.endswith('.xml'):
224 # Apply the passed in ACL xml file to the uploaded object.
225 gs_context.SetACL(remote_file, acl=acl)
226 else:
Mike Frysinger5186f262017-09-13 10:26:19 -0400227 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700228
Mike Frysingercc838832014-05-24 13:10:30 -0400229
Mike Frysinger540883b2014-05-24 13:46:16 -0400230def RemoteUpload(gs_context, acl, files, pool=10):
David James8c846492011-01-25 17:07:29 -0800231 """Upload to google storage.
232
233 Create a pool of process and call _GsUpload with the proper arguments.
234
235 Args:
Mike Frysinger540883b2014-05-24 13:46:16 -0400236 gs_context: A lib.gs.GSContext instance.
David Jamesfd0b0852011-02-23 11:15:36 -0800237 acl: The canned acl used for uploading. acl can be one of: "public-read",
238 "public-read-write", "authenticated-read", "bucket-owner-read",
239 "bucket-owner-full-control", or "private".
David James8c846492011-01-25 17:07:29 -0800240 files: dictionary with keys to local files and values to remote path.
241 pool: integer of maximum proesses to have at the same time.
242
243 Returns:
244 Return a set of tuple arguments of the failed uploads
245 """
Mike Frysinger540883b2014-05-24 13:46:16 -0400246 upload = functools.partial(_GsUpload, gs_context, acl)
Mike Frysinger0bdbc102019-06-13 15:27:29 -0400247 tasks = [[key, value] for key, value in files.items()]
Mike Frysinger540883b2014-05-24 13:46:16 -0400248 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800249
250
251def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
252 """Build a dictionary of local remote file key pairs to upload.
253
254 Args:
255 base_local_path: The base path to the files on the local hard drive.
Matt Tennante8179042013-10-01 15:47:32 -0700256 base_remote_path: The base path to the remote paths.
David James8c846492011-01-25 17:07:29 -0800257 pkgs: The packages to upload.
258
259 Returns:
260 Returns a dictionary of local_path/remote_path pairs
261 """
262 upload_files = {}
263 for pkg in pkgs:
264 suffix = pkg['CPV'] + '.tbz2'
265 local_path = os.path.join(base_local_path, suffix)
David James7b7d7c32015-04-02 13:48:17 -0700266 assert os.path.exists(local_path), '%s does not exist' % local_path
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800267 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800268
Bertrand SIMONNET811bcde2014-11-20 15:21:25 -0800269 if pkg.get('DEBUG_SYMBOLS') == 'yes':
270 debugsuffix = pkg['CPV'] + '.debug.tbz2'
271 local_path = os.path.join(base_local_path, debugsuffix)
272 assert os.path.exists(local_path)
273 upload_files[local_path] = os.path.join(base_remote_path, debugsuffix)
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800274
David James8c846492011-01-25 17:07:29 -0800275 return upload_files
276
Mike Frysingercc838832014-05-24 13:10:30 -0400277
Peter Mayo950e41a2014-02-06 21:07:33 +0000278def GetBoardOverlay(build_path, target):
David Jamese5867812012-10-19 12:02:20 -0700279 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500280
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400281 Args:
282 build_path: The path to the root of the build directory
283 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500284
Mike Frysinger6f3c48e2015-05-06 02:38:51 -0400285 Returns:
286 The last overlay configured for the given board as a string.
David James8c846492011-01-25 17:07:29 -0800287 """
David Jamese5867812012-10-19 12:02:20 -0700288 board = target.board_variant
Alex Deymo075c2292014-09-04 18:31:50 -0700289 overlays = portage_util.FindOverlays(constants.BOTH_OVERLAYS, board,
290 buildroot=build_path)
David Jamese5867812012-10-19 12:02:20 -0700291 # We only care about the last entry.
292 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800293
294
295def DeterminePrebuiltConfFile(build_path, target):
296 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
297
Mike Frysinger1a736a82013-12-12 01:50:59 -0500298 Args:
299 build_path: The path to the root of the build directory
300 target: String representation of the board. This includes host and board
301 targets
David James8c846492011-01-25 17:07:29 -0800302
Mike Frysinger1a736a82013-12-12 01:50:59 -0500303 Returns:
304 A string path to a prebuilt.conf file to be updated.
David James8c846492011-01-25 17:07:29 -0800305 """
David James4058b0d2011-12-08 21:24:50 -0800306 if _HOST_ARCH == target:
David James8c846492011-01-25 17:07:29 -0800307 # We are host.
308 # Without more examples of hosts this is a kludge for now.
309 # TODO(Scottz): as new host targets come online expand this to
310 # work more like boards.
Chris Sosa471532a2011-02-01 15:10:06 -0800311 make_path = _PREBUILT_MAKE_CONF[target]
David James8c846492011-01-25 17:07:29 -0800312 else:
313 # We are a board
Peter Mayo950e41a2014-02-06 21:07:33 +0000314 board = GetBoardOverlay(build_path, target)
David James8c846492011-01-25 17:07:29 -0800315 make_path = os.path.join(board, 'prebuilt.conf')
316
317 return make_path
318
319
320def UpdateBinhostConfFile(path, key, value):
321 """Update binhost config file file with key=value.
322
323 Args:
324 path: Filename to update.
325 key: Key to update.
326 value: New value for key.
327 """
David Jamesb26b9312014-12-15 11:26:46 -0800328 cwd, filename = os.path.split(os.path.abspath(path))
Brian Harringaf019fb2012-05-10 15:06:13 -0700329 osutils.SafeMakedirs(cwd)
David Jamesf6e8fb72013-05-10 08:58:43 -0700330 if not git.GetCurrentBranch(cwd):
331 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
Brian Harring22edb442012-05-11 23:55:18 -0700332 osutils.WriteFile(path, '', mode='a')
David Jamesb26b9312014-12-15 11:26:46 -0800333 if UpdateLocalFile(path, value, key):
334 desc = '%s: %s %s' % (filename, 'updating' if value else 'clearing', key)
335 git.AddPath(path)
336 git.Commit(cwd, desc)
David James8c846492011-01-25 17:07:29 -0800337
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800338def GenerateHtmlIndex(files, index, board, version, remote_location):
Mike Frysinger212e4292014-05-24 15:15:44 -0400339 """Given the list of |files|, generate an index.html at |index|.
340
341 Args:
342 files: The list of files to link to.
343 index: The path to the html index.
344 board: Name of the board this index is for.
345 version: Build version this index is for.
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800346 remote_location: Remote gs location prebuilts are uploaded to.
Mike Frysinger212e4292014-05-24 15:15:44 -0400347 """
Don Garrett13cbbff2016-08-09 14:18:38 -0700348 title = 'Package Prebuilt Index: %s / %s' % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400349
350 files = files + [
351 '.|Google Storage Index',
352 '..|',
353 ]
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800354 commands.GenerateHtmlIndex(index, files, title=title,
355 url_base=gs.GsUrlToHttp(remote_location))
Mike Frysinger212e4292014-05-24 15:15:44 -0400356
357
David Jamesce093af2011-02-23 15:21:58 -0800358def _GrabAllRemotePackageIndexes(binhost_urls):
David James05bcb2b2011-02-09 09:25:47 -0800359 """Grab all of the packages files associated with a list of binhost_urls.
360
David James05bcb2b2011-02-09 09:25:47 -0800361 Args:
362 binhost_urls: The URLs for the directories containing the Packages files we
363 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800364
365 Returns:
366 A list of PackageIndex objects.
367 """
368 pkg_indexes = []
369 for url in binhost_urls:
David James32faafe2012-06-08 14:25:03 -0700370 pkg_index = binpkg.GrabRemotePackageIndex(url)
David James05bcb2b2011-02-09 09:25:47 -0800371 if pkg_index:
372 pkg_indexes.append(pkg_index)
David James05bcb2b2011-02-09 09:25:47 -0800373 return pkg_indexes
374
375
David Jamesc0f158a2011-02-22 16:07:29 -0800376class PrebuiltUploader(object):
377 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800378
Mike Frysinger86509232014-05-24 13:18:37 -0400379 def __init__(self, upload_location, acl, binhost_base_url, pkg_indexes,
380 build_path, packages, skip_upload, binhost_conf_dir, dryrun,
Bob Haarmand1225ea2022-01-19 22:01:42 +0000381 target, slave_targets, version, chroot=None):
David Jamesc0f158a2011-02-22 16:07:29 -0800382 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800383
David Jamesc0f158a2011-02-22 16:07:29 -0800384 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800385
David Jamesc0f158a2011-02-22 16:07:29 -0800386 Args:
387 upload_location: The upload location.
David Jamesfd0b0852011-02-23 11:15:36 -0800388 acl: The canned acl used for uploading to Google Storage. acl can be one
389 of: "public-read", "public-read-write", "authenticated-read",
Matt Tennante8179042013-10-01 15:47:32 -0700390 "bucket-owner-read", "bucket-owner-full-control", "project-private",
391 or "private" (see "gsutil help acls"). If we are not uploading to
392 Google Storage, this parameter is unused.
David Jamesfd0b0852011-02-23 11:15:36 -0800393 binhost_base_url: The URL used for downloading the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800394 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
395 uploading duplicate files, we just link to the old files.
David James615e5b52011-06-03 11:10:15 -0700396 build_path: The path to the directory containing the chroot.
397 packages: Packages to upload.
David James32b0b2f2011-07-13 20:56:50 -0700398 skip_upload: Don't actually upload the tarballs.
399 binhost_conf_dir: Directory where to store binhost.conf files.
Mike Frysinger86509232014-05-24 13:18:37 -0400400 dryrun: Don't push or upload prebuilts.
David James4058b0d2011-12-08 21:24:50 -0800401 target: BuildTarget managed by this builder.
402 slave_targets: List of BuildTargets managed by slave builders.
Mike Frysinger8092a632014-05-24 13:25:46 -0400403 version: A unique string, intended to be included in the upload path,
404 which identifies the version number of the uploaded prebuilts.
Bob Haarmand1225ea2022-01-19 22:01:42 +0000405 chroot: Path to the chroot containing the prebuilts.
David Jamesc0f158a2011-02-22 16:07:29 -0800406 """
407 self._upload_location = upload_location
David Jamesfd0b0852011-02-23 11:15:36 -0800408 self._acl = acl
David Jamesc0f158a2011-02-22 16:07:29 -0800409 self._binhost_base_url = binhost_base_url
410 self._pkg_indexes = pkg_indexes
David James615e5b52011-06-03 11:10:15 -0700411 self._build_path = build_path
412 self._packages = set(packages)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500413 self._found_packages = set()
David James8ece7ee2011-06-29 16:02:30 -0700414 self._skip_upload = skip_upload
David James32b0b2f2011-07-13 20:56:50 -0700415 self._binhost_conf_dir = binhost_conf_dir
Mike Frysinger86509232014-05-24 13:18:37 -0400416 self._dryrun = dryrun
David James4058b0d2011-12-08 21:24:50 -0800417 self._target = target
418 self._slave_targets = slave_targets
Mike Frysinger8092a632014-05-24 13:25:46 -0400419 self._version = version
Bob Haarmand1225ea2022-01-19 22:01:42 +0000420 self._chroot = chroot or os.path.join(build_path,
421 constants.DEFAULT_CHROOT_DIR)
Mike Frysinger540883b2014-05-24 13:46:16 -0400422 self._gs_context = gs.GSContext(retries=_RETRIES, sleep=_SLEEP_TIME,
423 dry_run=self._dryrun)
424
425 def _Upload(self, local_file, remote_file):
426 """Wrapper around _GsUpload"""
427 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700428
429 def _ShouldFilterPackage(self, pkg):
430 if not self._packages:
431 return False
Alex Klein18a60af2020-06-11 12:08:47 -0600432 cpv = package_info.SplitCPV(pkg['CPV'])
Alex Klein9f93b482018-10-01 09:26:51 -0600433 self._found_packages.add(cpv.cp)
434 return cpv.package not in self._packages and cpv.cp not in self._packages
David James8c846492011-01-25 17:07:29 -0800435
David Jamesc0f158a2011-02-22 16:07:29 -0800436 def _UploadPrebuilt(self, package_path, url_suffix):
437 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800438
David Jamesc0f158a2011-02-22 16:07:29 -0800439 Args:
440 package_path: The path to the packages dir.
David Jamesce093af2011-02-23 15:21:58 -0800441 url_suffix: The remote subdirectory where we should upload the packages.
David Jamesc0f158a2011-02-22 16:07:29 -0800442 """
David Jamesc0f158a2011-02-22 16:07:29 -0800443 # Process Packages file, removing duplicates and filtered packages.
David James32faafe2012-06-08 14:25:03 -0700444 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
David Jamesc0f158a2011-02-22 16:07:29 -0800445 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
David James615e5b52011-06-03 11:10:15 -0700446 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
David Jamesc0f158a2011-02-22 16:07:29 -0800447 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
Mike Frysingerf5f809a2013-02-12 13:47:37 -0500448 unmatched_pkgs = self._packages - self._found_packages
449 if unmatched_pkgs:
Lann Martinffb95162018-08-28 12:02:54 -0600450 logging.warning('unable to match packages: %r', unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800451
David Jamesc0f158a2011-02-22 16:07:29 -0800452 # Write Packages file.
David James5ab67e32014-10-24 08:19:59 -0700453 pkg_index.header['TTL'] = _BINPKG_TTL
David Jamesc0f158a2011-02-22 16:07:29 -0800454 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800455
David Jamesc0f158a2011-02-22 16:07:29 -0800456 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
David James015af872012-06-19 15:24:36 -0700457 assert remote_location.startswith('gs://')
David James05bcb2b2011-02-09 09:25:47 -0800458
David James015af872012-06-19 15:24:36 -0700459 upload_files = GenerateUploadDict(package_path, remote_location, uploads)
460 remote_file = '%s/Packages' % remote_location.rstrip('/')
461 upload_files[tmp_packages_file.name] = remote_file
462
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400463 # Build list of files to upload. Manually include the dev-only files but
464 # skip them if not present.
465 dev_only = os.path.join(package_path, 'dev-only-extras.tar.xz')
466 if os.path.exists(dev_only):
467 upload_files[dev_only] = '%s/%s' % (
468 remote_location.rstrip('/'), os.path.basename(dev_only))
469
Mike Frysinger540883b2014-05-24 13:46:16 -0400470 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800471
Mike Frysinger212e4292014-05-24 15:15:44 -0400472 with tempfile.NamedTemporaryFile(
473 prefix='chromite.upload_prebuilts.index.') as index:
474 GenerateHtmlIndex(
475 [x[len(remote_location) + 1:] for x in upload_files.values()],
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800476 index.name, self._target, self._version, remote_location)
Mike Frysinger212e4292014-05-24 15:15:44 -0400477 self._Upload(index.name, '%s/index.html' % remote_location.rstrip('/'))
478
479 link_name = 'Prebuilts[%s]: %s' % (self._target, self._version)
480 url = '%s%s/index.html' % (gs.PUBLIC_BASE_HTTPS_URL,
481 remote_location[len(gs.BASE_GS_URL):])
Chris McDonaldb55b7032021-06-17 16:41:32 -0600482 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400483
Mike Frysinger8092a632014-05-24 13:25:46 -0400484 def _UploadSdkTarball(self, board_path, url_suffix, prepackaged,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700485 toolchains_overlay_tarballs,
486 toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500487 toolchain_tarballs, toolchain_upload_path):
488 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700489
490 Args:
491 board_path: The path to the board dir.
492 url_suffix: The remote subdirectory where we should upload the packages.
Zdenek Behan62a57792012-08-31 15:09:08 +0200493 prepackaged: If given, a tarball that has been packaged outside of this
494 script and should be used.
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700495 toolchains_overlay_tarballs: List of toolchains overlay tarball
496 specifications to upload. Items take the form
497 "toolchains_spec:/path/to/tarball".
498 toolchains_overlay_upload_path: Path template under the bucket to place
499 toolchains overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500500 toolchain_tarballs: List of toolchain tarballs to upload.
501 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David James8fa34ea2011-04-15 13:00:20 -0700502 """
503 remote_location = '%s/%s' % (self._upload_location.rstrip('/'), url_suffix)
504 assert remote_location.startswith('gs://')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200505 boardname = os.path.basename(board_path.rstrip('/'))
506 # We do not upload non SDK board tarballs,
507 assert boardname == constants.CHROOT_BUILDER_BOARD
508 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200509
Mike Frysinger8092a632014-05-24 13:25:46 -0400510 version_str = self._version[len('chroot-'):]
Mike Frysinger8e727a32013-01-16 16:57:53 -0500511 remote_tarfile = toolchain.GetSdkURL(
512 for_gsutil=True, suburl='cros-sdk-%s.tar.xz' % (version_str,))
Zdenek Behan86c15e92012-10-13 00:55:47 +0200513 # For SDK, also upload the manifest which is guaranteed to exist
514 # by the builderstage.
Mike Frysinger540883b2014-05-24 13:46:16 -0400515 self._Upload(prepackaged + '.Manifest', remote_tarfile + '.Manifest')
516 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200517
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700518 # Upload SDK toolchains overlays and toolchain tarballs, if given.
Gilad Arnoldad333182015-05-27 15:50:41 -0700519 for tarball_list, upload_path, qualifier_name in (
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700520 (toolchains_overlay_tarballs, toolchains_overlay_upload_path,
521 'toolchains'),
Gilad Arnoldad333182015-05-27 15:50:41 -0700522 (toolchain_tarballs, toolchain_upload_path, 'target')):
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700523 for tarball_spec in tarball_list:
524 qualifier_val, local_path = tarball_spec.split(':')
Gilad Arnoldad333182015-05-27 15:50:41 -0700525 suburl = upload_path % {qualifier_name: qualifier_val}
526 remote_path = toolchain.GetSdkURL(for_gsutil=True, suburl=suburl)
527 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500528
Zdenek Behan86c15e92012-10-13 00:55:47 +0200529 # Finally, also update the pointer to the latest SDK on which polling
530 # scripts rely.
David James4bc13702013-03-26 08:08:04 -0700531 with osutils.TempDir() as tmpdir:
Zdenek Behan86c15e92012-10-13 00:55:47 +0200532 pointerfile = os.path.join(tmpdir, 'cros-sdk-latest.conf')
Mike Frysinger8e727a32013-01-16 16:57:53 -0500533 remote_pointerfile = toolchain.GetSdkURL(for_gsutil=True,
534 suburl='cros-sdk-latest.conf')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200535 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
Mike Frysinger540883b2014-05-24 13:46:16 -0400536 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200537
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700538 def _GetTargets(self):
539 """Retuns the list of targets to use."""
540 targets = self._slave_targets[:]
541 if self._target:
542 targets.append(self._target)
543
544 return targets
545
Mike Frysinger8092a632014-05-24 13:25:46 -0400546 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
David Jamesc0f158a2011-02-22 16:07:29 -0800547 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800548
David Jamesc0f158a2011-02-22 16:07:29 -0800549 This function will sync both the standard host packages, plus the host
550 packages associated with all targets that have been "setup" with the
551 current host's chroot. For instance, if this host has been used to build
552 x86-generic, it will sync the host packages associated with
553 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
554 it will also sync the host packages associated with
555 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800556
David Jamesc0f158a2011-02-22 16:07:29 -0800557 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800558 key: The variable key to update in the git file.
559 git_sync: If set, update make.conf of target to reference the latest
560 prebuilt packages generated here.
561 sync_binhost_conf: If set, update binhost config file in
562 chromiumos-overlay for the host.
563 """
Mike Nicholsa1414162021-04-22 20:07:22 +0000564 # Slave boards are listed before the master board so that the master board
David Jamese2488642011-11-14 16:15:20 -0800565 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
566 # over preflight host prebuilts from other builders.)
567 binhost_urls = []
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700568 for target in self._GetTargets():
Mike Frysinger8092a632014-05-24 13:25:46 -0400569 url_suffix = _REL_HOST_PATH % {'version': self._version,
David James4058b0d2011-12-08 21:24:50 -0800570 'host_arch': _HOST_ARCH,
571 'target': target}
David Jamese2488642011-11-14 16:15:20 -0800572 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James05bcb2b2011-02-09 09:25:47 -0800573
Mike Frysinger540883b2014-05-24 13:46:16 -0400574 if self._target == target and not self._skip_upload:
David Jamese2488642011-11-14 16:15:20 -0800575 # Upload prebuilts.
Bob Haarmand1225ea2022-01-19 22:01:42 +0000576 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
David Jamese2488642011-11-14 16:15:20 -0800577 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700578
David Jamese2488642011-11-14 16:15:20 -0800579 # Record URL where prebuilts were uploaded.
580 binhost_urls.append('%s/%s/' % (self._binhost_base_url.rstrip('/'),
581 packages_url_suffix.rstrip('/')))
582
David James20b2b6f2011-11-18 15:11:58 -0800583 binhost = ' '.join(binhost_urls)
David James8ece7ee2011-06-29 16:02:30 -0700584 if git_sync:
David Jamesb26b9312014-12-15 11:26:46 -0800585 git_file = os.path.join(self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH])
Mike Frysinger86509232014-05-24 13:18:37 -0400586 RevGitFile(git_file, {key: binhost}, dryrun=self._dryrun)
David James8ece7ee2011-06-29 16:02:30 -0700587 if sync_binhost_conf:
David Jamesb26b9312014-12-15 11:26:46 -0800588 binhost_conf = os.path.join(
589 self._binhost_conf_dir, 'host', '%s-%s.conf' % (_HOST_ARCH, key))
David Jamese2488642011-11-14 16:15:20 -0800590 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800591
Mike Frysinger8092a632014-05-24 13:25:46 -0400592 def SyncBoardPrebuilts(self, key, git_sync, sync_binhost_conf,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500593 upload_board_tarball, prepackaged_board,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700594 toolchains_overlay_tarballs,
595 toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500596 toolchain_tarballs, toolchain_upload_path):
David Jamesc0f158a2011-02-22 16:07:29 -0800597 """Synchronize board prebuilt files.
598
599 Args:
David Jamesc0f158a2011-02-22 16:07:29 -0800600 key: The variable key to update in the git file.
601 git_sync: If set, update make.conf of target to reference the latest
602 prebuilt packages generated here.
603 sync_binhost_conf: If set, update binhost config file in
604 chromiumos-overlay for the current board.
David James8fa34ea2011-04-15 13:00:20 -0700605 upload_board_tarball: Include a tarball of the board in our upload.
Zdenek Behan62a57792012-08-31 15:09:08 +0200606 prepackaged_board: A tarball of the board built outside of this script.
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700607 toolchains_overlay_tarballs: List of toolchains overlay tarball
608 specifications to upload. Items take the form
609 "toolchains_spec:/path/to/tarball".
610 toolchains_overlay_upload_path: Path template under the bucket to place
611 toolchains overlay tarballs.
Mike Frysinger9e979b92012-11-29 02:55:09 -0500612 toolchain_tarballs: A list of toolchain tarballs to upload.
613 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
David Jamesc0f158a2011-02-22 16:07:29 -0800614 """
David Jamesb26b9312014-12-15 11:26:46 -0800615 updated_binhosts = set()
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700616 for target in self._GetTargets():
David Jamese2488642011-11-14 16:15:20 -0800617 board_path = os.path.join(self._build_path,
David James4058b0d2011-12-08 21:24:50 -0800618 _BOARD_PATH % {'board': target.board_variant})
David Jamese2488642011-11-14 16:15:20 -0800619 package_path = os.path.join(board_path, 'packages')
Mike Frysinger8092a632014-05-24 13:25:46 -0400620 url_suffix = _REL_BOARD_PATH % {'target': target,
621 'version': self._version}
David Jamese2488642011-11-14 16:15:20 -0800622 packages_url_suffix = '%s/packages' % url_suffix.rstrip('/')
David James8fa34ea2011-04-15 13:00:20 -0700623
Matt Tennante8179042013-10-01 15:47:32 -0700624 # Process the target board differently if it is the main --board.
Mike Frysinger540883b2014-05-24 13:46:16 -0400625 if self._target == target and not self._skip_upload:
Matt Tennante8179042013-10-01 15:47:32 -0700626 # This strips "chroot" prefix because that is sometimes added as the
627 # --prepend-version argument (e.g. by chromiumos-sdk bot).
628 # TODO(build): Clean it up to be less hard-coded.
Mike Frysinger8092a632014-05-24 13:25:46 -0400629 version_str = self._version[len('chroot-'):]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500630
David Jamese2488642011-11-14 16:15:20 -0800631 # Upload board tarballs in the background.
632 if upload_board_tarball:
Mike Frysinger9e979b92012-11-29 02:55:09 -0500633 if toolchain_upload_path:
634 toolchain_upload_path %= {'version': version_str}
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700635 if toolchains_overlay_upload_path:
636 toolchains_overlay_upload_path %= {'version': version_str}
Mike Frysinger9e979b92012-11-29 02:55:09 -0500637 tar_process = multiprocessing.Process(
638 target=self._UploadSdkTarball,
Mike Frysinger8092a632014-05-24 13:25:46 -0400639 args=(board_path, url_suffix, prepackaged_board,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700640 toolchains_overlay_tarballs,
641 toolchains_overlay_upload_path, toolchain_tarballs,
642 toolchain_upload_path))
David Jamese2488642011-11-14 16:15:20 -0800643 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700644
David Jamese2488642011-11-14 16:15:20 -0800645 # Upload prebuilts.
646 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700647
David Jamese2488642011-11-14 16:15:20 -0800648 # Make sure we finished uploading the board tarballs.
649 if upload_board_tarball:
650 tar_process.join()
651 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800652
David Jamese2488642011-11-14 16:15:20 -0800653 # Record URL where prebuilts were uploaded.
654 url_value = '%s/%s/' % (self._binhost_base_url.rstrip('/'),
655 packages_url_suffix.rstrip('/'))
656
657 if git_sync:
David James4058b0d2011-12-08 21:24:50 -0800658 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Mike Frysinger86509232014-05-24 13:18:37 -0400659 RevGitFile(git_file, {key: url_value}, dryrun=self._dryrun)
Matt Tennante8179042013-10-01 15:47:32 -0700660
David Jamese2488642011-11-14 16:15:20 -0800661 if sync_binhost_conf:
Matt Tennante8179042013-10-01 15:47:32 -0700662 # Update the binhost configuration file in git.
David Jamesb26b9312014-12-15 11:26:46 -0800663 binhost_conf = os.path.join(
664 self._binhost_conf_dir, 'target', '%s-%s.conf' % (target, key))
665 updated_binhosts.add(binhost_conf)
David Jamese2488642011-11-14 16:15:20 -0800666 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800667
David Jamesb26b9312014-12-15 11:26:46 -0800668 if sync_binhost_conf:
669 # Clear all old binhosts. The files must be left empty in case anybody
670 # is referring to them.
671 all_binhosts = set(glob.glob(os.path.join(
672 self._binhost_conf_dir, 'target', '*-%s.conf' % key)))
673 for binhost_conf in all_binhosts - updated_binhosts:
674 UpdateBinhostConfFile(binhost_conf, key, '')
675
David James05bcb2b2011-02-09 09:25:47 -0800676
Mike Nicholsa1414162021-04-22 20:07:22 +0000677class _AddSlaveBoardAction(argparse.Action):
678 """Callback that adds a slave board to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400679 def __call__(self, parser, namespace, values, option_string=None):
680 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800681
682
Mike Nicholsa1414162021-04-22 20:07:22 +0000683class _AddSlaveProfileAction(argparse.Action):
684 """Callback that adds a slave profile to the list of slave targets."""
Mike Frysingerb87bb782015-06-04 02:46:50 -0400685 def __call__(self, parser, namespace, values, option_string=None):
686 if not namespace.slave_targets:
687 parser.error('Must specify --slave-board before --slave-profile')
688 if namespace.slave_targets[-1].profile is not None:
689 parser.error('Cannot specify --slave-profile twice for same board')
690 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800691
692
Mike Frysinger86509232014-05-24 13:18:37 -0400693def ParseOptions(argv):
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700694 """Returns options given by the user and the target specified.
695
Mike Frysinger86509232014-05-24 13:18:37 -0400696 Args:
697 argv: The args to parse.
698
Mike Frysinger1a736a82013-12-12 01:50:59 -0500699 Returns:
700 A tuple containing a parsed options object and BuildTarget.
701 The target instance is None if no board is specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700702 """
Mike Frysingerb87bb782015-06-04 02:46:50 -0400703 parser = commandline.ArgumentParser()
704 parser.add_argument('-H', '--binhost-base-url', default=_BINHOST_BASE_URL,
705 help='Base URL to use for binhost in make.conf updates')
706 parser.add_argument('--previous-binhost-url', action='append', default=[],
707 help='Previous binhost URL')
708 parser.add_argument('-b', '--board',
709 help='Board type that was built on this machine')
710 parser.add_argument('-B', '--prepackaged-tarball', type='path',
711 help='Board tarball prebuilt outside of this script.')
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700712 parser.add_argument('--toolchains-overlay-tarball',
713 dest='toolchains_overlay_tarballs',
Mike Frysingerb87bb782015-06-04 02:46:50 -0400714 action='append', default=[],
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700715 help='Toolchains overlay tarball specification to '
716 'upload. Takes the form '
717 '"toolchains_spec:/path/to/tarball".')
718 parser.add_argument('--toolchains-overlay-upload-path', default='',
719 help='Path template for uploading toolchains overlays.')
Mike Frysingerb87bb782015-06-04 02:46:50 -0400720 parser.add_argument('--toolchain-tarball', dest='toolchain_tarballs',
721 action='append', default=[],
722 help='Redistributable toolchain tarball.')
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700723 parser.add_argument('--toolchain-upload-path', default='',
Mike Frysingerb87bb782015-06-04 02:46:50 -0400724 help='Path to place toolchain tarballs in the sdk tree.')
725 parser.add_argument('--profile',
726 help='Profile that was built on this machine')
Mike Nicholsa1414162021-04-22 20:07:22 +0000727 parser.add_argument('--slave-board', default=[], action=_AddSlaveBoardAction,
Mike Frysingerb87bb782015-06-04 02:46:50 -0400728 dest='slave_targets',
729 help='Board type that was built on a slave machine. To '
730 'add a profile to this board, use --slave-profile.')
Mike Nicholsa1414162021-04-22 20:07:22 +0000731 parser.add_argument('--slave-profile', action=_AddSlaveProfileAction,
Mike Frysingerb87bb782015-06-04 02:46:50 -0400732 help='Board profile that was built on a slave machine. '
733 'Applies to previous slave board.')
734 parser.add_argument('-p', '--build-path', required=True,
735 help='Path to the directory containing the chroot')
Bob Haarmand1225ea2022-01-19 22:01:42 +0000736 parser.add_argument('--chroot',
737 help='Path where the chroot is located. '
738 '(Default: {build_path}/chroot)')
Mike Frysingerb87bb782015-06-04 02:46:50 -0400739 parser.add_argument('--packages', action='append', default=[],
740 help='Only include the specified packages. '
741 '(Default is to include all packages.)')
742 parser.add_argument('-s', '--sync-host', default=False, action='store_true',
743 help='Sync host prebuilts')
744 parser.add_argument('-g', '--git-sync', default=False, action='store_true',
745 help='Enable git version sync (This commits to a repo.) '
746 'This is used by full builders to commit directly '
747 'to board overlays.')
748 parser.add_argument('-u', '--upload',
749 help='Upload location')
750 parser.add_argument('-V', '--prepend-version',
751 help='Add an identifier to the front of the version')
752 parser.add_argument('-f', '--filters', action='store_true', default=False,
753 help='Turn on filtering of private ebuild packages')
754 parser.add_argument('-k', '--key', default='PORTAGE_BINHOST',
755 help='Key to update in make.conf / binhost.conf')
756 parser.add_argument('--set-version',
757 help='Specify the version string')
758 parser.add_argument('--sync-binhost-conf', default=False, action='store_true',
759 help='Update binhost.conf in chromiumos-overlay or '
Mike Frysinger80de5012019-08-01 14:10:53 -0400760 "chromeos-overlay. Commit the changes, but don't "
Mike Frysingerb87bb782015-06-04 02:46:50 -0400761 'push them. This is used for preflight binhosts.')
762 parser.add_argument('--binhost-conf-dir',
763 help='Directory to commit binhost config with '
764 '--sync-binhost-conf.')
765 parser.add_argument('-P', '--private', action='store_true', default=False,
766 help='Mark gs:// uploads as private.')
767 parser.add_argument('--skip-upload', action='store_true', default=False,
768 help='Skip upload step.')
769 parser.add_argument('--upload-board-tarball', action='store_true',
770 default=False,
771 help='Upload board tarball to Google Storage.')
772 parser.add_argument('-n', '--dry-run', dest='dryrun',
773 action='store_true', default=False,
Mike Frysinger80de5012019-08-01 14:10:53 -0400774 help="Don't push or upload prebuilts.")
David James8c846492011-01-25 17:07:29 -0800775
Mike Frysingerb87bb782015-06-04 02:46:50 -0400776 options = parser.parse_args(argv)
David James8ece7ee2011-06-29 16:02:30 -0700777 if not options.upload and not options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400778 parser.error('you need to provide an upload location using -u')
David James8ece7ee2011-06-29 16:02:30 -0700779 if not options.set_version and options.skip_upload:
Mike Frysinger86509232014-05-24 13:18:37 -0400780 parser.error('If you are using --skip-upload, you must specify a '
781 'version number using --set-version.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700782
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700783 target = None
784 if options.board:
785 target = BuildTarget(options.board, options.profile)
786
787 if target in options.slave_targets:
Mike Frysinger86509232014-05-24 13:18:37 -0400788 parser.error('--board/--profile must not also be a slave target.')
David Jamese2488642011-11-14 16:15:20 -0800789
David James4058b0d2011-12-08 21:24:50 -0800790 if len(set(options.slave_targets)) != len(options.slave_targets):
Mike Frysinger86509232014-05-24 13:18:37 -0400791 parser.error('--slave-boards must not have duplicates.')
David Jamese2488642011-11-14 16:15:20 -0800792
David James4058b0d2011-12-08 21:24:50 -0800793 if options.slave_targets and options.git_sync:
Mike Frysinger86509232014-05-24 13:18:37 -0400794 parser.error('--slave-boards is not compatible with --git-sync')
David Jamese2488642011-11-14 16:15:20 -0800795
David James8ece7ee2011-06-29 16:02:30 -0700796 if (options.upload_board_tarball and options.skip_upload and
797 options.board == 'amd64-host'):
Mike Frysinger86509232014-05-24 13:18:37 -0400798 parser.error('--skip-upload is not compatible with '
799 '--upload-board-tarball and --board=amd64-host')
David James8fa34ea2011-04-15 13:00:20 -0700800
David James8ece7ee2011-06-29 16:02:30 -0700801 if (options.upload_board_tarball and not options.skip_upload and
802 not options.upload.startswith('gs://')):
Mike Frysinger86509232014-05-24 13:18:37 -0400803 parser.error('--upload-board-tarball only works with gs:// URLs.\n'
804 '--upload must be a gs:// URL.')
David James8fa34ea2011-04-15 13:00:20 -0700805
Zdenek Behan86c15e92012-10-13 00:55:47 +0200806 if options.upload_board_tarball and options.prepackaged_tarball is None:
Mike Frysinger86509232014-05-24 13:18:37 -0400807 parser.error('--upload-board-tarball requires --prepackaged-tarball')
Zdenek Behan86c15e92012-10-13 00:55:47 +0200808
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700809 if options.private:
810 if options.sync_host:
Mike Frysinger86509232014-05-24 13:18:37 -0400811 parser.error('--private and --sync-host/-s cannot be specified '
812 'together; we do not support private host prebuilts')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700813
David James8ece7ee2011-06-29 16:02:30 -0700814 if not options.upload or not options.upload.startswith('gs://'):
Mike Frysinger86509232014-05-24 13:18:37 -0400815 parser.error('--private is only valid for gs:// URLs; '
816 '--upload must be a gs:// URL.')
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700817
818 if options.binhost_base_url != _BINHOST_BASE_URL:
Mike Frysinger86509232014-05-24 13:18:37 -0400819 parser.error('when using --private the --binhost-base-url '
820 'is automatically derived.')
David James27fa7d12011-06-29 17:24:14 -0700821
David Jamesc31168e2014-06-05 14:40:05 -0700822 if options.sync_binhost_conf and not options.binhost_conf_dir:
823 parser.error('--sync-binhost-conf requires --binhost-conf-dir')
824
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700825 if (options.toolchains_overlay_tarballs and
826 not options.toolchains_overlay_upload_path):
827 parser.error('--toolchains-overlay-tarball requires '
828 '--toolchains-overlay-upload-path')
Gilad Arnoldad333182015-05-27 15:50:41 -0700829
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700830 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -0800831
Mike Frysingercc838832014-05-24 13:10:30 -0400832
Mike Frysinger86509232014-05-24 13:18:37 -0400833def main(argv):
Sloan Johnsona1c89eb2022-06-07 22:49:05 +0000834 # Set umask so that files created as root are readable.
Mike Frysinger60ec1012013-10-21 00:11:10 -0400835 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -0700836
Mike Frysinger86509232014-05-24 13:18:37 -0400837 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -0800838
David James05bcb2b2011-02-09 09:25:47 -0800839 # Calculate a list of Packages index files to compare against. Whenever we
840 # upload a package, we check to make sure it's not already stored in one of
841 # the packages files we uploaded. This list of packages files might contain
842 # both board and host packages.
David Jamesce093af2011-02-23 15:21:58 -0800843 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -0800844
David James8ece7ee2011-06-29 16:02:30 -0700845 if options.set_version:
846 version = options.set_version
847 else:
848 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -0700849
David Jamesc0f158a2011-02-22 16:07:29 -0800850 if options.prepend_version:
851 version = '%s-%s' % (options.prepend_version, version)
852
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700853 acl = 'public-read'
854 binhost_base_url = options.binhost_base_url
855
David Jamesadd21432013-05-21 10:04:07 -0700856 if options.private:
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700857 binhost_base_url = options.upload
David Jamesadd21432013-05-21 10:04:07 -0700858 if target:
Prathmesh Prabhu421eef22014-10-16 17:13:19 -0700859 acl = portage_util.FindOverlayFile(_GOOGLESTORAGE_GSUTIL_FILE,
860 board=target.board_variant,
861 buildroot=options.build_path)
Ben Chan067de492015-01-06 17:19:13 -0800862 if acl is None:
863 cros_build_lib.Die('No Google Storage ACL file %s found in %s overlay.',
864 _GOOGLESTORAGE_GSUTIL_FILE, target.board_variant)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700865
David Jamesb26b9312014-12-15 11:26:46 -0800866 binhost_conf_dir = None
867 if options.binhost_conf_dir:
868 binhost_conf_dir = os.path.join(options.build_path,
869 options.binhost_conf_dir)
870
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700871 uploader = PrebuiltUploader(options.upload, acl, binhost_base_url,
David James615e5b52011-06-03 11:10:15 -0700872 pkg_indexes, options.build_path,
David James27fa7d12011-06-29 17:24:14 -0700873 options.packages, options.skip_upload,
David Jamesb26b9312014-12-15 11:26:46 -0800874 binhost_conf_dir, options.dryrun,
Bob Haarmand1225ea2022-01-19 22:01:42 +0000875 target, options.slave_targets, version,
876 options.chroot)
David Jamesc0f158a2011-02-22 16:07:29 -0800877
David James8c846492011-01-25 17:07:29 -0800878 if options.sync_host:
Mike Frysinger8092a632014-05-24 13:25:46 -0400879 uploader.SyncHostPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700880 options.sync_binhost_conf)
David James8c846492011-01-25 17:07:29 -0800881
Chris Sosa62c8ff52012-06-04 15:03:12 -0700882 if options.board or options.slave_targets:
Mike Frysinger8092a632014-05-24 13:25:46 -0400883 uploader.SyncBoardPrebuilts(options.key, options.git_sync,
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700884 options.sync_binhost_conf,
Zdenek Behan62a57792012-08-31 15:09:08 +0200885 options.upload_board_tarball,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500886 options.prepackaged_tarball,
Gilad Arnold2b79e2d2015-06-02 11:26:07 -0700887 options.toolchains_overlay_tarballs,
888 options.toolchains_overlay_upload_path,
Mike Frysinger9e979b92012-11-29 02:55:09 -0500889 options.toolchain_tarballs,
890 options.toolchain_upload_path)