blob: b4f8f7471952675950dd8b76e0dba1c7077758c5 [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 )
Greg Edelston2469ad32023-03-20 13:55:21 -0600534
535 # TODO(b/274196697): When LATEST_SDK_UPREV_TARGET is more reliably found
536 # in the remote latest file, require that key too.
537 for required_key in ("LATEST_SDK",):
538 if required_key not in existing_keyval:
539 raise ValueError(
540 f"Remote pointer file {remote_pointerfile} missing expected key {required_key}:\n{existing_keyval}"
541 )
Greg Edelston64620d12023-02-23 15:29:49 -0700542
543 # If any values were not specified in args, use the existing values.
544 if latest_sdk is None:
545 latest_sdk = existing_keyval["LATEST_SDK"]
546 if latest_sdk_uprev_target is None:
Greg Edelston2469ad32023-03-20 13:55:21 -0600547 latest_sdk_uprev_target = existing_keyval.get(
548 "LATEST_SDK_UPREV_TARGET", None
549 )
Greg Edelston64620d12023-02-23 15:29:49 -0700550
551 # Write a new local latest file with target values, and upload.
552 new_file_contents = self._CreateRemoteSdkLatestFileContents(
553 latest_sdk, latest_sdk_uprev_target
554 )
555 with osutils.TempDir() as tmpdir:
556 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
557 osutils.WriteFile(local_pointerfile, new_file_contents)
558 self._Upload(local_pointerfile, remote_pointerfile)
559
560 @staticmethod
561 def _CreateRemoteSdkLatestFileContents(
562 latest_sdk: str, latest_sdk_uprev_target: str
563 ) -> str:
564 """Generate file contents for a remote SDK file.
565
566 Args:
567 latest_sdk: The latest SDK that is tested and ready to be used.
568 latest_sdk_uprev_target: The latest SDK that has been built, which
569 new PUprs can try to test and uprev.
570
571 Returns:
572 The contents of a remote SDK latest file containing the given args
573 as a key-value store.
574 """
575 return f"""\
576# The most recent SDK that is tested and ready for use.
577LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700578
579# The most recently built version. New uprev attempts should target this.
580# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700581LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200582
Alex Klein1699fab2022-09-08 08:46:06 -0600583 def _GetTargets(self):
584 """Retuns the list of targets to use."""
585 targets = self._slave_targets[:]
586 if self._target:
587 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700588
Alex Klein1699fab2022-09-08 08:46:06 -0600589 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700590
Alex Klein1699fab2022-09-08 08:46:06 -0600591 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
592 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800593
Alex Klein1699fab2022-09-08 08:46:06 -0600594 This function will sync both the standard host packages, plus the host
595 packages associated with all targets that have been "setup" with the
596 current host's chroot. For instance, if this host has been used to build
597 x86-generic, it will sync the host packages associated with
598 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
599 it will also sync the host packages associated with
600 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800601
Alex Klein1699fab2022-09-08 08:46:06 -0600602 Args:
603 key: The variable key to update in the git file.
604 git_sync: If set, update make.conf of target to reference the latest
605 prebuilt packages generated here.
606 sync_binhost_conf: If set, update binhost config file in
607 chromiumos-overlay for the host.
608 """
609 # Slave boards are listed before the master board so that the master board
610 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
611 # over preflight host prebuilts from other builders.)
612 binhost_urls = []
613 for target in self._GetTargets():
614 url_suffix = _REL_HOST_PATH % {
615 "version": self._version,
616 "host_arch": _HOST_ARCH,
617 "target": target,
618 }
619 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800620
Alex Klein1699fab2022-09-08 08:46:06 -0600621 if self._target == target and not self._skip_upload:
622 # Upload prebuilts.
623 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
624 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700625
Alex Klein1699fab2022-09-08 08:46:06 -0600626 # Record URL where prebuilts were uploaded.
627 binhost_urls.append(
628 "%s/%s/"
629 % (
630 self._binhost_base_url.rstrip("/"),
631 packages_url_suffix.rstrip("/"),
632 )
633 )
David Jamese2488642011-11-14 16:15:20 -0800634
Alex Klein1699fab2022-09-08 08:46:06 -0600635 binhost = " ".join(binhost_urls)
636 if git_sync:
637 git_file = os.path.join(
638 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
639 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700640 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700641 git_file, {key: binhost}, self._report, dryrun=self._dryrun
642 )
Alex Klein1699fab2022-09-08 08:46:06 -0600643 if sync_binhost_conf:
644 binhost_conf = os.path.join(
645 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
646 )
647 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800648
Alex Klein1699fab2022-09-08 08:46:06 -0600649 def SyncBoardPrebuilts(
650 self,
651 key,
652 git_sync,
653 sync_binhost_conf,
654 upload_board_tarball,
655 prepackaged_board,
656 toolchains_overlay_tarballs,
657 toolchains_overlay_upload_path,
658 toolchain_tarballs,
659 toolchain_upload_path,
660 ):
661 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800662
Alex Klein1699fab2022-09-08 08:46:06 -0600663 Args:
664 key: The variable key to update in the git file.
665 git_sync: If set, update make.conf of target to reference the latest
666 prebuilt packages generated here.
667 sync_binhost_conf: If set, update binhost config file in
668 chromiumos-overlay for the current board.
669 upload_board_tarball: Include a tarball of the board in our upload.
670 prepackaged_board: A tarball of the board built outside of this script.
671 toolchains_overlay_tarballs: List of toolchains overlay tarball
672 specifications to upload. Items take the form
673 "toolchains_spec:/path/to/tarball".
674 toolchains_overlay_upload_path: Path template under the bucket to place
675 toolchains overlay tarballs.
676 toolchain_tarballs: A list of toolchain tarballs to upload.
677 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
678 """
679 updated_binhosts = set()
680 for target in self._GetTargets():
681 board_path = os.path.join(
682 self._build_path, _BOARD_PATH % {"board": target.board_variant}
683 )
684 package_path = os.path.join(board_path, "packages")
685 url_suffix = _REL_BOARD_PATH % {
686 "target": target,
687 "version": self._version,
688 }
689 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700690
Alex Klein1699fab2022-09-08 08:46:06 -0600691 # Process the target board differently if it is the main --board.
692 if self._target == target and not self._skip_upload:
693 # This strips "chroot" prefix because that is sometimes added as the
694 # --prepend-version argument (e.g. by chromiumos-sdk bot).
695 # TODO(build): Clean it up to be less hard-coded.
696 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500697
Alex Klein1699fab2022-09-08 08:46:06 -0600698 # Upload board tarballs in the background.
699 if upload_board_tarball:
700 if toolchain_upload_path:
701 toolchain_upload_path %= {"version": version_str}
702 if toolchains_overlay_upload_path:
703 toolchains_overlay_upload_path %= {
704 "version": version_str
705 }
706 tar_process = multiprocessing.Process(
707 target=self._UploadSdkTarball,
708 args=(
709 board_path,
710 url_suffix,
711 prepackaged_board,
712 toolchains_overlay_tarballs,
713 toolchains_overlay_upload_path,
714 toolchain_tarballs,
715 toolchain_upload_path,
716 ),
717 )
718 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700719
Alex Klein1699fab2022-09-08 08:46:06 -0600720 # Upload prebuilts.
721 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700722
Alex Klein1699fab2022-09-08 08:46:06 -0600723 # Make sure we finished uploading the board tarballs.
724 if upload_board_tarball:
725 tar_process.join()
726 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 # Record URL where prebuilts were uploaded.
729 url_value = "%s/%s/" % (
730 self._binhost_base_url.rstrip("/"),
731 packages_url_suffix.rstrip("/"),
732 )
David Jamese2488642011-11-14 16:15:20 -0800733
Alex Klein1699fab2022-09-08 08:46:06 -0600734 if git_sync:
735 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700736 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700737 git_file,
738 {key: url_value},
739 self._report,
740 dryrun=self._dryrun,
741 )
Matt Tennante8179042013-10-01 15:47:32 -0700742
Alex Klein1699fab2022-09-08 08:46:06 -0600743 if sync_binhost_conf:
744 # Update the binhost configuration file in git.
745 binhost_conf = os.path.join(
746 self._binhost_conf_dir,
747 "target",
748 "%s-%s.conf" % (target, key),
749 )
750 updated_binhosts.add(binhost_conf)
751 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800752
Alex Klein1699fab2022-09-08 08:46:06 -0600753 if sync_binhost_conf:
754 # Clear all old binhosts. The files must be left empty in case anybody
755 # is referring to them.
756 all_binhosts = set(
757 glob.glob(
758 os.path.join(
759 self._binhost_conf_dir, "target", "*-%s.conf" % key
760 )
761 )
762 )
763 for binhost_conf in all_binhosts - updated_binhosts:
764 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800765
David James05bcb2b2011-02-09 09:25:47 -0800766
Mike Nicholsa1414162021-04-22 20:07:22 +0000767class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600768 """Callback that adds a slave board to the list of slave targets."""
769
770 def __call__(self, parser, namespace, values, option_string=None):
771 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800772
773
Mike Nicholsa1414162021-04-22 20:07:22 +0000774class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600775 """Callback that adds a slave profile to the list of slave targets."""
776
777 def __call__(self, parser, namespace, values, option_string=None):
778 if not namespace.slave_targets:
779 parser.error("Must specify --slave-board before --slave-profile")
780 if namespace.slave_targets[-1].profile is not None:
781 parser.error("Cannot specify --slave-profile twice for same board")
782 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800783
784
Mike Frysinger86509232014-05-24 13:18:37 -0400785def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600786 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700787
Alex Klein1699fab2022-09-08 08:46:06 -0600788 Args:
789 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400790
Alex Klein1699fab2022-09-08 08:46:06 -0600791 Returns:
792 A tuple containing a parsed options object and BuildTarget.
793 The target instance is None if no board is specified.
794 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500795 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600796 parser.add_argument(
797 "-H",
798 "--binhost-base-url",
799 default=_BINHOST_BASE_URL,
800 help="Base URL to use for binhost in make.conf updates",
801 )
802 parser.add_argument(
803 "--previous-binhost-url",
804 action="append",
805 default=[],
806 help="Previous binhost URL",
807 )
808 parser.add_argument(
809 "-b", "--board", help="Board type that was built on this machine"
810 )
811 parser.add_argument(
812 "-B",
813 "--prepackaged-tarball",
814 type="path",
815 help="Board tarball prebuilt outside of this script.",
816 )
817 parser.add_argument(
818 "--toolchains-overlay-tarball",
819 dest="toolchains_overlay_tarballs",
820 action="append",
821 default=[],
822 help="Toolchains overlay tarball specification to "
823 "upload. Takes the form "
824 '"toolchains_spec:/path/to/tarball".',
825 )
826 parser.add_argument(
827 "--toolchains-overlay-upload-path",
828 default="",
829 help="Path template for uploading toolchains overlays.",
830 )
831 parser.add_argument(
832 "--toolchain-tarball",
833 dest="toolchain_tarballs",
834 action="append",
835 default=[],
836 help="Redistributable toolchain tarball.",
837 )
838 parser.add_argument(
839 "--toolchain-upload-path",
840 default="",
841 help="Path to place toolchain tarballs in the sdk tree.",
842 )
843 parser.add_argument(
844 "--profile", help="Profile that was built on this machine"
845 )
846 parser.add_argument(
847 "--slave-board",
848 default=[],
849 action=_AddSlaveBoardAction,
850 dest="slave_targets",
851 help="Board type that was built on a slave machine. To "
852 "add a profile to this board, use --slave-profile.",
853 )
854 parser.add_argument(
855 "--slave-profile",
856 action=_AddSlaveProfileAction,
857 help="Board profile that was built on a slave machine. "
858 "Applies to previous slave board.",
859 )
860 parser.add_argument(
861 "-p",
862 "--build-path",
863 required=True,
864 help="Path to the directory containing the chroot",
865 )
866 parser.add_argument(
867 "--chroot",
868 help="Path where the chroot is located. "
869 "(Default: {build_path}/chroot)",
870 )
871 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700872 "--output",
873 type=Path,
874 help="Write a JSON report to the specified file. "
875 "(Default is not to write the report.)",
876 )
877 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600878 "--packages",
879 action="append",
880 default=[],
881 help="Only include the specified packages. "
882 "(Default is to include all packages.)",
883 )
884 parser.add_argument(
885 "-s",
886 "--sync-host",
887 default=False,
888 action="store_true",
889 help="Sync host prebuilts",
890 )
891 parser.add_argument(
892 "-g",
893 "--git-sync",
894 default=False,
895 action="store_true",
896 help="Enable git version sync (This commits to a repo.) "
897 "This is used by full builders to commit directly "
898 "to board overlays.",
899 )
900 parser.add_argument("-u", "--upload", help="Upload location")
901 parser.add_argument(
902 "-V",
903 "--prepend-version",
904 help="Add an identifier to the front of the version",
905 )
906 parser.add_argument(
907 "-f",
908 "--filters",
909 action="store_true",
910 default=False,
911 help="Turn on filtering of private ebuild packages",
912 )
913 parser.add_argument(
914 "-k",
915 "--key",
916 default="PORTAGE_BINHOST",
917 help="Key to update in make.conf / binhost.conf",
918 )
919 parser.add_argument("--set-version", help="Specify the version string")
920 parser.add_argument(
921 "--sync-binhost-conf",
922 default=False,
923 action="store_true",
924 help="Update binhost.conf in chromiumos-overlay or "
925 "chromeos-overlay. Commit the changes, but don't "
926 "push them. This is used for preflight binhosts.",
927 )
928 parser.add_argument(
929 "--binhost-conf-dir",
930 help="Directory to commit binhost config with " "--sync-binhost-conf.",
931 )
932 parser.add_argument(
933 "-P",
934 "--private",
935 action="store_true",
936 default=False,
937 help="Mark gs:// uploads as private.",
938 )
939 parser.add_argument(
940 "--skip-upload",
941 action="store_true",
942 default=False,
943 help="Skip upload step.",
944 )
945 parser.add_argument(
946 "--upload-board-tarball",
947 action="store_true",
948 default=False,
949 help="Upload board tarball to Google Storage.",
950 )
David James8c846492011-01-25 17:07:29 -0800951
Alex Klein1699fab2022-09-08 08:46:06 -0600952 options = parser.parse_args(argv)
953 if not options.upload and not options.skip_upload:
954 parser.error("you need to provide an upload location using -u")
955 if not options.set_version and options.skip_upload:
956 parser.error(
957 "If you are using --skip-upload, you must specify a "
958 "version number using --set-version."
959 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700960
Alex Klein1699fab2022-09-08 08:46:06 -0600961 target = None
962 if options.board:
963 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700964
Alex Klein1699fab2022-09-08 08:46:06 -0600965 if target in options.slave_targets:
966 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -0800967
Alex Klein1699fab2022-09-08 08:46:06 -0600968 if len(set(options.slave_targets)) != len(options.slave_targets):
969 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -0800970
Alex Klein1699fab2022-09-08 08:46:06 -0600971 if options.slave_targets and options.git_sync:
972 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -0800973
Alex Klein1699fab2022-09-08 08:46:06 -0600974 if (
975 options.upload_board_tarball
976 and options.skip_upload
977 and options.board == "amd64-host"
978 ):
979 parser.error(
980 "--skip-upload is not compatible with "
981 "--upload-board-tarball and --board=amd64-host"
982 )
David James8fa34ea2011-04-15 13:00:20 -0700983
Alex Klein1699fab2022-09-08 08:46:06 -0600984 if (
985 options.upload_board_tarball
986 and not options.skip_upload
987 and not options.upload.startswith("gs://")
988 ):
989 parser.error(
990 "--upload-board-tarball only works with gs:// URLs.\n"
991 "--upload must be a gs:// URL."
992 )
David James8fa34ea2011-04-15 13:00:20 -0700993
Alex Klein1699fab2022-09-08 08:46:06 -0600994 if options.upload_board_tarball and options.prepackaged_tarball is None:
995 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +0200996
Alex Klein1699fab2022-09-08 08:46:06 -0600997 if options.private:
998 if options.sync_host:
999 parser.error(
1000 "--private and --sync-host/-s cannot be specified "
1001 "together; we do not support private host prebuilts"
1002 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001003
Alex Klein1699fab2022-09-08 08:46:06 -06001004 if not options.upload or not options.upload.startswith("gs://"):
1005 parser.error(
1006 "--private is only valid for gs:// URLs; "
1007 "--upload must be a gs:// URL."
1008 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001009
Alex Klein1699fab2022-09-08 08:46:06 -06001010 if options.binhost_base_url != _BINHOST_BASE_URL:
1011 parser.error(
1012 "when using --private the --binhost-base-url "
1013 "is automatically derived."
1014 )
David James27fa7d12011-06-29 17:24:14 -07001015
Alex Klein1699fab2022-09-08 08:46:06 -06001016 if options.sync_binhost_conf and not options.binhost_conf_dir:
1017 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001018
Alex Klein1699fab2022-09-08 08:46:06 -06001019 if (
1020 options.toolchains_overlay_tarballs
1021 and not options.toolchains_overlay_upload_path
1022 ):
1023 parser.error(
1024 "--toolchains-overlay-tarball requires "
1025 "--toolchains-overlay-upload-path"
1026 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001027
Alex Klein1699fab2022-09-08 08:46:06 -06001028 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001029
Mike Frysingercc838832014-05-24 13:10:30 -04001030
Mike Frysinger86509232014-05-24 13:18:37 -04001031def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001032 # We accumulate information about actions taken and report it at the end
1033 # if asked to do so. Currently, this only records CL creation, which
1034 # is the only thing we need for now.
1035 report = {}
1036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 # Set umask so that files created as root are readable.
1038 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001039
Alex Klein1699fab2022-09-08 08:46:06 -06001040 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001041
Alex Klein1699fab2022-09-08 08:46:06 -06001042 # Calculate a list of Packages index files to compare against. Whenever we
1043 # upload a package, we check to make sure it's not already stored in one of
1044 # the packages files we uploaded. This list of packages files might contain
1045 # both board and host packages.
1046 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001047
Alex Klein1699fab2022-09-08 08:46:06 -06001048 if options.set_version:
1049 version = options.set_version
1050 else:
1051 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001052
Alex Klein1699fab2022-09-08 08:46:06 -06001053 if options.prepend_version:
1054 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001055
Alex Klein1699fab2022-09-08 08:46:06 -06001056 acl = "public-read"
1057 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001058
Alex Klein1699fab2022-09-08 08:46:06 -06001059 if options.private:
1060 binhost_base_url = options.upload
1061 if target:
1062 acl = portage_util.FindOverlayFile(
1063 _GOOGLESTORAGE_GSUTIL_FILE,
1064 board=target.board_variant,
1065 buildroot=options.build_path,
1066 )
1067 if acl is None:
1068 cros_build_lib.Die(
1069 "No Google Storage ACL file %s found in %s overlay.",
1070 _GOOGLESTORAGE_GSUTIL_FILE,
1071 target.board_variant,
1072 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001073
Alex Klein1699fab2022-09-08 08:46:06 -06001074 binhost_conf_dir = None
1075 if options.binhost_conf_dir:
1076 binhost_conf_dir = os.path.join(
1077 options.build_path, options.binhost_conf_dir
1078 )
David Jamesb26b9312014-12-15 11:26:46 -08001079
Alex Klein1699fab2022-09-08 08:46:06 -06001080 uploader = PrebuiltUploader(
1081 options.upload,
1082 acl,
1083 binhost_base_url,
1084 pkg_indexes,
1085 options.build_path,
1086 options.packages,
1087 options.skip_upload,
1088 binhost_conf_dir,
1089 options.dryrun,
1090 target,
1091 options.slave_targets,
1092 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001093 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001094 options.chroot,
1095 )
David Jamesc0f158a2011-02-22 16:07:29 -08001096
Alex Klein1699fab2022-09-08 08:46:06 -06001097 if options.sync_host:
1098 uploader.SyncHostPrebuilts(
1099 options.key, options.git_sync, options.sync_binhost_conf
1100 )
David James8c846492011-01-25 17:07:29 -08001101
Alex Klein1699fab2022-09-08 08:46:06 -06001102 if options.board or options.slave_targets:
1103 uploader.SyncBoardPrebuilts(
1104 options.key,
1105 options.git_sync,
1106 options.sync_binhost_conf,
1107 options.upload_board_tarball,
1108 options.prepackaged_tarball,
1109 options.toolchains_overlay_tarballs,
1110 options.toolchains_overlay_upload_path,
1111 options.toolchain_tarballs,
1112 options.toolchain_upload_path,
1113 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001114
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001115 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001116 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001117 pformat.json(report, fp=f)