blob: 0425f1455560074d74641b4dfe2f476ae6e02474 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
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
Bob Haarmanc0082602022-09-20 16:12:43 -070027from pathlib import Path
Mike Frysinger212e4292014-05-24 15:15:44 -040028import tempfile
Greg Edelston325e4412023-05-04 16:22:10 -060029from typing import Optional, Tuple
Chris Sosa1dc96132012-05-11 15:40:50 -070030
Chris McDonaldb55b7032021-06-17 16:41:32 -060031from chromite.cbuildbot import cbuildbot_alerts
David James14e97772014-06-04 18:44:49 -070032from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080033from chromite.lib import binpkg
Brian Norris74d1af92023-06-14 11:28:12 -070034from chromite.lib import chroot_lib
Mike Frysinger86509232014-05-24 13:18:37 -040035from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060036from chromite.lib import constants
Chris Sosa1dc96132012-05-11 15:40:50 -070037from chromite.lib import cros_build_lib
Mike Frysinger807d8282022-04-28 22:45:17 -040038from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070039from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070040from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080041from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070042from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050043from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060044from chromite.lib.parser import package_info
Bob Haarmanc0082602022-09-20 16:12:43 -070045from chromite.utils import pformat
Chris Sosa1dc96132012-05-11 15:40:50 -070046
Mike Frysinger72b7cf92020-04-19 06:00:51 -040047
David James015af872012-06-19 15:24:36 -070048# How many times to retry uploads.
49_RETRIES = 10
50
51# Multiplier for how long to sleep (in seconds) between retries; will delay
52# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
53_SLEEP_TIME = 60
54
David James5ab67e32014-10-24 08:19:59 -070055# The length of time (in seconds) that Portage should wait before refetching
56# binpkgs from the same binhost. We don't ever modify binhosts, so this should
57# be something big.
58_BINPKG_TTL = 60 * 60 * 24 * 365
59
Alex Klein1699fab2022-09-08 08:46:06 -060060_HOST_PACKAGES_PATH = "var/lib/portage/pkgs"
Alex Klein1699fab2022-09-08 08:46:06 -060061_HOST_ARCH = "amd64"
Brian Norris74d1af92023-06-14 11:28:12 -070062_BOARD_PATH = "build/%(board)s"
Alex Klein1699fab2022-09-08 08:46:06 -060063_REL_BOARD_PATH = "board/%(target)s/%(version)s"
64_REL_HOST_PATH = "host/%(host_arch)s/%(target)s/%(version)s"
David James8c846492011-01-25 17:07:29 -080065# Private overlays to look at for builds to filter
66# relative to build path
Alex Klein1699fab2022-09-08 08:46:06 -060067_PRIVATE_OVERLAY_DIR = "src/private-overlays"
68_GOOGLESTORAGE_GSUTIL_FILE = "googlestorage_acl.txt"
69_BINHOST_BASE_URL = "gs://chromeos-prebuilt"
70_PREBUILT_BASE_DIR = "src/third_party/chromiumos-overlay/chromeos/config/"
David James8c846492011-01-25 17:07:29 -080071# Created in the event of new host targets becoming available
Alex Klein1699fab2022-09-08 08:46:06 -060072_PREBUILT_MAKE_CONF = {
73 "amd64": os.path.join(_PREBUILT_BASE_DIR, "make.conf.amd64-host")
74}
David James8c846492011-01-25 17:07:29 -080075
76
Alex Klein074f94f2023-06-22 10:32:06 -060077class BuildTarget:
Alex Klein1699fab2022-09-08 08:46:06 -060078 """A board/variant/profile tuple."""
David James4058b0d2011-12-08 21:24:50 -080079
Alex Klein1699fab2022-09-08 08:46:06 -060080 def __init__(self, board_variant, profile=None):
81 self.board_variant = board_variant
82 self.board, _, self.variant = board_variant.partition("_")
83 self.profile = profile
David James4058b0d2011-12-08 21:24:50 -080084
Alex Klein1699fab2022-09-08 08:46:06 -060085 def __str__(self):
86 if self.profile:
87 return "%s_%s" % (self.board_variant, self.profile)
88 else:
89 return self.board_variant
David James4058b0d2011-12-08 21:24:50 -080090
Alex Klein1699fab2022-09-08 08:46:06 -060091 def __eq__(self, other):
92 return str(other) == str(self)
David James4058b0d2011-12-08 21:24:50 -080093
Alex Klein1699fab2022-09-08 08:46:06 -060094 def __hash__(self):
95 return hash(str(self))
David James4058b0d2011-12-08 21:24:50 -080096
97
David James8c846492011-01-25 17:07:29 -080098def GetVersion():
Alex Klein1699fab2022-09-08 08:46:06 -060099 """Get the version to put in LATEST and update the git version with."""
100 return datetime.datetime.now().strftime("%Y.%m.%d.%H%M%S")
David James8c846492011-01-25 17:07:29 -0800101
102
Mike Frysinger540883b2014-05-24 13:46:16 -0400103def _GsUpload(gs_context, acl, local_file, remote_file):
Alex Klein1699fab2022-09-08 08:46:06 -0600104 """Upload to GS bucket.
David James8c846492011-01-25 17:07:29 -0800105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600107 gs_context: A lib.gs.GSContext instance.
108 acl: The ACL to use for uploading the file.
109 local_file: The local file to be uploaded.
110 remote_file: The remote location to upload to.
Alex Klein1699fab2022-09-08 08:46:06 -0600111 """
112 CANNED_ACLS = [
113 "public-read",
114 "private",
115 "bucket-owner-read",
116 "authenticated-read",
117 "bucket-owner-full-control",
118 "public-read-write",
119 ]
120 if acl in CANNED_ACLS:
121 gs_context.Copy(local_file, remote_file, acl=acl)
Gabe Black40169e62014-06-17 15:23:47 -0700122 else:
Alex Kleind7197402023-04-05 13:05:29 -0600123 # For private uploads we assume that the overlay board is set up
124 # properly and a googlestore_acl.xml is present. Otherwise, this script
125 # errors. We set version=0 here to ensure that the ACL is set only once
126 # (see http://b/15883752#comment54).
Alex Klein1699fab2022-09-08 08:46:06 -0600127 try:
128 gs_context.Copy(local_file, remote_file, version=0)
129 except gs.GSContextPreconditionFailed as ex:
Alex Kleind7197402023-04-05 13:05:29 -0600130 # If we received a GSContextPreconditionFailed error, we know that
131 # the file exists now, but we don't know whether our specific update
Alex Klein1699fab2022-09-08 08:46:06 -0600132 # succeeded. See http://b/15883752#comment62
133 logging.warning(
Alex Kleind7197402023-04-05 13:05:29 -0600134 "Assuming upload succeeded despite PreconditionFailed errors: "
135 "%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600136 ex,
137 )
138
139 if acl.endswith(".xml"):
140 # Apply the passed in ACL xml file to the uploaded object.
141 gs_context.SetACL(remote_file, acl=acl)
142 else:
143 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700144
Mike Frysingercc838832014-05-24 13:10:30 -0400145
Mike Frysinger540883b2014-05-24 13:46:16 -0400146def RemoteUpload(gs_context, acl, files, pool=10):
Alex Klein1699fab2022-09-08 08:46:06 -0600147 """Upload to google storage.
David James8c846492011-01-25 17:07:29 -0800148
Alex Klein1699fab2022-09-08 08:46:06 -0600149 Create a pool of process and call _GsUpload with the proper arguments.
David James8c846492011-01-25 17:07:29 -0800150
Alex Klein1699fab2022-09-08 08:46:06 -0600151 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600152 gs_context: A lib.gs.GSContext instance.
153 acl: The canned acl used for uploading. acl can be one of:
154 "public-read", "public-read-write", "authenticated-read",
155 "bucket-owner-read", "bucket-owner-full-control", or "private".
156 files: dictionary with keys to local files and values to remote path.
157 pool: integer of maximum processes to have at the same time.
David James8c846492011-01-25 17:07:29 -0800158
Alex Klein1699fab2022-09-08 08:46:06 -0600159 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600160 Return a set of tuple arguments of the failed uploads
Alex Klein1699fab2022-09-08 08:46:06 -0600161 """
162 upload = functools.partial(_GsUpload, gs_context, acl)
163 tasks = [[key, value] for key, value in files.items()]
164 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800165
166
167def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
Alex Klein1699fab2022-09-08 08:46:06 -0600168 """Build a dictionary of local remote file key pairs to upload.
David James8c846492011-01-25 17:07:29 -0800169
Alex Klein1699fab2022-09-08 08:46:06 -0600170 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600171 base_local_path: The base path to the files on the local hard drive.
172 base_remote_path: The base path to the remote paths.
173 pkgs: The packages to upload.
David James8c846492011-01-25 17:07:29 -0800174
Alex Klein1699fab2022-09-08 08:46:06 -0600175 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600176 Returns a dictionary of local_path/remote_path pairs
Alex Klein1699fab2022-09-08 08:46:06 -0600177 """
178 upload_files = {}
179 for pkg in pkgs:
180 suffix = pkg["CPV"] + ".tbz2"
181 local_path = os.path.join(base_local_path, suffix)
182 assert os.path.exists(local_path), "%s does not exist" % local_path
183 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800184
Alex Klein1699fab2022-09-08 08:46:06 -0600185 if pkg.get("DEBUG_SYMBOLS") == "yes":
186 debugsuffix = pkg["CPV"] + ".debug.tbz2"
187 local_path = os.path.join(base_local_path, debugsuffix)
188 assert os.path.exists(local_path)
189 upload_files[local_path] = os.path.join(
190 base_remote_path, debugsuffix
191 )
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800192
Alex Klein1699fab2022-09-08 08:46:06 -0600193 return upload_files
David James8c846492011-01-25 17:07:29 -0800194
Mike Frysingercc838832014-05-24 13:10:30 -0400195
Peter Mayo950e41a2014-02-06 21:07:33 +0000196def GetBoardOverlay(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600197 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500198
Alex Klein1699fab2022-09-08 08:46:06 -0600199 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600200 build_path: The path to the root of the build directory.
201 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500202
Alex Klein1699fab2022-09-08 08:46:06 -0600203 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600204 The last overlay configured for the given board as a string.
Alex Klein1699fab2022-09-08 08:46:06 -0600205 """
206 board = target.board_variant
207 overlays = portage_util.FindOverlays(
208 constants.BOTH_OVERLAYS, board, buildroot=build_path
209 )
210 # We only care about the last entry.
211 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800212
213
214def DeterminePrebuiltConfFile(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600215 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
David James8c846492011-01-25 17:07:29 -0800216
Alex Klein1699fab2022-09-08 08:46:06 -0600217 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600218 build_path: The path to the root of the build directory.
219 target: String representation of the board. This includes host and board
220 targets.
David James8c846492011-01-25 17:07:29 -0800221
Alex Klein1699fab2022-09-08 08:46:06 -0600222 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600223 A string path to a prebuilt.conf file to be updated.
Alex Klein1699fab2022-09-08 08:46:06 -0600224 """
225 if _HOST_ARCH == target:
226 # We are host.
227 # Without more examples of hosts this is a kludge for now.
228 # TODO(Scottz): as new host targets come online expand this to
229 # work more like boards.
230 make_path = _PREBUILT_MAKE_CONF[target]
231 else:
232 # We are a board
233 board = GetBoardOverlay(build_path, target)
234 make_path = os.path.join(board, "prebuilt.conf")
David James8c846492011-01-25 17:07:29 -0800235
Alex Klein1699fab2022-09-08 08:46:06 -0600236 return make_path
David James8c846492011-01-25 17:07:29 -0800237
238
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600239def UpdateBinhostConfFile(filepath: str, key: str, value: str) -> None:
240 """Update binhost config file with key=value.
241
242 The updated file will be committed, but not submitted.
David James8c846492011-01-25 17:07:29 -0800243
Alex Klein1699fab2022-09-08 08:46:06 -0600244 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600245 filepath: Path to the key-value store file to update.
246 key: Key to update.
247 value: New value for key.
Alex Klein1699fab2022-09-08 08:46:06 -0600248 """
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600249 dirname, basename = os.path.split(os.path.abspath(filepath))
250 osutils.SafeMakedirs(dirname)
251 if not git.GetCurrentBranch(dirname):
252 git.CreatePushBranch(
253 constants.STABLE_EBUILD_BRANCH, dirname, sync=False
Alex Klein1699fab2022-09-08 08:46:06 -0600254 )
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600255 osutils.WriteFile(filepath, "", mode="a")
256 if binpkg.UpdateKeyInLocalFile(filepath, key, value):
257 desc = f"{basename}: {'updating' if value else 'clearing'} {key}"
258 git.AddPath(filepath)
259 git.Commit(dirname, desc)
Alex Klein1699fab2022-09-08 08:46:06 -0600260
David James8c846492011-01-25 17:07:29 -0800261
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800262def GenerateHtmlIndex(files, index, board, version, remote_location):
Alex Klein1699fab2022-09-08 08:46:06 -0600263 """Given the list of |files|, generate an index.html at |index|.
Mike Frysinger212e4292014-05-24 15:15:44 -0400264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600266 files: The list of files to link to.
267 index: The path to the html index.
268 board: Name of the board this index is for.
269 version: Build version this index is for.
270 remote_location: Remote gs location prebuilts are uploaded to.
Alex Klein1699fab2022-09-08 08:46:06 -0600271 """
272 title = "Package Prebuilt Index: %s / %s" % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 files = files + [
275 ".|Google Storage Index",
276 "..|",
277 ]
278 commands.GenerateHtmlIndex(
279 index, files, title=title, url_base=gs.GsUrlToHttp(remote_location)
280 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400281
282
David Jamesce093af2011-02-23 15:21:58 -0800283def _GrabAllRemotePackageIndexes(binhost_urls):
Alex Kleind7197402023-04-05 13:05:29 -0600284 """Grab all the packages files associated with a list of binhost_urls.
David James05bcb2b2011-02-09 09:25:47 -0800285
Alex Klein1699fab2022-09-08 08:46:06 -0600286 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600287 binhost_urls: The URLs for the directories containing the Packages files
288 we want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600291 A list of PackageIndex objects.
Alex Klein1699fab2022-09-08 08:46:06 -0600292 """
293 pkg_indexes = []
294 for url in binhost_urls:
295 pkg_index = binpkg.GrabRemotePackageIndex(url)
296 if pkg_index:
297 pkg_indexes.append(pkg_index)
298 return pkg_indexes
David James05bcb2b2011-02-09 09:25:47 -0800299
300
Alex Klein074f94f2023-06-22 10:32:06 -0600301class PrebuiltUploader:
Alex Klein1699fab2022-09-08 08:46:06 -0600302 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800303
Alex Klein1699fab2022-09-08 08:46:06 -0600304 def __init__(
305 self,
306 upload_location,
307 acl,
308 binhost_base_url,
309 pkg_indexes,
310 build_path,
311 packages,
312 skip_upload,
313 binhost_conf_dir,
314 dryrun,
315 target,
316 slave_targets,
317 version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700318 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600319 chroot=None,
Brian Norris74d1af92023-06-14 11:28:12 -0700320 out_dir=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600321 ):
322 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800323
Alex Klein1699fab2022-09-08 08:46:06 -0600324 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800325
Alex Klein1699fab2022-09-08 08:46:06 -0600326 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600327 upload_location: The upload location.
328 acl: The canned acl used for uploading to Google Storage. acl can be
329 one of: "public-read", "public-read-write",
330 "authenticated-read", "bucket-owner-read",
331 "bucket-owner-full-control", "project-private", or "private"
332 (see "gsutil help acls"). If we are not uploading to Google
333 Storage, this parameter is unused.
334 binhost_base_url: The URL used for downloading the prebuilts.
335 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
336 uploading duplicate files, we just link to the old files.
337 build_path: The path to the directory containing the chroot.
338 packages: Packages to upload.
339 skip_upload: Don't actually upload the tarballs.
340 binhost_conf_dir: Directory where to store binhost.conf files.
341 dryrun: Don't push or upload prebuilts.
342 target: BuildTarget managed by this builder.
343 slave_targets: List of BuildTargets managed by slave builders.
344 version: A unique string, intended to be included in the upload
345 path, which identifies the version number of the uploaded
346 prebuilts.
347 report: Dict in which to collect information to report to the user.
Brian Norris74d1af92023-06-14 11:28:12 -0700348 chroot: Path to the chroot.
349 out_dir: Path to the SDK output directory for the chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600350 """
351 self._upload_location = upload_location
352 self._acl = acl
353 self._binhost_base_url = binhost_base_url
354 self._pkg_indexes = pkg_indexes
355 self._build_path = build_path
356 self._packages = set(packages)
357 self._found_packages = set()
358 self._skip_upload = skip_upload
359 self._binhost_conf_dir = binhost_conf_dir
360 self._dryrun = dryrun
361 self._target = target
362 self._slave_targets = slave_targets
363 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700364 self._report = report
Brian Norris74d1af92023-06-14 11:28:12 -0700365 chroot_path = chroot or os.path.join(
Alex Klein1699fab2022-09-08 08:46:06 -0600366 build_path, constants.DEFAULT_CHROOT_DIR
367 )
Brian Norris74d1af92023-06-14 11:28:12 -0700368 out_path = Path(
369 out_dir or os.path.join(build_path, constants.DEFAULT_OUT_DIR)
370 )
371 self._chroot = chroot_lib.Chroot(path=chroot_path, out_path=out_path)
Alex Klein1699fab2022-09-08 08:46:06 -0600372 self._gs_context = gs.GSContext(
373 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
374 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 def _Upload(self, local_file, remote_file):
377 """Wrapper around _GsUpload"""
378 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700379
Alex Klein1699fab2022-09-08 08:46:06 -0600380 def _ShouldFilterPackage(self, pkg):
381 if not self._packages:
382 return False
383 cpv = package_info.SplitCPV(pkg["CPV"])
384 self._found_packages.add(cpv.cp)
385 return (
386 cpv.package not in self._packages and cpv.cp not in self._packages
387 )
David James8c846492011-01-25 17:07:29 -0800388
Alex Klein1699fab2022-09-08 08:46:06 -0600389 def _UploadPrebuilt(self, package_path, url_suffix):
390 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600393 package_path: The path to the packages dir.
394 url_suffix: The remote subdirectory where we should upload the
395 packages.
Alex Klein1699fab2022-09-08 08:46:06 -0600396 """
397 # Process Packages file, removing duplicates and filtered packages.
398 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
399 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
400 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
401 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
402 unmatched_pkgs = self._packages - self._found_packages
403 if unmatched_pkgs:
404 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800405
Alex Klein1699fab2022-09-08 08:46:06 -0600406 # Write Packages file.
407 pkg_index.header["TTL"] = _BINPKG_TTL
408 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800409
Alex Klein1699fab2022-09-08 08:46:06 -0600410 remote_location = "%s/%s" % (
411 self._upload_location.rstrip("/"),
412 url_suffix,
413 )
414 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800415
Alex Klein1699fab2022-09-08 08:46:06 -0600416 upload_files = GenerateUploadDict(
417 package_path, remote_location, uploads
418 )
419 remote_file = "%s/Packages" % remote_location.rstrip("/")
420 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700421
Alex Klein1699fab2022-09-08 08:46:06 -0600422 # Build list of files to upload. Manually include the dev-only files but
423 # skip them if not present.
424 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
425 if os.path.exists(dev_only):
426 upload_files[dev_only] = "%s/%s" % (
427 remote_location.rstrip("/"),
428 os.path.basename(dev_only),
429 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400430
Alex Klein1699fab2022-09-08 08:46:06 -0600431 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800432
Alex Klein1699fab2022-09-08 08:46:06 -0600433 with tempfile.NamedTemporaryFile(
434 prefix="chromite.upload_prebuilts.index."
435 ) as index:
436 GenerateHtmlIndex(
437 [x[len(remote_location) + 1 :] for x in upload_files.values()],
438 index.name,
439 self._target,
440 self._version,
441 remote_location,
442 )
443 self._Upload(
444 index.name, "%s/index.html" % remote_location.rstrip("/")
445 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400446
Alex Klein1699fab2022-09-08 08:46:06 -0600447 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
448 url = "%s%s/index.html" % (
449 gs.PUBLIC_BASE_HTTPS_URL,
450 remote_location[len(gs.BASE_GS_URL) :],
451 )
452 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400453
Alex Klein1699fab2022-09-08 08:46:06 -0600454 def _UploadSdkTarball(
455 self,
456 board_path,
457 url_suffix,
458 prepackaged,
459 toolchains_overlay_tarballs,
460 toolchains_overlay_upload_path,
461 toolchain_tarballs,
462 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600463 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600464 ):
465 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700466
Alex Klein1699fab2022-09-08 08:46:06 -0600467 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600468 board_path: The path to the board dir.
469 url_suffix: The remote subdirectory where we should upload the
470 packages.
471 prepackaged: If given, a tarball that has been packaged outside of
472 this script and should be used.
473 toolchains_overlay_tarballs: List of toolchains overlay tarball
474 specifications to upload. Items take the form
475 "toolchains_spec:/path/to/tarball".
476 toolchains_overlay_upload_path: Path template under the bucket to
477 place toolchains overlay tarballs.
478 toolchain_tarballs: List of toolchain tarballs to upload.
479 toolchain_upload_path: Path under the bucket to place toolchain
480 tarballs.
481 sync_remote_latest_sdk_file: If True, update the remote latest SDK
482 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600483 """
484 remote_location = "%s/%s" % (
485 self._upload_location.rstrip("/"),
486 url_suffix,
487 )
488 assert remote_location.startswith("gs://")
489 boardname = os.path.basename(board_path.rstrip("/"))
490 # We do not upload non SDK board tarballs,
491 assert boardname == constants.CHROOT_BUILDER_BOARD
492 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200493
Alex Klein1699fab2022-09-08 08:46:06 -0600494 version_str = self._version[len("chroot-") :]
495 remote_tarfile = toolchain.GetSdkURL(
496 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
497 )
498 # For SDK, also upload the manifest which is guaranteed to exist
499 # by the builderstage.
500 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
501 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200502
Alex Klein1699fab2022-09-08 08:46:06 -0600503 # Upload SDK toolchains overlays and toolchain tarballs, if given.
504 for tarball_list, upload_path, qualifier_name in (
505 (
506 toolchains_overlay_tarballs,
507 toolchains_overlay_upload_path,
508 "toolchains",
509 ),
510 (toolchain_tarballs, toolchain_upload_path, "target"),
511 ):
512 for tarball_spec in tarball_list:
513 qualifier_val, local_path = tarball_spec.split(":")
514 suburl = upload_path % {qualifier_name: qualifier_val}
515 remote_path = toolchain.GetSdkURL(
516 for_gsutil=True, suburl=suburl
517 )
518 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500519
Alex Klein1699fab2022-09-08 08:46:06 -0600520 # Finally, also update the pointer to the latest SDK on which polling
521 # scripts rely.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600522 if sync_remote_latest_sdk_file:
523 self._UpdateRemoteSdkLatestFile(latest_sdk=version_str)
Greg Edelston64620d12023-02-23 15:29:49 -0700524
525 def _UpdateRemoteSdkLatestFile(
526 self,
527 latest_sdk: Optional[str] = None,
528 latest_sdk_uprev_target: Optional[str] = None,
529 ) -> None:
530 """Update the remote SDK pointer file on GS://.
531
532 The remote file contains multiple key-value pairs. This function can
533 update one or more of them; Nones will retain their existing values.
534
535 Args:
536 latest_sdk: The latest SDK that is tested and ready to be used. If
537 None, then the existing value on GS:// will be retained.
538 latest_sdk_uprev_target: The latest SDK that has been built, which
539 new PUprs can try to test and uprev. If None, then the existing
540 value on GS:// will be retained.
541 """
Greg Edelston119cbe12023-03-29 14:26:52 -0600542 # This would be a noop in dryrun mode -- or worse, it would fail to
543 # parse the remote key-value store after pretending to download it.
544 # Instead of side-stepping errors, return early with descriptive logs.
545 if self._dryrun:
546 logging.debug("Not updating remote SDK latest file in dryrun mode.")
547 if latest_sdk is not None:
548 logging.debug("Would have set LATEST_SDK=%s", latest_sdk)
549 if latest_sdk_uprev_target is not None:
550 logging.debug(
551 "Would have set LATEST_SDK_UPREV_TARGET=%s",
552 latest_sdk_uprev_target,
553 )
554 return
555
Greg Edelston64620d12023-02-23 15:29:49 -0700556 # Get existing values from the remote file.
557 remote_pointerfile = toolchain.GetSdkURL(
558 for_gsutil=True, suburl="cros-sdk-latest.conf"
559 )
560 existing_keyval = self._gs_context.LoadKeyValueStore(
561 remote_pointerfile, acl=self._acl
562 )
Greg Edelston2469ad32023-03-20 13:55:21 -0600563
564 # TODO(b/274196697): When LATEST_SDK_UPREV_TARGET is more reliably found
565 # in the remote latest file, require that key too.
566 for required_key in ("LATEST_SDK",):
567 if required_key not in existing_keyval:
568 raise ValueError(
Alex Kleind7197402023-04-05 13:05:29 -0600569 f"Remote pointer file {remote_pointerfile} missing "
570 f"expected key {required_key}:\n{existing_keyval}"
Greg Edelston2469ad32023-03-20 13:55:21 -0600571 )
Greg Edelston64620d12023-02-23 15:29:49 -0700572
573 # If any values were not specified in args, use the existing values.
574 if latest_sdk is None:
575 latest_sdk = existing_keyval["LATEST_SDK"]
576 if latest_sdk_uprev_target is None:
Greg Edelston2469ad32023-03-20 13:55:21 -0600577 latest_sdk_uprev_target = existing_keyval.get(
578 "LATEST_SDK_UPREV_TARGET", None
579 )
Greg Edelston64620d12023-02-23 15:29:49 -0700580
581 # Write a new local latest file with target values, and upload.
582 new_file_contents = self._CreateRemoteSdkLatestFileContents(
583 latest_sdk, latest_sdk_uprev_target
584 )
585 with osutils.TempDir() as tmpdir:
586 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
587 osutils.WriteFile(local_pointerfile, new_file_contents)
588 self._Upload(local_pointerfile, remote_pointerfile)
589
590 @staticmethod
591 def _CreateRemoteSdkLatestFileContents(
592 latest_sdk: str, latest_sdk_uprev_target: str
593 ) -> str:
594 """Generate file contents for a remote SDK file.
595
596 Args:
597 latest_sdk: The latest SDK that is tested and ready to be used.
598 latest_sdk_uprev_target: The latest SDK that has been built, which
599 new PUprs can try to test and uprev.
600
601 Returns:
602 The contents of a remote SDK latest file containing the given args
603 as a key-value store.
604 """
605 return f"""\
606# The most recent SDK that is tested and ready for use.
607LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700608
609# The most recently built version. New uprev attempts should target this.
610# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700611LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200612
Alex Klein1699fab2022-09-08 08:46:06 -0600613 def _GetTargets(self):
614 """Retuns the list of targets to use."""
615 targets = self._slave_targets[:]
616 if self._target:
617 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700618
Alex Klein1699fab2022-09-08 08:46:06 -0600619 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700620
Alex Klein1699fab2022-09-08 08:46:06 -0600621 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
622 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800623
Alex Klein1699fab2022-09-08 08:46:06 -0600624 This function will sync both the standard host packages, plus the host
625 packages associated with all targets that have been "setup" with the
626 current host's chroot. For instance, if this host has been used to build
627 x86-generic, it will sync the host packages associated with
Alex Kleind7197402023-04-05 13:05:29 -0600628 'i686-pc-linux-gnu'. If this host has also been used to build
629 arm-generic, it will also sync the host packages associated with
Alex Klein1699fab2022-09-08 08:46:06 -0600630 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800631
Alex Klein1699fab2022-09-08 08:46:06 -0600632 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600633 key: The variable key to update in the git file.
634 git_sync: If set, update make.conf of target to reference the latest
635 prebuilt packages generated here.
636 sync_binhost_conf: If set, update binhost config file in
637 chromiumos-overlay for the host.
Alex Klein1699fab2022-09-08 08:46:06 -0600638 """
Alex Kleind7197402023-04-05 13:05:29 -0600639 # Slave boards are listed before the master board so that the master
640 # board takes priority (i.e. x86-generic preflight host prebuilts takes
641 # priority over preflight host prebuilts from other builders.)
Alex Klein1699fab2022-09-08 08:46:06 -0600642 binhost_urls = []
643 for target in self._GetTargets():
644 url_suffix = _REL_HOST_PATH % {
645 "version": self._version,
646 "host_arch": _HOST_ARCH,
647 "target": target,
648 }
649 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800650
Alex Klein1699fab2022-09-08 08:46:06 -0600651 if self._target == target and not self._skip_upload:
652 # Upload prebuilts.
Brian Norris74d1af92023-06-14 11:28:12 -0700653 package_path = self._chroot.full_path(_HOST_PACKAGES_PATH)
Alex Klein1699fab2022-09-08 08:46:06 -0600654 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700655
Alex Klein1699fab2022-09-08 08:46:06 -0600656 # Record URL where prebuilts were uploaded.
657 binhost_urls.append(
658 "%s/%s/"
659 % (
660 self._binhost_base_url.rstrip("/"),
661 packages_url_suffix.rstrip("/"),
662 )
663 )
David Jamese2488642011-11-14 16:15:20 -0800664
Alex Klein1699fab2022-09-08 08:46:06 -0600665 binhost = " ".join(binhost_urls)
666 if git_sync:
667 git_file = os.path.join(
668 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
669 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700670 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700671 git_file, {key: binhost}, self._report, dryrun=self._dryrun
672 )
Alex Klein1699fab2022-09-08 08:46:06 -0600673 if sync_binhost_conf:
674 binhost_conf = os.path.join(
675 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
676 )
677 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800678
Alex Klein1699fab2022-09-08 08:46:06 -0600679 def SyncBoardPrebuilts(
680 self,
681 key,
682 git_sync,
683 sync_binhost_conf,
684 upload_board_tarball,
685 prepackaged_board,
686 toolchains_overlay_tarballs,
687 toolchains_overlay_upload_path,
688 toolchain_tarballs,
689 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600690 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600691 ):
692 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800693
Alex Klein1699fab2022-09-08 08:46:06 -0600694 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600695 key: The variable key to update in the git file.
696 git_sync: If set, update make.conf of target to reference the latest
697 prebuilt packages generated here.
698 sync_binhost_conf: If set, update binhost config file in
699 chromiumos-overlay for the current board.
700 upload_board_tarball: Include a tarball of the board in our upload.
701 prepackaged_board: A tarball of the board built outside of this
702 script.
703 toolchains_overlay_tarballs: List of toolchains overlay tarball
704 specifications to upload. Items take the form
705 "toolchains_spec:/path/to/tarball".
706 toolchains_overlay_upload_path: Path template under the bucket to
707 place toolchains overlay tarballs.
708 toolchain_tarballs: A list of toolchain tarballs to upload.
709 toolchain_upload_path: Path under the bucket to place toolchain
710 tarballs.
711 sync_remote_latest_sdk_file: If True, update the remote latest SDK
712 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600713 """
714 updated_binhosts = set()
715 for target in self._GetTargets():
Brian Norris74d1af92023-06-14 11:28:12 -0700716 board_path = self._chroot.full_path(
717 _BOARD_PATH % {"board": target.board_variant}
Alex Klein1699fab2022-09-08 08:46:06 -0600718 )
719 package_path = os.path.join(board_path, "packages")
720 url_suffix = _REL_BOARD_PATH % {
721 "target": target,
722 "version": self._version,
723 }
724 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 # Process the target board differently if it is the main --board.
727 if self._target == target and not self._skip_upload:
Alex Kleind7197402023-04-05 13:05:29 -0600728 # This strips "chroot" prefix because that is sometimes added as
729 # the --prepend-version argument (e.g. by chromiumos-sdk bot).
Alex Klein1699fab2022-09-08 08:46:06 -0600730 # TODO(build): Clean it up to be less hard-coded.
731 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500732
Alex Klein1699fab2022-09-08 08:46:06 -0600733 # Upload board tarballs in the background.
734 if upload_board_tarball:
735 if toolchain_upload_path:
736 toolchain_upload_path %= {"version": version_str}
737 if toolchains_overlay_upload_path:
738 toolchains_overlay_upload_path %= {
739 "version": version_str
740 }
741 tar_process = multiprocessing.Process(
742 target=self._UploadSdkTarball,
743 args=(
744 board_path,
745 url_suffix,
746 prepackaged_board,
747 toolchains_overlay_tarballs,
748 toolchains_overlay_upload_path,
749 toolchain_tarballs,
750 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600751 sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600752 ),
753 )
754 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700755
Alex Klein1699fab2022-09-08 08:46:06 -0600756 # Upload prebuilts.
757 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700758
Alex Klein1699fab2022-09-08 08:46:06 -0600759 # Make sure we finished uploading the board tarballs.
760 if upload_board_tarball:
761 tar_process.join()
762 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800763
Alex Klein1699fab2022-09-08 08:46:06 -0600764 # Record URL where prebuilts were uploaded.
765 url_value = "%s/%s/" % (
766 self._binhost_base_url.rstrip("/"),
767 packages_url_suffix.rstrip("/"),
768 )
David Jamese2488642011-11-14 16:15:20 -0800769
Alex Klein1699fab2022-09-08 08:46:06 -0600770 if git_sync:
771 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700772 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700773 git_file,
774 {key: url_value},
775 self._report,
776 dryrun=self._dryrun,
777 )
Matt Tennante8179042013-10-01 15:47:32 -0700778
Alex Klein1699fab2022-09-08 08:46:06 -0600779 if sync_binhost_conf:
780 # Update the binhost configuration file in git.
781 binhost_conf = os.path.join(
782 self._binhost_conf_dir,
783 "target",
784 "%s-%s.conf" % (target, key),
785 )
786 updated_binhosts.add(binhost_conf)
787 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800788
Alex Klein1699fab2022-09-08 08:46:06 -0600789 if sync_binhost_conf:
Alex Kleind7197402023-04-05 13:05:29 -0600790 # Clear all old binhosts. The files must be left empty in case
791 # anybody is referring to them.
Alex Klein1699fab2022-09-08 08:46:06 -0600792 all_binhosts = set(
793 glob.glob(
794 os.path.join(
795 self._binhost_conf_dir, "target", "*-%s.conf" % key
796 )
797 )
798 )
799 for binhost_conf in all_binhosts - updated_binhosts:
800 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800801
David James05bcb2b2011-02-09 09:25:47 -0800802
Mike Nicholsa1414162021-04-22 20:07:22 +0000803class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600804 """Callback that adds a slave board to the list of slave targets."""
805
806 def __call__(self, parser, namespace, values, option_string=None):
807 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800808
809
Mike Nicholsa1414162021-04-22 20:07:22 +0000810class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600811 """Callback that adds a slave profile to the list of slave targets."""
812
813 def __call__(self, parser, namespace, values, option_string=None):
814 if not namespace.slave_targets:
815 parser.error("Must specify --slave-board before --slave-profile")
816 if namespace.slave_targets[-1].profile is not None:
817 parser.error("Cannot specify --slave-profile twice for same board")
818 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800819
820
Greg Edelston325e4412023-05-04 16:22:10 -0600821def ParseOptions(argv) -> Tuple[argparse.Namespace, Optional[BuildTarget]]:
Alex Klein1699fab2022-09-08 08:46:06 -0600822 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700823
Alex Klein1699fab2022-09-08 08:46:06 -0600824 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600825 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400826
Alex Klein1699fab2022-09-08 08:46:06 -0600827 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600828 A tuple containing a parsed options object and BuildTarget.
829 The target instance is None if no board is specified.
Alex Klein1699fab2022-09-08 08:46:06 -0600830 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500831 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600832 parser.add_argument(
833 "-H",
834 "--binhost-base-url",
835 default=_BINHOST_BASE_URL,
836 help="Base URL to use for binhost in make.conf updates",
837 )
838 parser.add_argument(
839 "--previous-binhost-url",
840 action="append",
841 default=[],
842 help="Previous binhost URL",
843 )
844 parser.add_argument(
845 "-b", "--board", help="Board type that was built on this machine"
846 )
847 parser.add_argument(
848 "-B",
849 "--prepackaged-tarball",
850 type="path",
851 help="Board tarball prebuilt outside of this script.",
852 )
853 parser.add_argument(
854 "--toolchains-overlay-tarball",
855 dest="toolchains_overlay_tarballs",
856 action="append",
857 default=[],
858 help="Toolchains overlay tarball specification to "
859 "upload. Takes the form "
860 '"toolchains_spec:/path/to/tarball".',
861 )
862 parser.add_argument(
863 "--toolchains-overlay-upload-path",
864 default="",
865 help="Path template for uploading toolchains overlays.",
866 )
867 parser.add_argument(
868 "--toolchain-tarball",
869 dest="toolchain_tarballs",
870 action="append",
871 default=[],
872 help="Redistributable toolchain tarball.",
873 )
874 parser.add_argument(
875 "--toolchain-upload-path",
876 default="",
877 help="Path to place toolchain tarballs in the sdk tree.",
878 )
879 parser.add_argument(
880 "--profile", help="Profile that was built on this machine"
881 )
882 parser.add_argument(
883 "--slave-board",
884 default=[],
885 action=_AddSlaveBoardAction,
886 dest="slave_targets",
887 help="Board type that was built on a slave machine. To "
888 "add a profile to this board, use --slave-profile.",
889 )
890 parser.add_argument(
891 "--slave-profile",
892 action=_AddSlaveProfileAction,
893 help="Board profile that was built on a slave machine. "
894 "Applies to previous slave board.",
895 )
896 parser.add_argument(
897 "-p",
898 "--build-path",
899 required=True,
900 help="Path to the directory containing the chroot",
901 )
902 parser.add_argument(
903 "--chroot",
904 help="Path where the chroot is located. "
905 "(Default: {build_path}/chroot)",
906 )
907 parser.add_argument(
Brian Norris74d1af92023-06-14 11:28:12 -0700908 "--out-dir",
909 help="Path where the SDK output directory is located. "
910 "(Default: {build_path}/out)",
911 )
912 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700913 "--output",
914 type=Path,
915 help="Write a JSON report to the specified file. "
916 "(Default is not to write the report.)",
917 )
918 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600919 "--packages",
920 action="append",
921 default=[],
922 help="Only include the specified packages. "
923 "(Default is to include all packages.)",
924 )
925 parser.add_argument(
926 "-s",
927 "--sync-host",
928 default=False,
929 action="store_true",
930 help="Sync host prebuilts",
931 )
932 parser.add_argument(
933 "-g",
934 "--git-sync",
935 default=False,
936 action="store_true",
937 help="Enable git version sync (This commits to a repo.) "
938 "This is used by full builders to commit directly "
939 "to board overlays.",
940 )
Greg Edelston8da51ce2023-03-22 10:40:59 -0600941 parser.add_argument(
942 "--sync-remote-latest-sdk-file",
943 action="store_true",
Greg Edelston325e4412023-05-04 16:22:10 -0600944 default=True,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600945 help="Sync the remote latest SDK file on GS://. (Default)",
946 )
947 parser.add_argument(
948 "--no-sync-remote-latest-sdk-file",
949 dest="sync_remote_latest_sdk_file",
950 action="store_false",
951 help="Skip syncing the remote latest SDK file on GS://.",
952 )
Alex Klein1699fab2022-09-08 08:46:06 -0600953 parser.add_argument("-u", "--upload", help="Upload location")
954 parser.add_argument(
955 "-V",
956 "--prepend-version",
957 help="Add an identifier to the front of the version",
958 )
959 parser.add_argument(
960 "-f",
961 "--filters",
962 action="store_true",
963 default=False,
964 help="Turn on filtering of private ebuild packages",
965 )
966 parser.add_argument(
967 "-k",
968 "--key",
969 default="PORTAGE_BINHOST",
970 help="Key to update in make.conf / binhost.conf",
971 )
972 parser.add_argument("--set-version", help="Specify the version string")
973 parser.add_argument(
974 "--sync-binhost-conf",
975 default=False,
976 action="store_true",
977 help="Update binhost.conf in chromiumos-overlay or "
978 "chromeos-overlay. Commit the changes, but don't "
979 "push them. This is used for preflight binhosts.",
980 )
981 parser.add_argument(
982 "--binhost-conf-dir",
983 help="Directory to commit binhost config with " "--sync-binhost-conf.",
984 )
985 parser.add_argument(
986 "-P",
987 "--private",
988 action="store_true",
989 default=False,
990 help="Mark gs:// uploads as private.",
991 )
992 parser.add_argument(
993 "--skip-upload",
994 action="store_true",
995 default=False,
996 help="Skip upload step.",
997 )
998 parser.add_argument(
999 "--upload-board-tarball",
1000 action="store_true",
1001 default=False,
1002 help="Upload board tarball to Google Storage.",
1003 )
David James8c846492011-01-25 17:07:29 -08001004
Alex Klein1699fab2022-09-08 08:46:06 -06001005 options = parser.parse_args(argv)
1006 if not options.upload and not options.skip_upload:
1007 parser.error("you need to provide an upload location using -u")
1008 if not options.set_version and options.skip_upload:
1009 parser.error(
1010 "If you are using --skip-upload, you must specify a "
1011 "version number using --set-version."
1012 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001013
Alex Klein1699fab2022-09-08 08:46:06 -06001014 target = None
1015 if options.board:
1016 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -07001017
Alex Klein1699fab2022-09-08 08:46:06 -06001018 if target in options.slave_targets:
1019 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001020
Alex Klein1699fab2022-09-08 08:46:06 -06001021 if len(set(options.slave_targets)) != len(options.slave_targets):
1022 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001023
Alex Klein1699fab2022-09-08 08:46:06 -06001024 if options.slave_targets and options.git_sync:
1025 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001026
Alex Klein1699fab2022-09-08 08:46:06 -06001027 if (
1028 options.upload_board_tarball
1029 and options.skip_upload
1030 and options.board == "amd64-host"
1031 ):
1032 parser.error(
1033 "--skip-upload is not compatible with "
1034 "--upload-board-tarball and --board=amd64-host"
1035 )
David James8fa34ea2011-04-15 13:00:20 -07001036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 if (
1038 options.upload_board_tarball
1039 and not options.skip_upload
1040 and not options.upload.startswith("gs://")
1041 ):
1042 parser.error(
1043 "--upload-board-tarball only works with gs:// URLs.\n"
1044 "--upload must be a gs:// URL."
1045 )
David James8fa34ea2011-04-15 13:00:20 -07001046
Alex Klein1699fab2022-09-08 08:46:06 -06001047 if options.upload_board_tarball and options.prepackaged_tarball is None:
1048 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001049
Alex Klein1699fab2022-09-08 08:46:06 -06001050 if options.private:
1051 if options.sync_host:
1052 parser.error(
1053 "--private and --sync-host/-s cannot be specified "
1054 "together; we do not support private host prebuilts"
1055 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001056
Alex Klein1699fab2022-09-08 08:46:06 -06001057 if not options.upload or not options.upload.startswith("gs://"):
1058 parser.error(
1059 "--private is only valid for gs:// URLs; "
1060 "--upload must be a gs:// URL."
1061 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001062
Alex Klein1699fab2022-09-08 08:46:06 -06001063 if options.binhost_base_url != _BINHOST_BASE_URL:
1064 parser.error(
1065 "when using --private the --binhost-base-url "
1066 "is automatically derived."
1067 )
David James27fa7d12011-06-29 17:24:14 -07001068
Alex Klein1699fab2022-09-08 08:46:06 -06001069 if options.sync_binhost_conf and not options.binhost_conf_dir:
1070 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001071
Alex Klein1699fab2022-09-08 08:46:06 -06001072 if (
1073 options.toolchains_overlay_tarballs
1074 and not options.toolchains_overlay_upload_path
1075 ):
1076 parser.error(
1077 "--toolchains-overlay-tarball requires "
1078 "--toolchains-overlay-upload-path"
1079 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001080
Alex Klein1699fab2022-09-08 08:46:06 -06001081 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001082
Mike Frysingercc838832014-05-24 13:10:30 -04001083
Mike Frysinger86509232014-05-24 13:18:37 -04001084def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001085 # We accumulate information about actions taken and report it at the end
1086 # if asked to do so. Currently, this only records CL creation, which
1087 # is the only thing we need for now.
1088 report = {}
1089
Alex Klein1699fab2022-09-08 08:46:06 -06001090 # Set umask so that files created as root are readable.
1091 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001092
Alex Klein1699fab2022-09-08 08:46:06 -06001093 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001094
Alex Klein1699fab2022-09-08 08:46:06 -06001095 # Calculate a list of Packages index files to compare against. Whenever we
1096 # upload a package, we check to make sure it's not already stored in one of
1097 # the packages files we uploaded. This list of packages files might contain
1098 # both board and host packages.
1099 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001100
Alex Klein1699fab2022-09-08 08:46:06 -06001101 if options.set_version:
1102 version = options.set_version
1103 else:
1104 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001105
Alex Klein1699fab2022-09-08 08:46:06 -06001106 if options.prepend_version:
1107 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001108
Alex Klein1699fab2022-09-08 08:46:06 -06001109 acl = "public-read"
1110 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001111
Alex Klein1699fab2022-09-08 08:46:06 -06001112 if options.private:
1113 binhost_base_url = options.upload
1114 if target:
1115 acl = portage_util.FindOverlayFile(
1116 _GOOGLESTORAGE_GSUTIL_FILE,
1117 board=target.board_variant,
1118 buildroot=options.build_path,
1119 )
1120 if acl is None:
1121 cros_build_lib.Die(
1122 "No Google Storage ACL file %s found in %s overlay.",
1123 _GOOGLESTORAGE_GSUTIL_FILE,
1124 target.board_variant,
1125 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001126
Alex Klein1699fab2022-09-08 08:46:06 -06001127 binhost_conf_dir = None
1128 if options.binhost_conf_dir:
1129 binhost_conf_dir = os.path.join(
1130 options.build_path, options.binhost_conf_dir
1131 )
David Jamesb26b9312014-12-15 11:26:46 -08001132
Alex Klein1699fab2022-09-08 08:46:06 -06001133 uploader = PrebuiltUploader(
1134 options.upload,
1135 acl,
1136 binhost_base_url,
1137 pkg_indexes,
1138 options.build_path,
1139 options.packages,
1140 options.skip_upload,
1141 binhost_conf_dir,
1142 options.dryrun,
1143 target,
1144 options.slave_targets,
1145 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001146 report,
Brian Norris74d1af92023-06-14 11:28:12 -07001147 chroot=options.chroot,
1148 out_dir=options.out_dir,
Alex Klein1699fab2022-09-08 08:46:06 -06001149 )
David Jamesc0f158a2011-02-22 16:07:29 -08001150
Alex Klein1699fab2022-09-08 08:46:06 -06001151 if options.sync_host:
1152 uploader.SyncHostPrebuilts(
1153 options.key, options.git_sync, options.sync_binhost_conf
1154 )
David James8c846492011-01-25 17:07:29 -08001155
Alex Klein1699fab2022-09-08 08:46:06 -06001156 if options.board or options.slave_targets:
1157 uploader.SyncBoardPrebuilts(
1158 options.key,
1159 options.git_sync,
1160 options.sync_binhost_conf,
1161 options.upload_board_tarball,
1162 options.prepackaged_tarball,
1163 options.toolchains_overlay_tarballs,
1164 options.toolchains_overlay_upload_path,
1165 options.toolchain_tarballs,
1166 options.toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -06001167 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -06001168 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001169
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001170 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001171 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001172 pformat.json(report, fp=f)