blob: bfef628986cf82b8235d278ef81bcc6636fa1dbb [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
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600239def UpdateBinhostConfFile(filepath: str, key: str, value: str) -> None:
240 """Update binhost config file with key=value.
241
242 The updated file will be committed, but not submitted.
David James8c846492011-01-25 17:07:29 -0800243
Alex Klein1699fab2022-09-08 08:46:06 -0600244 Args:
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600245 filepath: Path to the key-value store file to update.
Alex Klein1699fab2022-09-08 08:46:06 -0600246 key: Key to update.
247 value: New value for key.
248 """
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600249 dirname, basename = os.path.split(os.path.abspath(filepath))
250 osutils.SafeMakedirs(dirname)
251 if not git.GetCurrentBranch(dirname):
252 git.CreatePushBranch(
253 constants.STABLE_EBUILD_BRANCH, dirname, sync=False
Alex Klein1699fab2022-09-08 08:46:06 -0600254 )
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600255 osutils.WriteFile(filepath, "", mode="a")
256 if binpkg.UpdateKeyInLocalFile(filepath, key, value):
257 desc = f"{basename}: {'updating' if value else 'clearing'} {key}"
258 git.AddPath(filepath)
259 git.Commit(dirname, desc)
Alex Klein1699fab2022-09-08 08:46:06 -0600260
David James8c846492011-01-25 17:07:29 -0800261
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800262def GenerateHtmlIndex(files, index, board, version, remote_location):
Alex Klein1699fab2022-09-08 08:46:06 -0600263 """Given the list of |files|, generate an index.html at |index|.
Mike Frysinger212e4292014-05-24 15:15:44 -0400264
Alex Klein1699fab2022-09-08 08:46:06 -0600265 Args:
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,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600454 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600455 ):
456 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700457
Alex Klein1699fab2022-09-08 08:46:06 -0600458 Args:
459 board_path: The path to the board dir.
460 url_suffix: The remote subdirectory where we should upload the packages.
461 prepackaged: If given, a tarball that has been packaged outside of this
462 script and should be used.
463 toolchains_overlay_tarballs: List of toolchains overlay tarball
464 specifications to upload. Items take the form
465 "toolchains_spec:/path/to/tarball".
466 toolchains_overlay_upload_path: Path template under the bucket to place
467 toolchains overlay tarballs.
468 toolchain_tarballs: List of toolchain tarballs to upload.
469 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600470 sync_remote_latest_sdk_file: If True, update the remote latest SDK
471 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600472 """
473 remote_location = "%s/%s" % (
474 self._upload_location.rstrip("/"),
475 url_suffix,
476 )
477 assert remote_location.startswith("gs://")
478 boardname = os.path.basename(board_path.rstrip("/"))
479 # We do not upload non SDK board tarballs,
480 assert boardname == constants.CHROOT_BUILDER_BOARD
481 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 version_str = self._version[len("chroot-") :]
484 remote_tarfile = toolchain.GetSdkURL(
485 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
486 )
487 # For SDK, also upload the manifest which is guaranteed to exist
488 # by the builderstage.
489 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
490 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200491
Alex Klein1699fab2022-09-08 08:46:06 -0600492 # Upload SDK toolchains overlays and toolchain tarballs, if given.
493 for tarball_list, upload_path, qualifier_name in (
494 (
495 toolchains_overlay_tarballs,
496 toolchains_overlay_upload_path,
497 "toolchains",
498 ),
499 (toolchain_tarballs, toolchain_upload_path, "target"),
500 ):
501 for tarball_spec in tarball_list:
502 qualifier_val, local_path = tarball_spec.split(":")
503 suburl = upload_path % {qualifier_name: qualifier_val}
504 remote_path = toolchain.GetSdkURL(
505 for_gsutil=True, suburl=suburl
506 )
507 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 # Finally, also update the pointer to the latest SDK on which polling
510 # scripts rely.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600511 if sync_remote_latest_sdk_file:
512 self._UpdateRemoteSdkLatestFile(latest_sdk=version_str)
Greg Edelston64620d12023-02-23 15:29:49 -0700513
514 def _UpdateRemoteSdkLatestFile(
515 self,
516 latest_sdk: Optional[str] = None,
517 latest_sdk_uprev_target: Optional[str] = None,
518 ) -> None:
519 """Update the remote SDK pointer file on GS://.
520
521 The remote file contains multiple key-value pairs. This function can
522 update one or more of them; Nones will retain their existing values.
523
524 Args:
525 latest_sdk: The latest SDK that is tested and ready to be used. If
526 None, then the existing value on GS:// will be retained.
527 latest_sdk_uprev_target: The latest SDK that has been built, which
528 new PUprs can try to test and uprev. If None, then the existing
529 value on GS:// will be retained.
530 """
531 # Get existing values from the remote file.
532 remote_pointerfile = toolchain.GetSdkURL(
533 for_gsutil=True, suburl="cros-sdk-latest.conf"
534 )
535 existing_keyval = self._gs_context.LoadKeyValueStore(
536 remote_pointerfile, acl=self._acl
537 )
Greg Edelston2469ad32023-03-20 13:55:21 -0600538
539 # TODO(b/274196697): When LATEST_SDK_UPREV_TARGET is more reliably found
540 # in the remote latest file, require that key too.
541 for required_key in ("LATEST_SDK",):
542 if required_key not in existing_keyval:
543 raise ValueError(
544 f"Remote pointer file {remote_pointerfile} missing expected key {required_key}:\n{existing_keyval}"
545 )
Greg Edelston64620d12023-02-23 15:29:49 -0700546
547 # If any values were not specified in args, use the existing values.
548 if latest_sdk is None:
549 latest_sdk = existing_keyval["LATEST_SDK"]
550 if latest_sdk_uprev_target is None:
Greg Edelston2469ad32023-03-20 13:55:21 -0600551 latest_sdk_uprev_target = existing_keyval.get(
552 "LATEST_SDK_UPREV_TARGET", None
553 )
Greg Edelston64620d12023-02-23 15:29:49 -0700554
555 # Write a new local latest file with target values, and upload.
556 new_file_contents = self._CreateRemoteSdkLatestFileContents(
557 latest_sdk, latest_sdk_uprev_target
558 )
559 with osutils.TempDir() as tmpdir:
560 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
561 osutils.WriteFile(local_pointerfile, new_file_contents)
562 self._Upload(local_pointerfile, remote_pointerfile)
563
564 @staticmethod
565 def _CreateRemoteSdkLatestFileContents(
566 latest_sdk: str, latest_sdk_uprev_target: str
567 ) -> str:
568 """Generate file contents for a remote SDK file.
569
570 Args:
571 latest_sdk: The latest SDK that is tested and ready to be used.
572 latest_sdk_uprev_target: The latest SDK that has been built, which
573 new PUprs can try to test and uprev.
574
575 Returns:
576 The contents of a remote SDK latest file containing the given args
577 as a key-value store.
578 """
579 return f"""\
580# The most recent SDK that is tested and ready for use.
581LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700582
583# The most recently built version. New uprev attempts should target this.
584# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700585LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200586
Alex Klein1699fab2022-09-08 08:46:06 -0600587 def _GetTargets(self):
588 """Retuns the list of targets to use."""
589 targets = self._slave_targets[:]
590 if self._target:
591 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700592
Alex Klein1699fab2022-09-08 08:46:06 -0600593 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700594
Alex Klein1699fab2022-09-08 08:46:06 -0600595 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
596 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800597
Alex Klein1699fab2022-09-08 08:46:06 -0600598 This function will sync both the standard host packages, plus the host
599 packages associated with all targets that have been "setup" with the
600 current host's chroot. For instance, if this host has been used to build
601 x86-generic, it will sync the host packages associated with
602 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
603 it will also sync the host packages associated with
604 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800605
Alex Klein1699fab2022-09-08 08:46:06 -0600606 Args:
607 key: The variable key to update in the git file.
608 git_sync: If set, update make.conf of target to reference the latest
609 prebuilt packages generated here.
610 sync_binhost_conf: If set, update binhost config file in
611 chromiumos-overlay for the host.
612 """
613 # Slave boards are listed before the master board so that the master board
614 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
615 # over preflight host prebuilts from other builders.)
616 binhost_urls = []
617 for target in self._GetTargets():
618 url_suffix = _REL_HOST_PATH % {
619 "version": self._version,
620 "host_arch": _HOST_ARCH,
621 "target": target,
622 }
623 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800624
Alex Klein1699fab2022-09-08 08:46:06 -0600625 if self._target == target and not self._skip_upload:
626 # Upload prebuilts.
627 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
628 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700629
Alex Klein1699fab2022-09-08 08:46:06 -0600630 # Record URL where prebuilts were uploaded.
631 binhost_urls.append(
632 "%s/%s/"
633 % (
634 self._binhost_base_url.rstrip("/"),
635 packages_url_suffix.rstrip("/"),
636 )
637 )
David Jamese2488642011-11-14 16:15:20 -0800638
Alex Klein1699fab2022-09-08 08:46:06 -0600639 binhost = " ".join(binhost_urls)
640 if git_sync:
641 git_file = os.path.join(
642 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
643 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700644 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700645 git_file, {key: binhost}, self._report, dryrun=self._dryrun
646 )
Alex Klein1699fab2022-09-08 08:46:06 -0600647 if sync_binhost_conf:
648 binhost_conf = os.path.join(
649 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
650 )
651 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800652
Alex Klein1699fab2022-09-08 08:46:06 -0600653 def SyncBoardPrebuilts(
654 self,
655 key,
656 git_sync,
657 sync_binhost_conf,
658 upload_board_tarball,
659 prepackaged_board,
660 toolchains_overlay_tarballs,
661 toolchains_overlay_upload_path,
662 toolchain_tarballs,
663 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600664 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600665 ):
666 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800667
Alex Klein1699fab2022-09-08 08:46:06 -0600668 Args:
669 key: The variable key to update in the git file.
670 git_sync: If set, update make.conf of target to reference the latest
671 prebuilt packages generated here.
672 sync_binhost_conf: If set, update binhost config file in
673 chromiumos-overlay for the current board.
674 upload_board_tarball: Include a tarball of the board in our upload.
675 prepackaged_board: A tarball of the board built outside of this script.
676 toolchains_overlay_tarballs: List of toolchains overlay tarball
677 specifications to upload. Items take the form
678 "toolchains_spec:/path/to/tarball".
679 toolchains_overlay_upload_path: Path template under the bucket to place
680 toolchains overlay tarballs.
681 toolchain_tarballs: A list of toolchain tarballs to upload.
682 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600683 sync_remote_latest_sdk_file: If True, update the remote latest SDK
684 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600685 """
686 updated_binhosts = set()
687 for target in self._GetTargets():
688 board_path = os.path.join(
689 self._build_path, _BOARD_PATH % {"board": target.board_variant}
690 )
691 package_path = os.path.join(board_path, "packages")
692 url_suffix = _REL_BOARD_PATH % {
693 "target": target,
694 "version": self._version,
695 }
696 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700697
Alex Klein1699fab2022-09-08 08:46:06 -0600698 # Process the target board differently if it is the main --board.
699 if self._target == target and not self._skip_upload:
700 # This strips "chroot" prefix because that is sometimes added as the
701 # --prepend-version argument (e.g. by chromiumos-sdk bot).
702 # TODO(build): Clean it up to be less hard-coded.
703 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500704
Alex Klein1699fab2022-09-08 08:46:06 -0600705 # Upload board tarballs in the background.
706 if upload_board_tarball:
707 if toolchain_upload_path:
708 toolchain_upload_path %= {"version": version_str}
709 if toolchains_overlay_upload_path:
710 toolchains_overlay_upload_path %= {
711 "version": version_str
712 }
713 tar_process = multiprocessing.Process(
714 target=self._UploadSdkTarball,
715 args=(
716 board_path,
717 url_suffix,
718 prepackaged_board,
719 toolchains_overlay_tarballs,
720 toolchains_overlay_upload_path,
721 toolchain_tarballs,
722 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600723 sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600724 ),
725 )
726 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 # Upload prebuilts.
729 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700730
Alex Klein1699fab2022-09-08 08:46:06 -0600731 # Make sure we finished uploading the board tarballs.
732 if upload_board_tarball:
733 tar_process.join()
734 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800735
Alex Klein1699fab2022-09-08 08:46:06 -0600736 # Record URL where prebuilts were uploaded.
737 url_value = "%s/%s/" % (
738 self._binhost_base_url.rstrip("/"),
739 packages_url_suffix.rstrip("/"),
740 )
David Jamese2488642011-11-14 16:15:20 -0800741
Alex Klein1699fab2022-09-08 08:46:06 -0600742 if git_sync:
743 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700744 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700745 git_file,
746 {key: url_value},
747 self._report,
748 dryrun=self._dryrun,
749 )
Matt Tennante8179042013-10-01 15:47:32 -0700750
Alex Klein1699fab2022-09-08 08:46:06 -0600751 if sync_binhost_conf:
752 # Update the binhost configuration file in git.
753 binhost_conf = os.path.join(
754 self._binhost_conf_dir,
755 "target",
756 "%s-%s.conf" % (target, key),
757 )
758 updated_binhosts.add(binhost_conf)
759 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800760
Alex Klein1699fab2022-09-08 08:46:06 -0600761 if sync_binhost_conf:
762 # Clear all old binhosts. The files must be left empty in case anybody
763 # is referring to them.
764 all_binhosts = set(
765 glob.glob(
766 os.path.join(
767 self._binhost_conf_dir, "target", "*-%s.conf" % key
768 )
769 )
770 )
771 for binhost_conf in all_binhosts - updated_binhosts:
772 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800773
David James05bcb2b2011-02-09 09:25:47 -0800774
Mike Nicholsa1414162021-04-22 20:07:22 +0000775class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600776 """Callback that adds a slave board to the list of slave targets."""
777
778 def __call__(self, parser, namespace, values, option_string=None):
779 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800780
781
Mike Nicholsa1414162021-04-22 20:07:22 +0000782class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600783 """Callback that adds a slave profile to the list of slave targets."""
784
785 def __call__(self, parser, namespace, values, option_string=None):
786 if not namespace.slave_targets:
787 parser.error("Must specify --slave-board before --slave-profile")
788 if namespace.slave_targets[-1].profile is not None:
789 parser.error("Cannot specify --slave-profile twice for same board")
790 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800791
792
Mike Frysinger86509232014-05-24 13:18:37 -0400793def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600794 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700795
Alex Klein1699fab2022-09-08 08:46:06 -0600796 Args:
797 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400798
Alex Klein1699fab2022-09-08 08:46:06 -0600799 Returns:
800 A tuple containing a parsed options object and BuildTarget.
801 The target instance is None if no board is specified.
802 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500803 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600804 parser.add_argument(
805 "-H",
806 "--binhost-base-url",
807 default=_BINHOST_BASE_URL,
808 help="Base URL to use for binhost in make.conf updates",
809 )
810 parser.add_argument(
811 "--previous-binhost-url",
812 action="append",
813 default=[],
814 help="Previous binhost URL",
815 )
816 parser.add_argument(
817 "-b", "--board", help="Board type that was built on this machine"
818 )
819 parser.add_argument(
820 "-B",
821 "--prepackaged-tarball",
822 type="path",
823 help="Board tarball prebuilt outside of this script.",
824 )
825 parser.add_argument(
826 "--toolchains-overlay-tarball",
827 dest="toolchains_overlay_tarballs",
828 action="append",
829 default=[],
830 help="Toolchains overlay tarball specification to "
831 "upload. Takes the form "
832 '"toolchains_spec:/path/to/tarball".',
833 )
834 parser.add_argument(
835 "--toolchains-overlay-upload-path",
836 default="",
837 help="Path template for uploading toolchains overlays.",
838 )
839 parser.add_argument(
840 "--toolchain-tarball",
841 dest="toolchain_tarballs",
842 action="append",
843 default=[],
844 help="Redistributable toolchain tarball.",
845 )
846 parser.add_argument(
847 "--toolchain-upload-path",
848 default="",
849 help="Path to place toolchain tarballs in the sdk tree.",
850 )
851 parser.add_argument(
852 "--profile", help="Profile that was built on this machine"
853 )
854 parser.add_argument(
855 "--slave-board",
856 default=[],
857 action=_AddSlaveBoardAction,
858 dest="slave_targets",
859 help="Board type that was built on a slave machine. To "
860 "add a profile to this board, use --slave-profile.",
861 )
862 parser.add_argument(
863 "--slave-profile",
864 action=_AddSlaveProfileAction,
865 help="Board profile that was built on a slave machine. "
866 "Applies to previous slave board.",
867 )
868 parser.add_argument(
869 "-p",
870 "--build-path",
871 required=True,
872 help="Path to the directory containing the chroot",
873 )
874 parser.add_argument(
875 "--chroot",
876 help="Path where the chroot is located. "
877 "(Default: {build_path}/chroot)",
878 )
879 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700880 "--output",
881 type=Path,
882 help="Write a JSON report to the specified file. "
883 "(Default is not to write the report.)",
884 )
885 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600886 "--packages",
887 action="append",
888 default=[],
889 help="Only include the specified packages. "
890 "(Default is to include all packages.)",
891 )
892 parser.add_argument(
893 "-s",
894 "--sync-host",
895 default=False,
896 action="store_true",
897 help="Sync host prebuilts",
898 )
899 parser.add_argument(
900 "-g",
901 "--git-sync",
902 default=False,
903 action="store_true",
904 help="Enable git version sync (This commits to a repo.) "
905 "This is used by full builders to commit directly "
906 "to board overlays.",
907 )
Greg Edelston8da51ce2023-03-22 10:40:59 -0600908 parser.add_argument(
909 "--sync-remote-latest-sdk-file",
910 action="store_true",
911 help="Sync the remote latest SDK file on GS://. (Default)",
912 )
913 parser.add_argument(
914 "--no-sync-remote-latest-sdk-file",
915 dest="sync_remote_latest_sdk_file",
916 action="store_false",
917 help="Skip syncing the remote latest SDK file on GS://.",
918 )
Alex Klein1699fab2022-09-08 08:46:06 -0600919 parser.add_argument("-u", "--upload", help="Upload location")
920 parser.add_argument(
921 "-V",
922 "--prepend-version",
923 help="Add an identifier to the front of the version",
924 )
925 parser.add_argument(
926 "-f",
927 "--filters",
928 action="store_true",
929 default=False,
930 help="Turn on filtering of private ebuild packages",
931 )
932 parser.add_argument(
933 "-k",
934 "--key",
935 default="PORTAGE_BINHOST",
936 help="Key to update in make.conf / binhost.conf",
937 )
938 parser.add_argument("--set-version", help="Specify the version string")
939 parser.add_argument(
940 "--sync-binhost-conf",
941 default=False,
942 action="store_true",
943 help="Update binhost.conf in chromiumos-overlay or "
944 "chromeos-overlay. Commit the changes, but don't "
945 "push them. This is used for preflight binhosts.",
946 )
947 parser.add_argument(
948 "--binhost-conf-dir",
949 help="Directory to commit binhost config with " "--sync-binhost-conf.",
950 )
951 parser.add_argument(
952 "-P",
953 "--private",
954 action="store_true",
955 default=False,
956 help="Mark gs:// uploads as private.",
957 )
958 parser.add_argument(
959 "--skip-upload",
960 action="store_true",
961 default=False,
962 help="Skip upload step.",
963 )
964 parser.add_argument(
965 "--upload-board-tarball",
966 action="store_true",
967 default=False,
968 help="Upload board tarball to Google Storage.",
969 )
David James8c846492011-01-25 17:07:29 -0800970
Alex Klein1699fab2022-09-08 08:46:06 -0600971 options = parser.parse_args(argv)
972 if not options.upload and not options.skip_upload:
973 parser.error("you need to provide an upload location using -u")
974 if not options.set_version and options.skip_upload:
975 parser.error(
976 "If you are using --skip-upload, you must specify a "
977 "version number using --set-version."
978 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700979
Alex Klein1699fab2022-09-08 08:46:06 -0600980 target = None
981 if options.board:
982 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700983
Alex Klein1699fab2022-09-08 08:46:06 -0600984 if target in options.slave_targets:
985 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -0800986
Alex Klein1699fab2022-09-08 08:46:06 -0600987 if len(set(options.slave_targets)) != len(options.slave_targets):
988 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -0800989
Alex Klein1699fab2022-09-08 08:46:06 -0600990 if options.slave_targets and options.git_sync:
991 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -0800992
Alex Klein1699fab2022-09-08 08:46:06 -0600993 if (
994 options.upload_board_tarball
995 and options.skip_upload
996 and options.board == "amd64-host"
997 ):
998 parser.error(
999 "--skip-upload is not compatible with "
1000 "--upload-board-tarball and --board=amd64-host"
1001 )
David James8fa34ea2011-04-15 13:00:20 -07001002
Alex Klein1699fab2022-09-08 08:46:06 -06001003 if (
1004 options.upload_board_tarball
1005 and not options.skip_upload
1006 and not options.upload.startswith("gs://")
1007 ):
1008 parser.error(
1009 "--upload-board-tarball only works with gs:// URLs.\n"
1010 "--upload must be a gs:// URL."
1011 )
David James8fa34ea2011-04-15 13:00:20 -07001012
Alex Klein1699fab2022-09-08 08:46:06 -06001013 if options.upload_board_tarball and options.prepackaged_tarball is None:
1014 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001015
Alex Klein1699fab2022-09-08 08:46:06 -06001016 if options.private:
1017 if options.sync_host:
1018 parser.error(
1019 "--private and --sync-host/-s cannot be specified "
1020 "together; we do not support private host prebuilts"
1021 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001022
Alex Klein1699fab2022-09-08 08:46:06 -06001023 if not options.upload or not options.upload.startswith("gs://"):
1024 parser.error(
1025 "--private is only valid for gs:// URLs; "
1026 "--upload must be a gs:// URL."
1027 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001028
Alex Klein1699fab2022-09-08 08:46:06 -06001029 if options.binhost_base_url != _BINHOST_BASE_URL:
1030 parser.error(
1031 "when using --private the --binhost-base-url "
1032 "is automatically derived."
1033 )
David James27fa7d12011-06-29 17:24:14 -07001034
Alex Klein1699fab2022-09-08 08:46:06 -06001035 if options.sync_binhost_conf and not options.binhost_conf_dir:
1036 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001037
Alex Klein1699fab2022-09-08 08:46:06 -06001038 if (
1039 options.toolchains_overlay_tarballs
1040 and not options.toolchains_overlay_upload_path
1041 ):
1042 parser.error(
1043 "--toolchains-overlay-tarball requires "
1044 "--toolchains-overlay-upload-path"
1045 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001046
Alex Klein1699fab2022-09-08 08:46:06 -06001047 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001048
Mike Frysingercc838832014-05-24 13:10:30 -04001049
Mike Frysinger86509232014-05-24 13:18:37 -04001050def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001051 # We accumulate information about actions taken and report it at the end
1052 # if asked to do so. Currently, this only records CL creation, which
1053 # is the only thing we need for now.
1054 report = {}
1055
Alex Klein1699fab2022-09-08 08:46:06 -06001056 # Set umask so that files created as root are readable.
1057 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001058
Alex Klein1699fab2022-09-08 08:46:06 -06001059 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001060
Alex Klein1699fab2022-09-08 08:46:06 -06001061 # Calculate a list of Packages index files to compare against. Whenever we
1062 # upload a package, we check to make sure it's not already stored in one of
1063 # the packages files we uploaded. This list of packages files might contain
1064 # both board and host packages.
1065 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001066
Alex Klein1699fab2022-09-08 08:46:06 -06001067 if options.set_version:
1068 version = options.set_version
1069 else:
1070 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001071
Alex Klein1699fab2022-09-08 08:46:06 -06001072 if options.prepend_version:
1073 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001074
Alex Klein1699fab2022-09-08 08:46:06 -06001075 acl = "public-read"
1076 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001077
Alex Klein1699fab2022-09-08 08:46:06 -06001078 if options.private:
1079 binhost_base_url = options.upload
1080 if target:
1081 acl = portage_util.FindOverlayFile(
1082 _GOOGLESTORAGE_GSUTIL_FILE,
1083 board=target.board_variant,
1084 buildroot=options.build_path,
1085 )
1086 if acl is None:
1087 cros_build_lib.Die(
1088 "No Google Storage ACL file %s found in %s overlay.",
1089 _GOOGLESTORAGE_GSUTIL_FILE,
1090 target.board_variant,
1091 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001092
Alex Klein1699fab2022-09-08 08:46:06 -06001093 binhost_conf_dir = None
1094 if options.binhost_conf_dir:
1095 binhost_conf_dir = os.path.join(
1096 options.build_path, options.binhost_conf_dir
1097 )
David Jamesb26b9312014-12-15 11:26:46 -08001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 uploader = PrebuiltUploader(
1100 options.upload,
1101 acl,
1102 binhost_base_url,
1103 pkg_indexes,
1104 options.build_path,
1105 options.packages,
1106 options.skip_upload,
1107 binhost_conf_dir,
1108 options.dryrun,
1109 target,
1110 options.slave_targets,
1111 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001112 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001113 options.chroot,
1114 )
David Jamesc0f158a2011-02-22 16:07:29 -08001115
Alex Klein1699fab2022-09-08 08:46:06 -06001116 if options.sync_host:
1117 uploader.SyncHostPrebuilts(
1118 options.key, options.git_sync, options.sync_binhost_conf
1119 )
David James8c846492011-01-25 17:07:29 -08001120
Alex Klein1699fab2022-09-08 08:46:06 -06001121 if options.board or options.slave_targets:
1122 uploader.SyncBoardPrebuilts(
1123 options.key,
1124 options.git_sync,
1125 options.sync_binhost_conf,
1126 options.upload_board_tarball,
1127 options.prepackaged_tarball,
1128 options.toolchains_overlay_tarballs,
1129 options.toolchains_overlay_upload_path,
1130 options.toolchain_tarballs,
1131 options.toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -06001132 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -06001133 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001134
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001135 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001136 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001137 pformat.json(report, fp=f)