blob: 6b2671e2fd25e2375f57db1e8dddb410d03a675b [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 Edelston64620d12023-02-23 15:29:49 -070029from typing import Optional
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
Mike Frysinger86509232014-05-24 13:18:37 -040034from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060035from chromite.lib import constants
Chris Sosa1dc96132012-05-11 15:40:50 -070036from chromite.lib import cros_build_lib
Mike Frysinger807d8282022-04-28 22:45:17 -040037from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070038from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070039from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080040from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070041from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050042from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060043from chromite.lib.parser import package_info
Bob Haarmanc0082602022-09-20 16:12:43 -070044from chromite.utils import pformat
Chris Sosa1dc96132012-05-11 15:40:50 -070045
Mike Frysinger72b7cf92020-04-19 06:00:51 -040046
David James015af872012-06-19 15:24:36 -070047# How many times to retry uploads.
48_RETRIES = 10
49
50# Multiplier for how long to sleep (in seconds) between retries; will delay
51# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
52_SLEEP_TIME = 60
53
David James5ab67e32014-10-24 08:19:59 -070054# The length of time (in seconds) that Portage should wait before refetching
55# binpkgs from the same binhost. We don't ever modify binhosts, so this should
56# be something big.
57_BINPKG_TTL = 60 * 60 * 24 * 365
58
Alex Klein1699fab2022-09-08 08:46:06 -060059_HOST_PACKAGES_PATH = "var/lib/portage/pkgs"
60_CATEGORIES_PATH = "chroot/etc/portage/categories"
61_PYM_PATH = "chroot/usr/lib/portage/pym"
62_HOST_ARCH = "amd64"
63_BOARD_PATH = "chroot/build/%(board)s"
64_REL_BOARD_PATH = "board/%(target)s/%(version)s"
65_REL_HOST_PATH = "host/%(host_arch)s/%(target)s/%(version)s"
David James8c846492011-01-25 17:07:29 -080066# Private overlays to look at for builds to filter
67# relative to build path
Alex Klein1699fab2022-09-08 08:46:06 -060068_PRIVATE_OVERLAY_DIR = "src/private-overlays"
69_GOOGLESTORAGE_GSUTIL_FILE = "googlestorage_acl.txt"
70_BINHOST_BASE_URL = "gs://chromeos-prebuilt"
71_PREBUILT_BASE_DIR = "src/third_party/chromiumos-overlay/chromeos/config/"
David James8c846492011-01-25 17:07:29 -080072# Created in the event of new host targets becoming available
Alex Klein1699fab2022-09-08 08:46:06 -060073_PREBUILT_MAKE_CONF = {
74 "amd64": os.path.join(_PREBUILT_BASE_DIR, "make.conf.amd64-host")
75}
David James8c846492011-01-25 17:07:29 -080076
77
David James4058b0d2011-12-08 21:24:50 -080078class BuildTarget(object):
Alex Klein1699fab2022-09-08 08:46:06 -060079 """A board/variant/profile tuple."""
David James4058b0d2011-12-08 21:24:50 -080080
Alex Klein1699fab2022-09-08 08:46:06 -060081 def __init__(self, board_variant, profile=None):
82 self.board_variant = board_variant
83 self.board, _, self.variant = board_variant.partition("_")
84 self.profile = profile
David James4058b0d2011-12-08 21:24:50 -080085
Alex Klein1699fab2022-09-08 08:46:06 -060086 def __str__(self):
87 if self.profile:
88 return "%s_%s" % (self.board_variant, self.profile)
89 else:
90 return self.board_variant
David James4058b0d2011-12-08 21:24:50 -080091
Alex Klein1699fab2022-09-08 08:46:06 -060092 def __eq__(self, other):
93 return str(other) == str(self)
David James4058b0d2011-12-08 21:24:50 -080094
Alex Klein1699fab2022-09-08 08:46:06 -060095 def __hash__(self):
96 return hash(str(self))
David James4058b0d2011-12-08 21:24:50 -080097
98
David James8c846492011-01-25 17:07:29 -080099def GetVersion():
Alex Klein1699fab2022-09-08 08:46:06 -0600100 """Get the version to put in LATEST and update the git version with."""
101 return datetime.datetime.now().strftime("%Y.%m.%d.%H%M%S")
David James8c846492011-01-25 17:07:29 -0800102
103
Mike Frysinger540883b2014-05-24 13:46:16 -0400104def _GsUpload(gs_context, acl, local_file, remote_file):
Alex Klein1699fab2022-09-08 08:46:06 -0600105 """Upload to GS bucket.
David James8c846492011-01-25 17:07:29 -0800106
Alex Klein1699fab2022-09-08 08:46:06 -0600107 Args:
108 gs_context: A lib.gs.GSContext instance.
109 acl: The ACL to use for uploading the file.
110 local_file: The local file to be uploaded.
111 remote_file: The remote location to upload to.
112 """
113 CANNED_ACLS = [
114 "public-read",
115 "private",
116 "bucket-owner-read",
117 "authenticated-read",
118 "bucket-owner-full-control",
119 "public-read-write",
120 ]
121 if acl in CANNED_ACLS:
122 gs_context.Copy(local_file, remote_file, acl=acl)
Gabe Black40169e62014-06-17 15:23:47 -0700123 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600124 # For private uploads we assume that the overlay board is set up properly
125 # and a googlestore_acl.xml is present. Otherwise, this script errors.
126 # We set version=0 here to ensure that the ACL is set only once (see
127 # http://b/15883752#comment54).
128 try:
129 gs_context.Copy(local_file, remote_file, version=0)
130 except gs.GSContextPreconditionFailed as ex:
131 # If we received a GSContextPreconditionFailed error, we know that the
132 # file exists now, but we don't know whether our specific update
133 # succeeded. See http://b/15883752#comment62
134 logging.warning(
135 "Assuming upload succeeded despite PreconditionFailed errors: %s",
136 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:
152 gs_context: A lib.gs.GSContext instance.
153 acl: The canned acl used for uploading. acl can be one of: "public-read",
154 "public-read-write", "authenticated-read", "bucket-owner-read",
155 "bucket-owner-full-control", or "private".
156 files: dictionary with keys to local files and values to remote path.
157 pool: integer of maximum proesses to have at the same time.
David James8c846492011-01-25 17:07:29 -0800158
Alex Klein1699fab2022-09-08 08:46:06 -0600159 Returns:
160 Return a set of tuple arguments of the failed uploads
161 """
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:
171 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:
176 Returns a dictionary of local_path/remote_path pairs
177 """
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:
200 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:
204 The last overlay configured for the given board as a string.
205 """
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:
218 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:
223 A string path to a prebuilt.conf file to be updated.
224 """
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
239def UpdateBinhostConfFile(path, key, value):
Alex Klein1699fab2022-09-08 08:46:06 -0600240 """Update binhost config file file with key=value.
David James8c846492011-01-25 17:07:29 -0800241
Alex Klein1699fab2022-09-08 08:46:06 -0600242 Args:
243 path: Filename to update.
244 key: Key to update.
245 value: New value for key.
246 """
247 cwd, filename = os.path.split(os.path.abspath(path))
248 osutils.SafeMakedirs(cwd)
249 if not git.GetCurrentBranch(cwd):
250 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
251 osutils.WriteFile(path, "", mode="a")
Greg Edelston86aea7b2023-02-15 16:50:25 -0700252 if binpkg.UpdateKeyInLocalFile(path, value, key):
Alex Klein1699fab2022-09-08 08:46:06 -0600253 desc = "%s: %s %s" % (
254 filename,
255 "updating" if value else "clearing",
256 key,
257 )
258 git.AddPath(path)
259 git.Commit(cwd, desc)
260
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:
266 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.
271 """
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 Klein1699fab2022-09-08 08:46:06 -0600284 """Grab all of 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:
287 binhost_urls: The URLs for the directories containing the Packages files we
288 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 Returns:
291 A list of PackageIndex objects.
292 """
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
David Jamesc0f158a2011-02-22 16:07:29 -0800301class PrebuiltUploader(object):
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,
320 ):
321 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800322
Alex Klein1699fab2022-09-08 08:46:06 -0600323 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 Args:
326 upload_location: The upload location.
327 acl: The canned acl used for uploading to Google Storage. acl can be one
328 of: "public-read", "public-read-write", "authenticated-read",
329 "bucket-owner-read", "bucket-owner-full-control", "project-private",
330 or "private" (see "gsutil help acls"). If we are not uploading to
331 Google Storage, this parameter is unused.
332 binhost_base_url: The URL used for downloading the prebuilts.
333 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
334 uploading duplicate files, we just link to the old files.
335 build_path: The path to the directory containing the chroot.
336 packages: Packages to upload.
337 skip_upload: Don't actually upload the tarballs.
338 binhost_conf_dir: Directory where to store binhost.conf files.
339 dryrun: Don't push or upload prebuilts.
340 target: BuildTarget managed by this builder.
341 slave_targets: List of BuildTargets managed by slave builders.
342 version: A unique string, intended to be included in the upload path,
343 which identifies the version number of the uploaded prebuilts.
Bob Haarmanc0082602022-09-20 16:12:43 -0700344 report: Dict in which to collect information to report to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600345 chroot: Path to the chroot containing the prebuilts.
346 """
347 self._upload_location = upload_location
348 self._acl = acl
349 self._binhost_base_url = binhost_base_url
350 self._pkg_indexes = pkg_indexes
351 self._build_path = build_path
352 self._packages = set(packages)
353 self._found_packages = set()
354 self._skip_upload = skip_upload
355 self._binhost_conf_dir = binhost_conf_dir
356 self._dryrun = dryrun
357 self._target = target
358 self._slave_targets = slave_targets
359 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700360 self._report = report
Alex Klein1699fab2022-09-08 08:46:06 -0600361 self._chroot = chroot or os.path.join(
362 build_path, constants.DEFAULT_CHROOT_DIR
363 )
364 self._gs_context = gs.GSContext(
365 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
366 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400367
Alex Klein1699fab2022-09-08 08:46:06 -0600368 def _Upload(self, local_file, remote_file):
369 """Wrapper around _GsUpload"""
370 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700371
Alex Klein1699fab2022-09-08 08:46:06 -0600372 def _ShouldFilterPackage(self, pkg):
373 if not self._packages:
374 return False
375 cpv = package_info.SplitCPV(pkg["CPV"])
376 self._found_packages.add(cpv.cp)
377 return (
378 cpv.package not in self._packages and cpv.cp not in self._packages
379 )
David James8c846492011-01-25 17:07:29 -0800380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 def _UploadPrebuilt(self, package_path, url_suffix):
382 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 Args:
385 package_path: The path to the packages dir.
386 url_suffix: The remote subdirectory where we should upload the packages.
387 """
388 # Process Packages file, removing duplicates and filtered packages.
389 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
390 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
391 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
392 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
393 unmatched_pkgs = self._packages - self._found_packages
394 if unmatched_pkgs:
395 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800396
Alex Klein1699fab2022-09-08 08:46:06 -0600397 # Write Packages file.
398 pkg_index.header["TTL"] = _BINPKG_TTL
399 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800400
Alex Klein1699fab2022-09-08 08:46:06 -0600401 remote_location = "%s/%s" % (
402 self._upload_location.rstrip("/"),
403 url_suffix,
404 )
405 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800406
Alex Klein1699fab2022-09-08 08:46:06 -0600407 upload_files = GenerateUploadDict(
408 package_path, remote_location, uploads
409 )
410 remote_file = "%s/Packages" % remote_location.rstrip("/")
411 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700412
Alex Klein1699fab2022-09-08 08:46:06 -0600413 # Build list of files to upload. Manually include the dev-only files but
414 # skip them if not present.
415 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
416 if os.path.exists(dev_only):
417 upload_files[dev_only] = "%s/%s" % (
418 remote_location.rstrip("/"),
419 os.path.basename(dev_only),
420 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400421
Alex Klein1699fab2022-09-08 08:46:06 -0600422 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800423
Alex Klein1699fab2022-09-08 08:46:06 -0600424 with tempfile.NamedTemporaryFile(
425 prefix="chromite.upload_prebuilts.index."
426 ) as index:
427 GenerateHtmlIndex(
428 [x[len(remote_location) + 1 :] for x in upload_files.values()],
429 index.name,
430 self._target,
431 self._version,
432 remote_location,
433 )
434 self._Upload(
435 index.name, "%s/index.html" % remote_location.rstrip("/")
436 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400437
Alex Klein1699fab2022-09-08 08:46:06 -0600438 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
439 url = "%s%s/index.html" % (
440 gs.PUBLIC_BASE_HTTPS_URL,
441 remote_location[len(gs.BASE_GS_URL) :],
442 )
443 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400444
Alex Klein1699fab2022-09-08 08:46:06 -0600445 def _UploadSdkTarball(
446 self,
447 board_path,
448 url_suffix,
449 prepackaged,
450 toolchains_overlay_tarballs,
451 toolchains_overlay_upload_path,
452 toolchain_tarballs,
453 toolchain_upload_path,
454 ):
455 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700456
Alex Klein1699fab2022-09-08 08:46:06 -0600457 Args:
458 board_path: The path to the board dir.
459 url_suffix: The remote subdirectory where we should upload the packages.
460 prepackaged: If given, a tarball that has been packaged outside of this
461 script and should be used.
462 toolchains_overlay_tarballs: List of toolchains overlay tarball
463 specifications to upload. Items take the form
464 "toolchains_spec:/path/to/tarball".
465 toolchains_overlay_upload_path: Path template under the bucket to place
466 toolchains overlay tarballs.
467 toolchain_tarballs: List of toolchain tarballs to upload.
468 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
469 """
470 remote_location = "%s/%s" % (
471 self._upload_location.rstrip("/"),
472 url_suffix,
473 )
474 assert remote_location.startswith("gs://")
475 boardname = os.path.basename(board_path.rstrip("/"))
476 # We do not upload non SDK board tarballs,
477 assert boardname == constants.CHROOT_BUILDER_BOARD
478 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200479
Alex Klein1699fab2022-09-08 08:46:06 -0600480 version_str = self._version[len("chroot-") :]
481 remote_tarfile = toolchain.GetSdkURL(
482 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
483 )
484 # For SDK, also upload the manifest which is guaranteed to exist
485 # by the builderstage.
486 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
487 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200488
Alex Klein1699fab2022-09-08 08:46:06 -0600489 # Upload SDK toolchains overlays and toolchain tarballs, if given.
490 for tarball_list, upload_path, qualifier_name in (
491 (
492 toolchains_overlay_tarballs,
493 toolchains_overlay_upload_path,
494 "toolchains",
495 ),
496 (toolchain_tarballs, toolchain_upload_path, "target"),
497 ):
498 for tarball_spec in tarball_list:
499 qualifier_val, local_path = tarball_spec.split(":")
500 suburl = upload_path % {qualifier_name: qualifier_val}
501 remote_path = toolchain.GetSdkURL(
502 for_gsutil=True, suburl=suburl
503 )
504 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500505
Alex Klein1699fab2022-09-08 08:46:06 -0600506 # Finally, also update the pointer to the latest SDK on which polling
507 # scripts rely.
Greg Edelston64620d12023-02-23 15:29:49 -0700508 self._UpdateRemoteSdkLatestFile(latest_sdk=version_str)
509
510 def _UpdateRemoteSdkLatestFile(
511 self,
512 latest_sdk: Optional[str] = None,
513 latest_sdk_uprev_target: Optional[str] = None,
514 ) -> None:
515 """Update the remote SDK pointer file on GS://.
516
517 The remote file contains multiple key-value pairs. This function can
518 update one or more of them; Nones will retain their existing values.
519
520 Args:
521 latest_sdk: The latest SDK that is tested and ready to be used. If
522 None, then the existing value on GS:// will be retained.
523 latest_sdk_uprev_target: The latest SDK that has been built, which
524 new PUprs can try to test and uprev. If None, then the existing
525 value on GS:// will be retained.
526 """
527 # Get existing values from the remote file.
528 remote_pointerfile = toolchain.GetSdkURL(
529 for_gsutil=True, suburl="cros-sdk-latest.conf"
530 )
531 existing_keyval = self._gs_context.LoadKeyValueStore(
532 remote_pointerfile, acl=self._acl
533 )
534 if not all(
535 key in existing_keyval
536 for key in ("LATEST_SDK", "LATEST_SDK_UPREV_TARGET")
537 ):
538 raise ValueError(
539 f"Remote pointerfile missing expected keys:\n{existing_keyval}"
Alex Klein1699fab2022-09-08 08:46:06 -0600540 )
Greg Edelston64620d12023-02-23 15:29:49 -0700541
542 # If any values were not specified in args, use the existing values.
543 if latest_sdk is None:
544 latest_sdk = existing_keyval["LATEST_SDK"]
545 if latest_sdk_uprev_target is None:
546 latest_sdk_uprev_target = existing_keyval["LATEST_SDK_UPREV_TARGET"]
547
548 # Write a new local latest file with target values, and upload.
549 new_file_contents = self._CreateRemoteSdkLatestFileContents(
550 latest_sdk, latest_sdk_uprev_target
551 )
552 with osutils.TempDir() as tmpdir:
553 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
554 osutils.WriteFile(local_pointerfile, new_file_contents)
555 self._Upload(local_pointerfile, remote_pointerfile)
556
557 @staticmethod
558 def _CreateRemoteSdkLatestFileContents(
559 latest_sdk: str, latest_sdk_uprev_target: str
560 ) -> str:
561 """Generate file contents for a remote SDK file.
562
563 Args:
564 latest_sdk: The latest SDK that is tested and ready to be used.
565 latest_sdk_uprev_target: The latest SDK that has been built, which
566 new PUprs can try to test and uprev.
567
568 Returns:
569 The contents of a remote SDK latest file containing the given args
570 as a key-value store.
571 """
572 return f"""\
573# The most recent SDK that is tested and ready for use.
574LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700575
576# The most recently built version. New uprev attempts should target this.
577# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700578LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200579
Alex Klein1699fab2022-09-08 08:46:06 -0600580 def _GetTargets(self):
581 """Retuns the list of targets to use."""
582 targets = self._slave_targets[:]
583 if self._target:
584 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700585
Alex Klein1699fab2022-09-08 08:46:06 -0600586 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700587
Alex Klein1699fab2022-09-08 08:46:06 -0600588 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
589 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800590
Alex Klein1699fab2022-09-08 08:46:06 -0600591 This function will sync both the standard host packages, plus the host
592 packages associated with all targets that have been "setup" with the
593 current host's chroot. For instance, if this host has been used to build
594 x86-generic, it will sync the host packages associated with
595 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
596 it will also sync the host packages associated with
597 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800598
Alex Klein1699fab2022-09-08 08:46:06 -0600599 Args:
600 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 host.
605 """
606 # Slave boards are listed before the master board so that the master board
607 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
608 # over preflight host prebuilts from other builders.)
609 binhost_urls = []
610 for target in self._GetTargets():
611 url_suffix = _REL_HOST_PATH % {
612 "version": self._version,
613 "host_arch": _HOST_ARCH,
614 "target": target,
615 }
616 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800617
Alex Klein1699fab2022-09-08 08:46:06 -0600618 if self._target == target and not self._skip_upload:
619 # Upload prebuilts.
620 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
621 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700622
Alex Klein1699fab2022-09-08 08:46:06 -0600623 # Record URL where prebuilts were uploaded.
624 binhost_urls.append(
625 "%s/%s/"
626 % (
627 self._binhost_base_url.rstrip("/"),
628 packages_url_suffix.rstrip("/"),
629 )
630 )
David Jamese2488642011-11-14 16:15:20 -0800631
Alex Klein1699fab2022-09-08 08:46:06 -0600632 binhost = " ".join(binhost_urls)
633 if git_sync:
634 git_file = os.path.join(
635 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
636 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700637 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700638 git_file, {key: binhost}, self._report, dryrun=self._dryrun
639 )
Alex Klein1699fab2022-09-08 08:46:06 -0600640 if sync_binhost_conf:
641 binhost_conf = os.path.join(
642 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
643 )
644 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800645
Alex Klein1699fab2022-09-08 08:46:06 -0600646 def SyncBoardPrebuilts(
647 self,
648 key,
649 git_sync,
650 sync_binhost_conf,
651 upload_board_tarball,
652 prepackaged_board,
653 toolchains_overlay_tarballs,
654 toolchains_overlay_upload_path,
655 toolchain_tarballs,
656 toolchain_upload_path,
657 ):
658 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800659
Alex Klein1699fab2022-09-08 08:46:06 -0600660 Args:
661 key: The variable key to update in the git file.
662 git_sync: If set, update make.conf of target to reference the latest
663 prebuilt packages generated here.
664 sync_binhost_conf: If set, update binhost config file in
665 chromiumos-overlay for the current board.
666 upload_board_tarball: Include a tarball of the board in our upload.
667 prepackaged_board: A tarball of the board built outside of this script.
668 toolchains_overlay_tarballs: List of toolchains overlay tarball
669 specifications to upload. Items take the form
670 "toolchains_spec:/path/to/tarball".
671 toolchains_overlay_upload_path: Path template under the bucket to place
672 toolchains overlay tarballs.
673 toolchain_tarballs: A list of toolchain tarballs to upload.
674 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
675 """
676 updated_binhosts = set()
677 for target in self._GetTargets():
678 board_path = os.path.join(
679 self._build_path, _BOARD_PATH % {"board": target.board_variant}
680 )
681 package_path = os.path.join(board_path, "packages")
682 url_suffix = _REL_BOARD_PATH % {
683 "target": target,
684 "version": self._version,
685 }
686 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700687
Alex Klein1699fab2022-09-08 08:46:06 -0600688 # Process the target board differently if it is the main --board.
689 if self._target == target and not self._skip_upload:
690 # This strips "chroot" prefix because that is sometimes added as the
691 # --prepend-version argument (e.g. by chromiumos-sdk bot).
692 # TODO(build): Clean it up to be less hard-coded.
693 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500694
Alex Klein1699fab2022-09-08 08:46:06 -0600695 # Upload board tarballs in the background.
696 if upload_board_tarball:
697 if toolchain_upload_path:
698 toolchain_upload_path %= {"version": version_str}
699 if toolchains_overlay_upload_path:
700 toolchains_overlay_upload_path %= {
701 "version": version_str
702 }
703 tar_process = multiprocessing.Process(
704 target=self._UploadSdkTarball,
705 args=(
706 board_path,
707 url_suffix,
708 prepackaged_board,
709 toolchains_overlay_tarballs,
710 toolchains_overlay_upload_path,
711 toolchain_tarballs,
712 toolchain_upload_path,
713 ),
714 )
715 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700716
Alex Klein1699fab2022-09-08 08:46:06 -0600717 # Upload prebuilts.
718 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700719
Alex Klein1699fab2022-09-08 08:46:06 -0600720 # Make sure we finished uploading the board tarballs.
721 if upload_board_tarball:
722 tar_process.join()
723 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800724
Alex Klein1699fab2022-09-08 08:46:06 -0600725 # Record URL where prebuilts were uploaded.
726 url_value = "%s/%s/" % (
727 self._binhost_base_url.rstrip("/"),
728 packages_url_suffix.rstrip("/"),
729 )
David Jamese2488642011-11-14 16:15:20 -0800730
Alex Klein1699fab2022-09-08 08:46:06 -0600731 if git_sync:
732 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700733 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700734 git_file,
735 {key: url_value},
736 self._report,
737 dryrun=self._dryrun,
738 )
Matt Tennante8179042013-10-01 15:47:32 -0700739
Alex Klein1699fab2022-09-08 08:46:06 -0600740 if sync_binhost_conf:
741 # Update the binhost configuration file in git.
742 binhost_conf = os.path.join(
743 self._binhost_conf_dir,
744 "target",
745 "%s-%s.conf" % (target, key),
746 )
747 updated_binhosts.add(binhost_conf)
748 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800749
Alex Klein1699fab2022-09-08 08:46:06 -0600750 if sync_binhost_conf:
751 # Clear all old binhosts. The files must be left empty in case anybody
752 # is referring to them.
753 all_binhosts = set(
754 glob.glob(
755 os.path.join(
756 self._binhost_conf_dir, "target", "*-%s.conf" % key
757 )
758 )
759 )
760 for binhost_conf in all_binhosts - updated_binhosts:
761 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800762
David James05bcb2b2011-02-09 09:25:47 -0800763
Mike Nicholsa1414162021-04-22 20:07:22 +0000764class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600765 """Callback that adds a slave board to the list of slave targets."""
766
767 def __call__(self, parser, namespace, values, option_string=None):
768 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800769
770
Mike Nicholsa1414162021-04-22 20:07:22 +0000771class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600772 """Callback that adds a slave profile to the list of slave targets."""
773
774 def __call__(self, parser, namespace, values, option_string=None):
775 if not namespace.slave_targets:
776 parser.error("Must specify --slave-board before --slave-profile")
777 if namespace.slave_targets[-1].profile is not None:
778 parser.error("Cannot specify --slave-profile twice for same board")
779 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800780
781
Mike Frysinger86509232014-05-24 13:18:37 -0400782def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600783 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700784
Alex Klein1699fab2022-09-08 08:46:06 -0600785 Args:
786 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400787
Alex Klein1699fab2022-09-08 08:46:06 -0600788 Returns:
789 A tuple containing a parsed options object and BuildTarget.
790 The target instance is None if no board is specified.
791 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500792 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600793 parser.add_argument(
794 "-H",
795 "--binhost-base-url",
796 default=_BINHOST_BASE_URL,
797 help="Base URL to use for binhost in make.conf updates",
798 )
799 parser.add_argument(
800 "--previous-binhost-url",
801 action="append",
802 default=[],
803 help="Previous binhost URL",
804 )
805 parser.add_argument(
806 "-b", "--board", help="Board type that was built on this machine"
807 )
808 parser.add_argument(
809 "-B",
810 "--prepackaged-tarball",
811 type="path",
812 help="Board tarball prebuilt outside of this script.",
813 )
814 parser.add_argument(
815 "--toolchains-overlay-tarball",
816 dest="toolchains_overlay_tarballs",
817 action="append",
818 default=[],
819 help="Toolchains overlay tarball specification to "
820 "upload. Takes the form "
821 '"toolchains_spec:/path/to/tarball".',
822 )
823 parser.add_argument(
824 "--toolchains-overlay-upload-path",
825 default="",
826 help="Path template for uploading toolchains overlays.",
827 )
828 parser.add_argument(
829 "--toolchain-tarball",
830 dest="toolchain_tarballs",
831 action="append",
832 default=[],
833 help="Redistributable toolchain tarball.",
834 )
835 parser.add_argument(
836 "--toolchain-upload-path",
837 default="",
838 help="Path to place toolchain tarballs in the sdk tree.",
839 )
840 parser.add_argument(
841 "--profile", help="Profile that was built on this machine"
842 )
843 parser.add_argument(
844 "--slave-board",
845 default=[],
846 action=_AddSlaveBoardAction,
847 dest="slave_targets",
848 help="Board type that was built on a slave machine. To "
849 "add a profile to this board, use --slave-profile.",
850 )
851 parser.add_argument(
852 "--slave-profile",
853 action=_AddSlaveProfileAction,
854 help="Board profile that was built on a slave machine. "
855 "Applies to previous slave board.",
856 )
857 parser.add_argument(
858 "-p",
859 "--build-path",
860 required=True,
861 help="Path to the directory containing the chroot",
862 )
863 parser.add_argument(
864 "--chroot",
865 help="Path where the chroot is located. "
866 "(Default: {build_path}/chroot)",
867 )
868 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700869 "--output",
870 type=Path,
871 help="Write a JSON report to the specified file. "
872 "(Default is not to write the report.)",
873 )
874 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600875 "--packages",
876 action="append",
877 default=[],
878 help="Only include the specified packages. "
879 "(Default is to include all packages.)",
880 )
881 parser.add_argument(
882 "-s",
883 "--sync-host",
884 default=False,
885 action="store_true",
886 help="Sync host prebuilts",
887 )
888 parser.add_argument(
889 "-g",
890 "--git-sync",
891 default=False,
892 action="store_true",
893 help="Enable git version sync (This commits to a repo.) "
894 "This is used by full builders to commit directly "
895 "to board overlays.",
896 )
897 parser.add_argument("-u", "--upload", help="Upload location")
898 parser.add_argument(
899 "-V",
900 "--prepend-version",
901 help="Add an identifier to the front of the version",
902 )
903 parser.add_argument(
904 "-f",
905 "--filters",
906 action="store_true",
907 default=False,
908 help="Turn on filtering of private ebuild packages",
909 )
910 parser.add_argument(
911 "-k",
912 "--key",
913 default="PORTAGE_BINHOST",
914 help="Key to update in make.conf / binhost.conf",
915 )
916 parser.add_argument("--set-version", help="Specify the version string")
917 parser.add_argument(
918 "--sync-binhost-conf",
919 default=False,
920 action="store_true",
921 help="Update binhost.conf in chromiumos-overlay or "
922 "chromeos-overlay. Commit the changes, but don't "
923 "push them. This is used for preflight binhosts.",
924 )
925 parser.add_argument(
926 "--binhost-conf-dir",
927 help="Directory to commit binhost config with " "--sync-binhost-conf.",
928 )
929 parser.add_argument(
930 "-P",
931 "--private",
932 action="store_true",
933 default=False,
934 help="Mark gs:// uploads as private.",
935 )
936 parser.add_argument(
937 "--skip-upload",
938 action="store_true",
939 default=False,
940 help="Skip upload step.",
941 )
942 parser.add_argument(
943 "--upload-board-tarball",
944 action="store_true",
945 default=False,
946 help="Upload board tarball to Google Storage.",
947 )
David James8c846492011-01-25 17:07:29 -0800948
Alex Klein1699fab2022-09-08 08:46:06 -0600949 options = parser.parse_args(argv)
950 if not options.upload and not options.skip_upload:
951 parser.error("you need to provide an upload location using -u")
952 if not options.set_version and options.skip_upload:
953 parser.error(
954 "If you are using --skip-upload, you must specify a "
955 "version number using --set-version."
956 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700957
Alex Klein1699fab2022-09-08 08:46:06 -0600958 target = None
959 if options.board:
960 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700961
Alex Klein1699fab2022-09-08 08:46:06 -0600962 if target in options.slave_targets:
963 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -0800964
Alex Klein1699fab2022-09-08 08:46:06 -0600965 if len(set(options.slave_targets)) != len(options.slave_targets):
966 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -0800967
Alex Klein1699fab2022-09-08 08:46:06 -0600968 if options.slave_targets and options.git_sync:
969 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -0800970
Alex Klein1699fab2022-09-08 08:46:06 -0600971 if (
972 options.upload_board_tarball
973 and options.skip_upload
974 and options.board == "amd64-host"
975 ):
976 parser.error(
977 "--skip-upload is not compatible with "
978 "--upload-board-tarball and --board=amd64-host"
979 )
David James8fa34ea2011-04-15 13:00:20 -0700980
Alex Klein1699fab2022-09-08 08:46:06 -0600981 if (
982 options.upload_board_tarball
983 and not options.skip_upload
984 and not options.upload.startswith("gs://")
985 ):
986 parser.error(
987 "--upload-board-tarball only works with gs:// URLs.\n"
988 "--upload must be a gs:// URL."
989 )
David James8fa34ea2011-04-15 13:00:20 -0700990
Alex Klein1699fab2022-09-08 08:46:06 -0600991 if options.upload_board_tarball and options.prepackaged_tarball is None:
992 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +0200993
Alex Klein1699fab2022-09-08 08:46:06 -0600994 if options.private:
995 if options.sync_host:
996 parser.error(
997 "--private and --sync-host/-s cannot be specified "
998 "together; we do not support private host prebuilts"
999 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001000
Alex Klein1699fab2022-09-08 08:46:06 -06001001 if not options.upload or not options.upload.startswith("gs://"):
1002 parser.error(
1003 "--private is only valid for gs:// URLs; "
1004 "--upload must be a gs:// URL."
1005 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001006
Alex Klein1699fab2022-09-08 08:46:06 -06001007 if options.binhost_base_url != _BINHOST_BASE_URL:
1008 parser.error(
1009 "when using --private the --binhost-base-url "
1010 "is automatically derived."
1011 )
David James27fa7d12011-06-29 17:24:14 -07001012
Alex Klein1699fab2022-09-08 08:46:06 -06001013 if options.sync_binhost_conf and not options.binhost_conf_dir:
1014 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001015
Alex Klein1699fab2022-09-08 08:46:06 -06001016 if (
1017 options.toolchains_overlay_tarballs
1018 and not options.toolchains_overlay_upload_path
1019 ):
1020 parser.error(
1021 "--toolchains-overlay-tarball requires "
1022 "--toolchains-overlay-upload-path"
1023 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001024
Alex Klein1699fab2022-09-08 08:46:06 -06001025 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001026
Mike Frysingercc838832014-05-24 13:10:30 -04001027
Mike Frysinger86509232014-05-24 13:18:37 -04001028def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001029 # We accumulate information about actions taken and report it at the end
1030 # if asked to do so. Currently, this only records CL creation, which
1031 # is the only thing we need for now.
1032 report = {}
1033
Alex Klein1699fab2022-09-08 08:46:06 -06001034 # Set umask so that files created as root are readable.
1035 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001038
Alex Klein1699fab2022-09-08 08:46:06 -06001039 # Calculate a list of Packages index files to compare against. Whenever we
1040 # upload a package, we check to make sure it's not already stored in one of
1041 # the packages files we uploaded. This list of packages files might contain
1042 # both board and host packages.
1043 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001044
Alex Klein1699fab2022-09-08 08:46:06 -06001045 if options.set_version:
1046 version = options.set_version
1047 else:
1048 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001049
Alex Klein1699fab2022-09-08 08:46:06 -06001050 if options.prepend_version:
1051 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001052
Alex Klein1699fab2022-09-08 08:46:06 -06001053 acl = "public-read"
1054 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001055
Alex Klein1699fab2022-09-08 08:46:06 -06001056 if options.private:
1057 binhost_base_url = options.upload
1058 if target:
1059 acl = portage_util.FindOverlayFile(
1060 _GOOGLESTORAGE_GSUTIL_FILE,
1061 board=target.board_variant,
1062 buildroot=options.build_path,
1063 )
1064 if acl is None:
1065 cros_build_lib.Die(
1066 "No Google Storage ACL file %s found in %s overlay.",
1067 _GOOGLESTORAGE_GSUTIL_FILE,
1068 target.board_variant,
1069 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001070
Alex Klein1699fab2022-09-08 08:46:06 -06001071 binhost_conf_dir = None
1072 if options.binhost_conf_dir:
1073 binhost_conf_dir = os.path.join(
1074 options.build_path, options.binhost_conf_dir
1075 )
David Jamesb26b9312014-12-15 11:26:46 -08001076
Alex Klein1699fab2022-09-08 08:46:06 -06001077 uploader = PrebuiltUploader(
1078 options.upload,
1079 acl,
1080 binhost_base_url,
1081 pkg_indexes,
1082 options.build_path,
1083 options.packages,
1084 options.skip_upload,
1085 binhost_conf_dir,
1086 options.dryrun,
1087 target,
1088 options.slave_targets,
1089 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001090 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001091 options.chroot,
1092 )
David Jamesc0f158a2011-02-22 16:07:29 -08001093
Alex Klein1699fab2022-09-08 08:46:06 -06001094 if options.sync_host:
1095 uploader.SyncHostPrebuilts(
1096 options.key, options.git_sync, options.sync_binhost_conf
1097 )
David James8c846492011-01-25 17:07:29 -08001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 if options.board or options.slave_targets:
1100 uploader.SyncBoardPrebuilts(
1101 options.key,
1102 options.git_sync,
1103 options.sync_binhost_conf,
1104 options.upload_board_tarball,
1105 options.prepackaged_tarball,
1106 options.toolchains_overlay_tarballs,
1107 options.toolchains_overlay_upload_path,
1108 options.toolchain_tarballs,
1109 options.toolchain_upload_path,
1110 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001111
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001112 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001113 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001114 pformat.json(report, fp=f)