blob: 0de765645ce64a2fd6774430e75547da8c9a6fea [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 """
Greg Edelston119cbe12023-03-29 14:26:52 -0600531 # This would be a noop in dryrun mode -- or worse, it would fail to
532 # parse the remote key-value store after pretending to download it.
533 # Instead of side-stepping errors, return early with descriptive logs.
534 if self._dryrun:
535 logging.debug("Not updating remote SDK latest file in dryrun mode.")
536 if latest_sdk is not None:
537 logging.debug("Would have set LATEST_SDK=%s", latest_sdk)
538 if latest_sdk_uprev_target is not None:
539 logging.debug(
540 "Would have set LATEST_SDK_UPREV_TARGET=%s",
541 latest_sdk_uprev_target,
542 )
543 return
544
Greg Edelston64620d12023-02-23 15:29:49 -0700545 # Get existing values from the remote file.
546 remote_pointerfile = toolchain.GetSdkURL(
547 for_gsutil=True, suburl="cros-sdk-latest.conf"
548 )
549 existing_keyval = self._gs_context.LoadKeyValueStore(
550 remote_pointerfile, acl=self._acl
551 )
Greg Edelston2469ad32023-03-20 13:55:21 -0600552
553 # TODO(b/274196697): When LATEST_SDK_UPREV_TARGET is more reliably found
554 # in the remote latest file, require that key too.
555 for required_key in ("LATEST_SDK",):
556 if required_key not in existing_keyval:
557 raise ValueError(
558 f"Remote pointer file {remote_pointerfile} missing expected key {required_key}:\n{existing_keyval}"
559 )
Greg Edelston64620d12023-02-23 15:29:49 -0700560
561 # If any values were not specified in args, use the existing values.
562 if latest_sdk is None:
563 latest_sdk = existing_keyval["LATEST_SDK"]
564 if latest_sdk_uprev_target is None:
Greg Edelston2469ad32023-03-20 13:55:21 -0600565 latest_sdk_uprev_target = existing_keyval.get(
566 "LATEST_SDK_UPREV_TARGET", None
567 )
Greg Edelston64620d12023-02-23 15:29:49 -0700568
569 # Write a new local latest file with target values, and upload.
570 new_file_contents = self._CreateRemoteSdkLatestFileContents(
571 latest_sdk, latest_sdk_uprev_target
572 )
573 with osutils.TempDir() as tmpdir:
574 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
575 osutils.WriteFile(local_pointerfile, new_file_contents)
576 self._Upload(local_pointerfile, remote_pointerfile)
577
578 @staticmethod
579 def _CreateRemoteSdkLatestFileContents(
580 latest_sdk: str, latest_sdk_uprev_target: str
581 ) -> str:
582 """Generate file contents for a remote SDK file.
583
584 Args:
585 latest_sdk: The latest SDK that is tested and ready to be used.
586 latest_sdk_uprev_target: The latest SDK that has been built, which
587 new PUprs can try to test and uprev.
588
589 Returns:
590 The contents of a remote SDK latest file containing the given args
591 as a key-value store.
592 """
593 return f"""\
594# The most recent SDK that is tested and ready for use.
595LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700596
597# The most recently built version. New uprev attempts should target this.
598# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700599LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200600
Alex Klein1699fab2022-09-08 08:46:06 -0600601 def _GetTargets(self):
602 """Retuns the list of targets to use."""
603 targets = self._slave_targets[:]
604 if self._target:
605 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700606
Alex Klein1699fab2022-09-08 08:46:06 -0600607 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700608
Alex Klein1699fab2022-09-08 08:46:06 -0600609 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
610 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800611
Alex Klein1699fab2022-09-08 08:46:06 -0600612 This function will sync both the standard host packages, plus the host
613 packages associated with all targets that have been "setup" with the
614 current host's chroot. For instance, if this host has been used to build
615 x86-generic, it will sync the host packages associated with
616 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
617 it will also sync the host packages associated with
618 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800619
Alex Klein1699fab2022-09-08 08:46:06 -0600620 Args:
621 key: The variable key to update in the git file.
622 git_sync: If set, update make.conf of target to reference the latest
623 prebuilt packages generated here.
624 sync_binhost_conf: If set, update binhost config file in
625 chromiumos-overlay for the host.
626 """
627 # Slave boards are listed before the master board so that the master board
628 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
629 # over preflight host prebuilts from other builders.)
630 binhost_urls = []
631 for target in self._GetTargets():
632 url_suffix = _REL_HOST_PATH % {
633 "version": self._version,
634 "host_arch": _HOST_ARCH,
635 "target": target,
636 }
637 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800638
Alex Klein1699fab2022-09-08 08:46:06 -0600639 if self._target == target and not self._skip_upload:
640 # Upload prebuilts.
641 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
642 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700643
Alex Klein1699fab2022-09-08 08:46:06 -0600644 # Record URL where prebuilts were uploaded.
645 binhost_urls.append(
646 "%s/%s/"
647 % (
648 self._binhost_base_url.rstrip("/"),
649 packages_url_suffix.rstrip("/"),
650 )
651 )
David Jamese2488642011-11-14 16:15:20 -0800652
Alex Klein1699fab2022-09-08 08:46:06 -0600653 binhost = " ".join(binhost_urls)
654 if git_sync:
655 git_file = os.path.join(
656 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
657 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700658 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700659 git_file, {key: binhost}, self._report, dryrun=self._dryrun
660 )
Alex Klein1699fab2022-09-08 08:46:06 -0600661 if sync_binhost_conf:
662 binhost_conf = os.path.join(
663 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
664 )
665 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800666
Alex Klein1699fab2022-09-08 08:46:06 -0600667 def SyncBoardPrebuilts(
668 self,
669 key,
670 git_sync,
671 sync_binhost_conf,
672 upload_board_tarball,
673 prepackaged_board,
674 toolchains_overlay_tarballs,
675 toolchains_overlay_upload_path,
676 toolchain_tarballs,
677 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600678 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600679 ):
680 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800681
Alex Klein1699fab2022-09-08 08:46:06 -0600682 Args:
683 key: The variable key to update in the git file.
684 git_sync: If set, update make.conf of target to reference the latest
685 prebuilt packages generated here.
686 sync_binhost_conf: If set, update binhost config file in
687 chromiumos-overlay for the current board.
688 upload_board_tarball: Include a tarball of the board in our upload.
689 prepackaged_board: A tarball of the board built outside of this script.
690 toolchains_overlay_tarballs: List of toolchains overlay tarball
691 specifications to upload. Items take the form
692 "toolchains_spec:/path/to/tarball".
693 toolchains_overlay_upload_path: Path template under the bucket to place
694 toolchains overlay tarballs.
695 toolchain_tarballs: A list of toolchain tarballs to upload.
696 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600697 sync_remote_latest_sdk_file: If True, update the remote latest SDK
698 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600699 """
700 updated_binhosts = set()
701 for target in self._GetTargets():
702 board_path = os.path.join(
703 self._build_path, _BOARD_PATH % {"board": target.board_variant}
704 )
705 package_path = os.path.join(board_path, "packages")
706 url_suffix = _REL_BOARD_PATH % {
707 "target": target,
708 "version": self._version,
709 }
710 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700711
Alex Klein1699fab2022-09-08 08:46:06 -0600712 # Process the target board differently if it is the main --board.
713 if self._target == target and not self._skip_upload:
714 # This strips "chroot" prefix because that is sometimes added as the
715 # --prepend-version argument (e.g. by chromiumos-sdk bot).
716 # TODO(build): Clean it up to be less hard-coded.
717 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500718
Alex Klein1699fab2022-09-08 08:46:06 -0600719 # Upload board tarballs in the background.
720 if upload_board_tarball:
721 if toolchain_upload_path:
722 toolchain_upload_path %= {"version": version_str}
723 if toolchains_overlay_upload_path:
724 toolchains_overlay_upload_path %= {
725 "version": version_str
726 }
727 tar_process = multiprocessing.Process(
728 target=self._UploadSdkTarball,
729 args=(
730 board_path,
731 url_suffix,
732 prepackaged_board,
733 toolchains_overlay_tarballs,
734 toolchains_overlay_upload_path,
735 toolchain_tarballs,
736 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600737 sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600738 ),
739 )
740 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700741
Alex Klein1699fab2022-09-08 08:46:06 -0600742 # Upload prebuilts.
743 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700744
Alex Klein1699fab2022-09-08 08:46:06 -0600745 # Make sure we finished uploading the board tarballs.
746 if upload_board_tarball:
747 tar_process.join()
748 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800749
Alex Klein1699fab2022-09-08 08:46:06 -0600750 # Record URL where prebuilts were uploaded.
751 url_value = "%s/%s/" % (
752 self._binhost_base_url.rstrip("/"),
753 packages_url_suffix.rstrip("/"),
754 )
David Jamese2488642011-11-14 16:15:20 -0800755
Alex Klein1699fab2022-09-08 08:46:06 -0600756 if git_sync:
757 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700758 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700759 git_file,
760 {key: url_value},
761 self._report,
762 dryrun=self._dryrun,
763 )
Matt Tennante8179042013-10-01 15:47:32 -0700764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 if sync_binhost_conf:
766 # Update the binhost configuration file in git.
767 binhost_conf = os.path.join(
768 self._binhost_conf_dir,
769 "target",
770 "%s-%s.conf" % (target, key),
771 )
772 updated_binhosts.add(binhost_conf)
773 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800774
Alex Klein1699fab2022-09-08 08:46:06 -0600775 if sync_binhost_conf:
776 # Clear all old binhosts. The files must be left empty in case anybody
777 # is referring to them.
778 all_binhosts = set(
779 glob.glob(
780 os.path.join(
781 self._binhost_conf_dir, "target", "*-%s.conf" % key
782 )
783 )
784 )
785 for binhost_conf in all_binhosts - updated_binhosts:
786 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800787
David James05bcb2b2011-02-09 09:25:47 -0800788
Mike Nicholsa1414162021-04-22 20:07:22 +0000789class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600790 """Callback that adds a slave board to the list of slave targets."""
791
792 def __call__(self, parser, namespace, values, option_string=None):
793 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800794
795
Mike Nicholsa1414162021-04-22 20:07:22 +0000796class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600797 """Callback that adds a slave profile to the list of slave targets."""
798
799 def __call__(self, parser, namespace, values, option_string=None):
800 if not namespace.slave_targets:
801 parser.error("Must specify --slave-board before --slave-profile")
802 if namespace.slave_targets[-1].profile is not None:
803 parser.error("Cannot specify --slave-profile twice for same board")
804 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800805
806
Mike Frysinger86509232014-05-24 13:18:37 -0400807def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600808 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700809
Alex Klein1699fab2022-09-08 08:46:06 -0600810 Args:
811 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400812
Alex Klein1699fab2022-09-08 08:46:06 -0600813 Returns:
814 A tuple containing a parsed options object and BuildTarget.
815 The target instance is None if no board is specified.
816 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500817 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600818 parser.add_argument(
819 "-H",
820 "--binhost-base-url",
821 default=_BINHOST_BASE_URL,
822 help="Base URL to use for binhost in make.conf updates",
823 )
824 parser.add_argument(
825 "--previous-binhost-url",
826 action="append",
827 default=[],
828 help="Previous binhost URL",
829 )
830 parser.add_argument(
831 "-b", "--board", help="Board type that was built on this machine"
832 )
833 parser.add_argument(
834 "-B",
835 "--prepackaged-tarball",
836 type="path",
837 help="Board tarball prebuilt outside of this script.",
838 )
839 parser.add_argument(
840 "--toolchains-overlay-tarball",
841 dest="toolchains_overlay_tarballs",
842 action="append",
843 default=[],
844 help="Toolchains overlay tarball specification to "
845 "upload. Takes the form "
846 '"toolchains_spec:/path/to/tarball".',
847 )
848 parser.add_argument(
849 "--toolchains-overlay-upload-path",
850 default="",
851 help="Path template for uploading toolchains overlays.",
852 )
853 parser.add_argument(
854 "--toolchain-tarball",
855 dest="toolchain_tarballs",
856 action="append",
857 default=[],
858 help="Redistributable toolchain tarball.",
859 )
860 parser.add_argument(
861 "--toolchain-upload-path",
862 default="",
863 help="Path to place toolchain tarballs in the sdk tree.",
864 )
865 parser.add_argument(
866 "--profile", help="Profile that was built on this machine"
867 )
868 parser.add_argument(
869 "--slave-board",
870 default=[],
871 action=_AddSlaveBoardAction,
872 dest="slave_targets",
873 help="Board type that was built on a slave machine. To "
874 "add a profile to this board, use --slave-profile.",
875 )
876 parser.add_argument(
877 "--slave-profile",
878 action=_AddSlaveProfileAction,
879 help="Board profile that was built on a slave machine. "
880 "Applies to previous slave board.",
881 )
882 parser.add_argument(
883 "-p",
884 "--build-path",
885 required=True,
886 help="Path to the directory containing the chroot",
887 )
888 parser.add_argument(
889 "--chroot",
890 help="Path where the chroot is located. "
891 "(Default: {build_path}/chroot)",
892 )
893 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700894 "--output",
895 type=Path,
896 help="Write a JSON report to the specified file. "
897 "(Default is not to write the report.)",
898 )
899 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600900 "--packages",
901 action="append",
902 default=[],
903 help="Only include the specified packages. "
904 "(Default is to include all packages.)",
905 )
906 parser.add_argument(
907 "-s",
908 "--sync-host",
909 default=False,
910 action="store_true",
911 help="Sync host prebuilts",
912 )
913 parser.add_argument(
914 "-g",
915 "--git-sync",
916 default=False,
917 action="store_true",
918 help="Enable git version sync (This commits to a repo.) "
919 "This is used by full builders to commit directly "
920 "to board overlays.",
921 )
Greg Edelston8da51ce2023-03-22 10:40:59 -0600922 parser.add_argument(
923 "--sync-remote-latest-sdk-file",
924 action="store_true",
925 help="Sync the remote latest SDK file on GS://. (Default)",
926 )
927 parser.add_argument(
928 "--no-sync-remote-latest-sdk-file",
929 dest="sync_remote_latest_sdk_file",
930 action="store_false",
931 help="Skip syncing the remote latest SDK file on GS://.",
932 )
Alex Klein1699fab2022-09-08 08:46:06 -0600933 parser.add_argument("-u", "--upload", help="Upload location")
934 parser.add_argument(
935 "-V",
936 "--prepend-version",
937 help="Add an identifier to the front of the version",
938 )
939 parser.add_argument(
940 "-f",
941 "--filters",
942 action="store_true",
943 default=False,
944 help="Turn on filtering of private ebuild packages",
945 )
946 parser.add_argument(
947 "-k",
948 "--key",
949 default="PORTAGE_BINHOST",
950 help="Key to update in make.conf / binhost.conf",
951 )
952 parser.add_argument("--set-version", help="Specify the version string")
953 parser.add_argument(
954 "--sync-binhost-conf",
955 default=False,
956 action="store_true",
957 help="Update binhost.conf in chromiumos-overlay or "
958 "chromeos-overlay. Commit the changes, but don't "
959 "push them. This is used for preflight binhosts.",
960 )
961 parser.add_argument(
962 "--binhost-conf-dir",
963 help="Directory to commit binhost config with " "--sync-binhost-conf.",
964 )
965 parser.add_argument(
966 "-P",
967 "--private",
968 action="store_true",
969 default=False,
970 help="Mark gs:// uploads as private.",
971 )
972 parser.add_argument(
973 "--skip-upload",
974 action="store_true",
975 default=False,
976 help="Skip upload step.",
977 )
978 parser.add_argument(
979 "--upload-board-tarball",
980 action="store_true",
981 default=False,
982 help="Upload board tarball to Google Storage.",
983 )
David James8c846492011-01-25 17:07:29 -0800984
Alex Klein1699fab2022-09-08 08:46:06 -0600985 options = parser.parse_args(argv)
986 if not options.upload and not options.skip_upload:
987 parser.error("you need to provide an upload location using -u")
988 if not options.set_version and options.skip_upload:
989 parser.error(
990 "If you are using --skip-upload, you must specify a "
991 "version number using --set-version."
992 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700993
Alex Klein1699fab2022-09-08 08:46:06 -0600994 target = None
995 if options.board:
996 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700997
Alex Klein1699fab2022-09-08 08:46:06 -0600998 if target in options.slave_targets:
999 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001000
Alex Klein1699fab2022-09-08 08:46:06 -06001001 if len(set(options.slave_targets)) != len(options.slave_targets):
1002 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001003
Alex Klein1699fab2022-09-08 08:46:06 -06001004 if options.slave_targets and options.git_sync:
1005 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001006
Alex Klein1699fab2022-09-08 08:46:06 -06001007 if (
1008 options.upload_board_tarball
1009 and options.skip_upload
1010 and options.board == "amd64-host"
1011 ):
1012 parser.error(
1013 "--skip-upload is not compatible with "
1014 "--upload-board-tarball and --board=amd64-host"
1015 )
David James8fa34ea2011-04-15 13:00:20 -07001016
Alex Klein1699fab2022-09-08 08:46:06 -06001017 if (
1018 options.upload_board_tarball
1019 and not options.skip_upload
1020 and not options.upload.startswith("gs://")
1021 ):
1022 parser.error(
1023 "--upload-board-tarball only works with gs:// URLs.\n"
1024 "--upload must be a gs:// URL."
1025 )
David James8fa34ea2011-04-15 13:00:20 -07001026
Alex Klein1699fab2022-09-08 08:46:06 -06001027 if options.upload_board_tarball and options.prepackaged_tarball is None:
1028 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001029
Alex Klein1699fab2022-09-08 08:46:06 -06001030 if options.private:
1031 if options.sync_host:
1032 parser.error(
1033 "--private and --sync-host/-s cannot be specified "
1034 "together; we do not support private host prebuilts"
1035 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 if not options.upload or not options.upload.startswith("gs://"):
1038 parser.error(
1039 "--private is only valid for gs:// URLs; "
1040 "--upload must be a gs:// URL."
1041 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001042
Alex Klein1699fab2022-09-08 08:46:06 -06001043 if options.binhost_base_url != _BINHOST_BASE_URL:
1044 parser.error(
1045 "when using --private the --binhost-base-url "
1046 "is automatically derived."
1047 )
David James27fa7d12011-06-29 17:24:14 -07001048
Alex Klein1699fab2022-09-08 08:46:06 -06001049 if options.sync_binhost_conf and not options.binhost_conf_dir:
1050 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001051
Alex Klein1699fab2022-09-08 08:46:06 -06001052 if (
1053 options.toolchains_overlay_tarballs
1054 and not options.toolchains_overlay_upload_path
1055 ):
1056 parser.error(
1057 "--toolchains-overlay-tarball requires "
1058 "--toolchains-overlay-upload-path"
1059 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001060
Alex Klein1699fab2022-09-08 08:46:06 -06001061 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001062
Mike Frysingercc838832014-05-24 13:10:30 -04001063
Mike Frysinger86509232014-05-24 13:18:37 -04001064def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001065 # We accumulate information about actions taken and report it at the end
1066 # if asked to do so. Currently, this only records CL creation, which
1067 # is the only thing we need for now.
1068 report = {}
1069
Alex Klein1699fab2022-09-08 08:46:06 -06001070 # Set umask so that files created as root are readable.
1071 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001072
Alex Klein1699fab2022-09-08 08:46:06 -06001073 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001074
Alex Klein1699fab2022-09-08 08:46:06 -06001075 # Calculate a list of Packages index files to compare against. Whenever we
1076 # upload a package, we check to make sure it's not already stored in one of
1077 # the packages files we uploaded. This list of packages files might contain
1078 # both board and host packages.
1079 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001080
Alex Klein1699fab2022-09-08 08:46:06 -06001081 if options.set_version:
1082 version = options.set_version
1083 else:
1084 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001085
Alex Klein1699fab2022-09-08 08:46:06 -06001086 if options.prepend_version:
1087 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001088
Alex Klein1699fab2022-09-08 08:46:06 -06001089 acl = "public-read"
1090 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001091
Alex Klein1699fab2022-09-08 08:46:06 -06001092 if options.private:
1093 binhost_base_url = options.upload
1094 if target:
1095 acl = portage_util.FindOverlayFile(
1096 _GOOGLESTORAGE_GSUTIL_FILE,
1097 board=target.board_variant,
1098 buildroot=options.build_path,
1099 )
1100 if acl is None:
1101 cros_build_lib.Die(
1102 "No Google Storage ACL file %s found in %s overlay.",
1103 _GOOGLESTORAGE_GSUTIL_FILE,
1104 target.board_variant,
1105 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001106
Alex Klein1699fab2022-09-08 08:46:06 -06001107 binhost_conf_dir = None
1108 if options.binhost_conf_dir:
1109 binhost_conf_dir = os.path.join(
1110 options.build_path, options.binhost_conf_dir
1111 )
David Jamesb26b9312014-12-15 11:26:46 -08001112
Alex Klein1699fab2022-09-08 08:46:06 -06001113 uploader = PrebuiltUploader(
1114 options.upload,
1115 acl,
1116 binhost_base_url,
1117 pkg_indexes,
1118 options.build_path,
1119 options.packages,
1120 options.skip_upload,
1121 binhost_conf_dir,
1122 options.dryrun,
1123 target,
1124 options.slave_targets,
1125 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001126 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001127 options.chroot,
1128 )
David Jamesc0f158a2011-02-22 16:07:29 -08001129
Alex Klein1699fab2022-09-08 08:46:06 -06001130 if options.sync_host:
1131 uploader.SyncHostPrebuilts(
1132 options.key, options.git_sync, options.sync_binhost_conf
1133 )
David James8c846492011-01-25 17:07:29 -08001134
Alex Klein1699fab2022-09-08 08:46:06 -06001135 if options.board or options.slave_targets:
1136 uploader.SyncBoardPrebuilts(
1137 options.key,
1138 options.git_sync,
1139 options.sync_binhost_conf,
1140 options.upload_board_tarball,
1141 options.prepackaged_tarball,
1142 options.toolchains_overlay_tarballs,
1143 options.toolchains_overlay_upload_path,
1144 options.toolchain_tarballs,
1145 options.toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -06001146 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -06001147 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001148
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001149 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001150 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001151 pformat.json(report, fp=f)