blob: fc80dc3e71547e16b2895b3858a81892ef03c677 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
David James8c846492011-01-25 17:07:29 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Brian Harringaf019fb2012-05-10 15:06:13 -07005"""This script is used to upload host prebuilts as well as board BINHOSTS.
David James8c846492011-01-25 17:07:29 -08006
David James015af872012-06-19 15:24:36 -07007Prebuilts are uploaded using gsutil to Google Storage. After these prebuilts
8are successfully uploaded, a file is updated with the proper BINHOST version.
David James8c846492011-01-25 17:07:29 -08009
10To read more about prebuilts/binhost binary packages please refer to:
David James015af872012-06-19 15:24:36 -070011http://goto/chromeos-prebuilts
David James8c846492011-01-25 17:07:29 -080012
13Example of uploading prebuilt amd64 host files to Google Storage:
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000014upload_prebuilts -p /b/cbuild/build -s -u gs://chromeos-prebuilt
David James8c846492011-01-25 17:07:29 -080015
16Example of uploading x86-dogfood binhosts to Google Storage:
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000017upload_prebuilts -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
David James8c846492011-01-25 17:07:29 -080018"""
19
Mike Frysingerb87bb782015-06-04 02:46:50 -040020import argparse
Chris Sosa1dc96132012-05-11 15:40:50 -070021import datetime
Mike Frysinger540883b2014-05-24 13:46:16 -040022import functools
David Jamesb26b9312014-12-15 11:26:46 -080023import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060024import logging
Chris Sosa1dc96132012-05-11 15:40:50 -070025import multiprocessing
Chris Sosa1dc96132012-05-11 15:40:50 -070026import os
Bob Haarmanc0082602022-09-20 16:12:43 -070027from pathlib import Path
Mike Frysinger212e4292014-05-24 15:15:44 -040028import tempfile
Greg Edelston325e4412023-05-04 16:22:10 -060029from typing import Optional, Tuple
Chris Sosa1dc96132012-05-11 15:40:50 -070030
Chris McDonaldb55b7032021-06-17 16:41:32 -060031from chromite.cbuildbot import cbuildbot_alerts
David James14e97772014-06-04 18:44:49 -070032from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080033from chromite.lib import binpkg
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:
Alex Kleind7197402023-04-05 13:05:29 -0600108 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.
Alex Klein1699fab2022-09-08 08:46:06 -0600112 """
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 Kleind7197402023-04-05 13:05:29 -0600124 # For private uploads we assume that the overlay board is set up
125 # properly and a googlestore_acl.xml is present. Otherwise, this script
126 # errors. We set version=0 here to ensure that the ACL is set only once
127 # (see http://b/15883752#comment54).
Alex Klein1699fab2022-09-08 08:46:06 -0600128 try:
129 gs_context.Copy(local_file, remote_file, version=0)
130 except gs.GSContextPreconditionFailed as ex:
Alex Kleind7197402023-04-05 13:05:29 -0600131 # If we received a GSContextPreconditionFailed error, we know that
132 # the file exists now, but we don't know whether our specific update
Alex Klein1699fab2022-09-08 08:46:06 -0600133 # succeeded. See http://b/15883752#comment62
134 logging.warning(
Alex Kleind7197402023-04-05 13:05:29 -0600135 "Assuming upload succeeded despite PreconditionFailed errors: "
136 "%s",
Alex Klein1699fab2022-09-08 08:46:06 -0600137 ex,
138 )
139
140 if acl.endswith(".xml"):
141 # Apply the passed in ACL xml file to the uploaded object.
142 gs_context.SetACL(remote_file, acl=acl)
143 else:
144 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700145
Mike Frysingercc838832014-05-24 13:10:30 -0400146
Mike Frysinger540883b2014-05-24 13:46:16 -0400147def RemoteUpload(gs_context, acl, files, pool=10):
Alex Klein1699fab2022-09-08 08:46:06 -0600148 """Upload to google storage.
David James8c846492011-01-25 17:07:29 -0800149
Alex Klein1699fab2022-09-08 08:46:06 -0600150 Create a pool of process and call _GsUpload with the proper arguments.
David James8c846492011-01-25 17:07:29 -0800151
Alex Klein1699fab2022-09-08 08:46:06 -0600152 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600153 gs_context: A lib.gs.GSContext instance.
154 acl: The canned acl used for uploading. acl can be one of:
155 "public-read", "public-read-write", "authenticated-read",
156 "bucket-owner-read", "bucket-owner-full-control", or "private".
157 files: dictionary with keys to local files and values to remote path.
158 pool: integer of maximum processes to have at the same time.
David James8c846492011-01-25 17:07:29 -0800159
Alex Klein1699fab2022-09-08 08:46:06 -0600160 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600161 Return a set of tuple arguments of the failed uploads
Alex Klein1699fab2022-09-08 08:46:06 -0600162 """
163 upload = functools.partial(_GsUpload, gs_context, acl)
164 tasks = [[key, value] for key, value in files.items()]
165 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800166
167
168def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
Alex Klein1699fab2022-09-08 08:46:06 -0600169 """Build a dictionary of local remote file key pairs to upload.
David James8c846492011-01-25 17:07:29 -0800170
Alex Klein1699fab2022-09-08 08:46:06 -0600171 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600172 base_local_path: The base path to the files on the local hard drive.
173 base_remote_path: The base path to the remote paths.
174 pkgs: The packages to upload.
David James8c846492011-01-25 17:07:29 -0800175
Alex Klein1699fab2022-09-08 08:46:06 -0600176 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600177 Returns a dictionary of local_path/remote_path pairs
Alex Klein1699fab2022-09-08 08:46:06 -0600178 """
179 upload_files = {}
180 for pkg in pkgs:
181 suffix = pkg["CPV"] + ".tbz2"
182 local_path = os.path.join(base_local_path, suffix)
183 assert os.path.exists(local_path), "%s does not exist" % local_path
184 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800185
Alex Klein1699fab2022-09-08 08:46:06 -0600186 if pkg.get("DEBUG_SYMBOLS") == "yes":
187 debugsuffix = pkg["CPV"] + ".debug.tbz2"
188 local_path = os.path.join(base_local_path, debugsuffix)
189 assert os.path.exists(local_path)
190 upload_files[local_path] = os.path.join(
191 base_remote_path, debugsuffix
192 )
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800193
Alex Klein1699fab2022-09-08 08:46:06 -0600194 return upload_files
David James8c846492011-01-25 17:07:29 -0800195
Mike Frysingercc838832014-05-24 13:10:30 -0400196
Peter Mayo950e41a2014-02-06 21:07:33 +0000197def GetBoardOverlay(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600198 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500199
Alex Klein1699fab2022-09-08 08:46:06 -0600200 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600201 build_path: The path to the root of the build directory.
202 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500203
Alex Klein1699fab2022-09-08 08:46:06 -0600204 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600205 The last overlay configured for the given board as a string.
Alex Klein1699fab2022-09-08 08:46:06 -0600206 """
207 board = target.board_variant
208 overlays = portage_util.FindOverlays(
209 constants.BOTH_OVERLAYS, board, buildroot=build_path
210 )
211 # We only care about the last entry.
212 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800213
214
215def DeterminePrebuiltConfFile(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600216 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
David James8c846492011-01-25 17:07:29 -0800217
Alex Klein1699fab2022-09-08 08:46:06 -0600218 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600219 build_path: The path to the root of the build directory.
220 target: String representation of the board. This includes host and board
221 targets.
David James8c846492011-01-25 17:07:29 -0800222
Alex Klein1699fab2022-09-08 08:46:06 -0600223 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600224 A string path to a prebuilt.conf file to be updated.
Alex Klein1699fab2022-09-08 08:46:06 -0600225 """
226 if _HOST_ARCH == target:
227 # We are host.
228 # Without more examples of hosts this is a kludge for now.
229 # TODO(Scottz): as new host targets come online expand this to
230 # work more like boards.
231 make_path = _PREBUILT_MAKE_CONF[target]
232 else:
233 # We are a board
234 board = GetBoardOverlay(build_path, target)
235 make_path = os.path.join(board, "prebuilt.conf")
David James8c846492011-01-25 17:07:29 -0800236
Alex Klein1699fab2022-09-08 08:46:06 -0600237 return make_path
David James8c846492011-01-25 17:07:29 -0800238
239
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600240def UpdateBinhostConfFile(filepath: str, key: str, value: str) -> None:
241 """Update binhost config file with key=value.
242
243 The updated file will be committed, but not submitted.
David James8c846492011-01-25 17:07:29 -0800244
Alex Klein1699fab2022-09-08 08:46:06 -0600245 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600246 filepath: Path to the key-value store file to update.
247 key: Key to update.
248 value: New value for key.
Alex Klein1699fab2022-09-08 08:46:06 -0600249 """
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600250 dirname, basename = os.path.split(os.path.abspath(filepath))
251 osutils.SafeMakedirs(dirname)
252 if not git.GetCurrentBranch(dirname):
253 git.CreatePushBranch(
254 constants.STABLE_EBUILD_BRANCH, dirname, sync=False
Alex Klein1699fab2022-09-08 08:46:06 -0600255 )
Greg Edelstonadecc1b2023-03-22 17:00:42 -0600256 osutils.WriteFile(filepath, "", mode="a")
257 if binpkg.UpdateKeyInLocalFile(filepath, key, value):
258 desc = f"{basename}: {'updating' if value else 'clearing'} {key}"
259 git.AddPath(filepath)
260 git.Commit(dirname, desc)
Alex Klein1699fab2022-09-08 08:46:06 -0600261
David James8c846492011-01-25 17:07:29 -0800262
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800263def GenerateHtmlIndex(files, index, board, version, remote_location):
Alex Klein1699fab2022-09-08 08:46:06 -0600264 """Given the list of |files|, generate an index.html at |index|.
Mike Frysinger212e4292014-05-24 15:15:44 -0400265
Alex Klein1699fab2022-09-08 08:46:06 -0600266 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600267 files: The list of files to link to.
268 index: The path to the html index.
269 board: Name of the board this index is for.
270 version: Build version this index is for.
271 remote_location: Remote gs location prebuilts are uploaded to.
Alex Klein1699fab2022-09-08 08:46:06 -0600272 """
273 title = "Package Prebuilt Index: %s / %s" % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400274
Alex Klein1699fab2022-09-08 08:46:06 -0600275 files = files + [
276 ".|Google Storage Index",
277 "..|",
278 ]
279 commands.GenerateHtmlIndex(
280 index, files, title=title, url_base=gs.GsUrlToHttp(remote_location)
281 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400282
283
David Jamesce093af2011-02-23 15:21:58 -0800284def _GrabAllRemotePackageIndexes(binhost_urls):
Alex Kleind7197402023-04-05 13:05:29 -0600285 """Grab all the packages files associated with a list of binhost_urls.
David James05bcb2b2011-02-09 09:25:47 -0800286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600288 binhost_urls: The URLs for the directories containing the Packages files
289 we want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800290
Alex Klein1699fab2022-09-08 08:46:06 -0600291 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600292 A list of PackageIndex objects.
Alex Klein1699fab2022-09-08 08:46:06 -0600293 """
294 pkg_indexes = []
295 for url in binhost_urls:
296 pkg_index = binpkg.GrabRemotePackageIndex(url)
297 if pkg_index:
298 pkg_indexes.append(pkg_index)
299 return pkg_indexes
David James05bcb2b2011-02-09 09:25:47 -0800300
301
David Jamesc0f158a2011-02-22 16:07:29 -0800302class PrebuiltUploader(object):
Alex Klein1699fab2022-09-08 08:46:06 -0600303 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800304
Alex Klein1699fab2022-09-08 08:46:06 -0600305 def __init__(
306 self,
307 upload_location,
308 acl,
309 binhost_base_url,
310 pkg_indexes,
311 build_path,
312 packages,
313 skip_upload,
314 binhost_conf_dir,
315 dryrun,
316 target,
317 slave_targets,
318 version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700319 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600320 chroot=None,
321 ):
322 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800323
Alex Klein1699fab2022-09-08 08:46:06 -0600324 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800325
Alex Klein1699fab2022-09-08 08:46:06 -0600326 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600327 upload_location: The upload location.
328 acl: The canned acl used for uploading to Google Storage. acl can be
329 one of: "public-read", "public-read-write",
330 "authenticated-read", "bucket-owner-read",
331 "bucket-owner-full-control", "project-private", or "private"
332 (see "gsutil help acls"). If we are not uploading to Google
333 Storage, this parameter is unused.
334 binhost_base_url: The URL used for downloading the prebuilts.
335 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
336 uploading duplicate files, we just link to the old files.
337 build_path: The path to the directory containing the chroot.
338 packages: Packages to upload.
339 skip_upload: Don't actually upload the tarballs.
340 binhost_conf_dir: Directory where to store binhost.conf files.
341 dryrun: Don't push or upload prebuilts.
342 target: BuildTarget managed by this builder.
343 slave_targets: List of BuildTargets managed by slave builders.
344 version: A unique string, intended to be included in the upload
345 path, which identifies the version number of the uploaded
346 prebuilts.
347 report: Dict in which to collect information to report to the user.
348 chroot: Path to the chroot containing the prebuilts.
Alex Klein1699fab2022-09-08 08:46:06 -0600349 """
350 self._upload_location = upload_location
351 self._acl = acl
352 self._binhost_base_url = binhost_base_url
353 self._pkg_indexes = pkg_indexes
354 self._build_path = build_path
355 self._packages = set(packages)
356 self._found_packages = set()
357 self._skip_upload = skip_upload
358 self._binhost_conf_dir = binhost_conf_dir
359 self._dryrun = dryrun
360 self._target = target
361 self._slave_targets = slave_targets
362 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700363 self._report = report
Alex Klein1699fab2022-09-08 08:46:06 -0600364 self._chroot = chroot or os.path.join(
365 build_path, constants.DEFAULT_CHROOT_DIR
366 )
367 self._gs_context = gs.GSContext(
368 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
369 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400370
Alex Klein1699fab2022-09-08 08:46:06 -0600371 def _Upload(self, local_file, remote_file):
372 """Wrapper around _GsUpload"""
373 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700374
Alex Klein1699fab2022-09-08 08:46:06 -0600375 def _ShouldFilterPackage(self, pkg):
376 if not self._packages:
377 return False
378 cpv = package_info.SplitCPV(pkg["CPV"])
379 self._found_packages.add(cpv.cp)
380 return (
381 cpv.package not in self._packages and cpv.cp not in self._packages
382 )
David James8c846492011-01-25 17:07:29 -0800383
Alex Klein1699fab2022-09-08 08:46:06 -0600384 def _UploadPrebuilt(self, package_path, url_suffix):
385 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800386
Alex Klein1699fab2022-09-08 08:46:06 -0600387 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600388 package_path: The path to the packages dir.
389 url_suffix: The remote subdirectory where we should upload the
390 packages.
Alex Klein1699fab2022-09-08 08:46:06 -0600391 """
392 # Process Packages file, removing duplicates and filtered packages.
393 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
394 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
395 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
396 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
397 unmatched_pkgs = self._packages - self._found_packages
398 if unmatched_pkgs:
399 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800400
Alex Klein1699fab2022-09-08 08:46:06 -0600401 # Write Packages file.
402 pkg_index.header["TTL"] = _BINPKG_TTL
403 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800404
Alex Klein1699fab2022-09-08 08:46:06 -0600405 remote_location = "%s/%s" % (
406 self._upload_location.rstrip("/"),
407 url_suffix,
408 )
409 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800410
Alex Klein1699fab2022-09-08 08:46:06 -0600411 upload_files = GenerateUploadDict(
412 package_path, remote_location, uploads
413 )
414 remote_file = "%s/Packages" % remote_location.rstrip("/")
415 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 # Build list of files to upload. Manually include the dev-only files but
418 # skip them if not present.
419 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
420 if os.path.exists(dev_only):
421 upload_files[dev_only] = "%s/%s" % (
422 remote_location.rstrip("/"),
423 os.path.basename(dev_only),
424 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400425
Alex Klein1699fab2022-09-08 08:46:06 -0600426 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800427
Alex Klein1699fab2022-09-08 08:46:06 -0600428 with tempfile.NamedTemporaryFile(
429 prefix="chromite.upload_prebuilts.index."
430 ) as index:
431 GenerateHtmlIndex(
432 [x[len(remote_location) + 1 :] for x in upload_files.values()],
433 index.name,
434 self._target,
435 self._version,
436 remote_location,
437 )
438 self._Upload(
439 index.name, "%s/index.html" % remote_location.rstrip("/")
440 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400441
Alex Klein1699fab2022-09-08 08:46:06 -0600442 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
443 url = "%s%s/index.html" % (
444 gs.PUBLIC_BASE_HTTPS_URL,
445 remote_location[len(gs.BASE_GS_URL) :],
446 )
447 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400448
Alex Klein1699fab2022-09-08 08:46:06 -0600449 def _UploadSdkTarball(
450 self,
451 board_path,
452 url_suffix,
453 prepackaged,
454 toolchains_overlay_tarballs,
455 toolchains_overlay_upload_path,
456 toolchain_tarballs,
457 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600458 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600459 ):
460 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700461
Alex Klein1699fab2022-09-08 08:46:06 -0600462 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600463 board_path: The path to the board dir.
464 url_suffix: The remote subdirectory where we should upload the
465 packages.
466 prepackaged: If given, a tarball that has been packaged outside of
467 this script and should be used.
468 toolchains_overlay_tarballs: List of toolchains overlay tarball
469 specifications to upload. Items take the form
470 "toolchains_spec:/path/to/tarball".
471 toolchains_overlay_upload_path: Path template under the bucket to
472 place toolchains overlay tarballs.
473 toolchain_tarballs: List of toolchain tarballs to upload.
474 toolchain_upload_path: Path under the bucket to place toolchain
475 tarballs.
476 sync_remote_latest_sdk_file: If True, update the remote latest SDK
477 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600478 """
479 remote_location = "%s/%s" % (
480 self._upload_location.rstrip("/"),
481 url_suffix,
482 )
483 assert remote_location.startswith("gs://")
484 boardname = os.path.basename(board_path.rstrip("/"))
485 # We do not upload non SDK board tarballs,
486 assert boardname == constants.CHROOT_BUILDER_BOARD
487 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200488
Alex Klein1699fab2022-09-08 08:46:06 -0600489 version_str = self._version[len("chroot-") :]
490 remote_tarfile = toolchain.GetSdkURL(
491 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
492 )
493 # For SDK, also upload the manifest which is guaranteed to exist
494 # by the builderstage.
495 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
496 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200497
Alex Klein1699fab2022-09-08 08:46:06 -0600498 # Upload SDK toolchains overlays and toolchain tarballs, if given.
499 for tarball_list, upload_path, qualifier_name in (
500 (
501 toolchains_overlay_tarballs,
502 toolchains_overlay_upload_path,
503 "toolchains",
504 ),
505 (toolchain_tarballs, toolchain_upload_path, "target"),
506 ):
507 for tarball_spec in tarball_list:
508 qualifier_val, local_path = tarball_spec.split(":")
509 suburl = upload_path % {qualifier_name: qualifier_val}
510 remote_path = toolchain.GetSdkURL(
511 for_gsutil=True, suburl=suburl
512 )
513 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500514
Alex Klein1699fab2022-09-08 08:46:06 -0600515 # Finally, also update the pointer to the latest SDK on which polling
516 # scripts rely.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600517 if sync_remote_latest_sdk_file:
518 self._UpdateRemoteSdkLatestFile(latest_sdk=version_str)
Greg Edelston64620d12023-02-23 15:29:49 -0700519
520 def _UpdateRemoteSdkLatestFile(
521 self,
522 latest_sdk: Optional[str] = None,
523 latest_sdk_uprev_target: Optional[str] = None,
524 ) -> None:
525 """Update the remote SDK pointer file on GS://.
526
527 The remote file contains multiple key-value pairs. This function can
528 update one or more of them; Nones will retain their existing values.
529
530 Args:
531 latest_sdk: The latest SDK that is tested and ready to be used. If
532 None, then the existing value on GS:// will be retained.
533 latest_sdk_uprev_target: The latest SDK that has been built, which
534 new PUprs can try to test and uprev. If None, then the existing
535 value on GS:// will be retained.
536 """
Greg Edelston119cbe12023-03-29 14:26:52 -0600537 # This would be a noop in dryrun mode -- or worse, it would fail to
538 # parse the remote key-value store after pretending to download it.
539 # Instead of side-stepping errors, return early with descriptive logs.
540 if self._dryrun:
541 logging.debug("Not updating remote SDK latest file in dryrun mode.")
542 if latest_sdk is not None:
543 logging.debug("Would have set LATEST_SDK=%s", latest_sdk)
544 if latest_sdk_uprev_target is not None:
545 logging.debug(
546 "Would have set LATEST_SDK_UPREV_TARGET=%s",
547 latest_sdk_uprev_target,
548 )
549 return
550
Greg Edelston64620d12023-02-23 15:29:49 -0700551 # Get existing values from the remote file.
552 remote_pointerfile = toolchain.GetSdkURL(
553 for_gsutil=True, suburl="cros-sdk-latest.conf"
554 )
555 existing_keyval = self._gs_context.LoadKeyValueStore(
556 remote_pointerfile, acl=self._acl
557 )
Greg Edelston2469ad32023-03-20 13:55:21 -0600558
559 # TODO(b/274196697): When LATEST_SDK_UPREV_TARGET is more reliably found
560 # in the remote latest file, require that key too.
561 for required_key in ("LATEST_SDK",):
562 if required_key not in existing_keyval:
563 raise ValueError(
Alex Kleind7197402023-04-05 13:05:29 -0600564 f"Remote pointer file {remote_pointerfile} missing "
565 f"expected key {required_key}:\n{existing_keyval}"
Greg Edelston2469ad32023-03-20 13:55:21 -0600566 )
Greg Edelston64620d12023-02-23 15:29:49 -0700567
568 # If any values were not specified in args, use the existing values.
569 if latest_sdk is None:
570 latest_sdk = existing_keyval["LATEST_SDK"]
571 if latest_sdk_uprev_target is None:
Greg Edelston2469ad32023-03-20 13:55:21 -0600572 latest_sdk_uprev_target = existing_keyval.get(
573 "LATEST_SDK_UPREV_TARGET", None
574 )
Greg Edelston64620d12023-02-23 15:29:49 -0700575
576 # Write a new local latest file with target values, and upload.
577 new_file_contents = self._CreateRemoteSdkLatestFileContents(
578 latest_sdk, latest_sdk_uprev_target
579 )
580 with osutils.TempDir() as tmpdir:
581 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
582 osutils.WriteFile(local_pointerfile, new_file_contents)
583 self._Upload(local_pointerfile, remote_pointerfile)
584
585 @staticmethod
586 def _CreateRemoteSdkLatestFileContents(
587 latest_sdk: str, latest_sdk_uprev_target: str
588 ) -> str:
589 """Generate file contents for a remote SDK file.
590
591 Args:
592 latest_sdk: The latest SDK that is tested and ready to be used.
593 latest_sdk_uprev_target: The latest SDK that has been built, which
594 new PUprs can try to test and uprev.
595
596 Returns:
597 The contents of a remote SDK latest file containing the given args
598 as a key-value store.
599 """
600 return f"""\
601# The most recent SDK that is tested and ready for use.
602LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700603
604# The most recently built version. New uprev attempts should target this.
605# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700606LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200607
Alex Klein1699fab2022-09-08 08:46:06 -0600608 def _GetTargets(self):
609 """Retuns the list of targets to use."""
610 targets = self._slave_targets[:]
611 if self._target:
612 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700613
Alex Klein1699fab2022-09-08 08:46:06 -0600614 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700615
Alex Klein1699fab2022-09-08 08:46:06 -0600616 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
617 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800618
Alex Klein1699fab2022-09-08 08:46:06 -0600619 This function will sync both the standard host packages, plus the host
620 packages associated with all targets that have been "setup" with the
621 current host's chroot. For instance, if this host has been used to build
622 x86-generic, it will sync the host packages associated with
Alex Kleind7197402023-04-05 13:05:29 -0600623 'i686-pc-linux-gnu'. If this host has also been used to build
624 arm-generic, it will also sync the host packages associated with
Alex Klein1699fab2022-09-08 08:46:06 -0600625 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600628 key: The variable key to update in the git file.
629 git_sync: If set, update make.conf of target to reference the latest
630 prebuilt packages generated here.
631 sync_binhost_conf: If set, update binhost config file in
632 chromiumos-overlay for the host.
Alex Klein1699fab2022-09-08 08:46:06 -0600633 """
Alex Kleind7197402023-04-05 13:05:29 -0600634 # Slave boards are listed before the master board so that the master
635 # board takes priority (i.e. x86-generic preflight host prebuilts takes
636 # priority over preflight host prebuilts from other builders.)
Alex Klein1699fab2022-09-08 08:46:06 -0600637 binhost_urls = []
638 for target in self._GetTargets():
639 url_suffix = _REL_HOST_PATH % {
640 "version": self._version,
641 "host_arch": _HOST_ARCH,
642 "target": target,
643 }
644 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800645
Alex Klein1699fab2022-09-08 08:46:06 -0600646 if self._target == target and not self._skip_upload:
647 # Upload prebuilts.
648 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
649 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700650
Alex Klein1699fab2022-09-08 08:46:06 -0600651 # Record URL where prebuilts were uploaded.
652 binhost_urls.append(
653 "%s/%s/"
654 % (
655 self._binhost_base_url.rstrip("/"),
656 packages_url_suffix.rstrip("/"),
657 )
658 )
David Jamese2488642011-11-14 16:15:20 -0800659
Alex Klein1699fab2022-09-08 08:46:06 -0600660 binhost = " ".join(binhost_urls)
661 if git_sync:
662 git_file = os.path.join(
663 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
664 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700665 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700666 git_file, {key: binhost}, self._report, dryrun=self._dryrun
667 )
Alex Klein1699fab2022-09-08 08:46:06 -0600668 if sync_binhost_conf:
669 binhost_conf = os.path.join(
670 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
671 )
672 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800673
Alex Klein1699fab2022-09-08 08:46:06 -0600674 def SyncBoardPrebuilts(
675 self,
676 key,
677 git_sync,
678 sync_binhost_conf,
679 upload_board_tarball,
680 prepackaged_board,
681 toolchains_overlay_tarballs,
682 toolchains_overlay_upload_path,
683 toolchain_tarballs,
684 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600685 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600686 ):
687 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800688
Alex Klein1699fab2022-09-08 08:46:06 -0600689 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600690 key: The variable key to update in the git file.
691 git_sync: If set, update make.conf of target to reference the latest
692 prebuilt packages generated here.
693 sync_binhost_conf: If set, update binhost config file in
694 chromiumos-overlay for the current board.
695 upload_board_tarball: Include a tarball of the board in our upload.
696 prepackaged_board: A tarball of the board built outside of this
697 script.
698 toolchains_overlay_tarballs: List of toolchains overlay tarball
699 specifications to upload. Items take the form
700 "toolchains_spec:/path/to/tarball".
701 toolchains_overlay_upload_path: Path template under the bucket to
702 place toolchains overlay tarballs.
703 toolchain_tarballs: A list of toolchain tarballs to upload.
704 toolchain_upload_path: Path under the bucket to place toolchain
705 tarballs.
706 sync_remote_latest_sdk_file: If True, update the remote latest SDK
707 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600708 """
709 updated_binhosts = set()
710 for target in self._GetTargets():
711 board_path = os.path.join(
712 self._build_path, _BOARD_PATH % {"board": target.board_variant}
713 )
714 package_path = os.path.join(board_path, "packages")
715 url_suffix = _REL_BOARD_PATH % {
716 "target": target,
717 "version": self._version,
718 }
719 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700720
Alex Klein1699fab2022-09-08 08:46:06 -0600721 # Process the target board differently if it is the main --board.
722 if self._target == target and not self._skip_upload:
Alex Kleind7197402023-04-05 13:05:29 -0600723 # This strips "chroot" prefix because that is sometimes added as
724 # the --prepend-version argument (e.g. by chromiumos-sdk bot).
Alex Klein1699fab2022-09-08 08:46:06 -0600725 # TODO(build): Clean it up to be less hard-coded.
726 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 # Upload board tarballs in the background.
729 if upload_board_tarball:
730 if toolchain_upload_path:
731 toolchain_upload_path %= {"version": version_str}
732 if toolchains_overlay_upload_path:
733 toolchains_overlay_upload_path %= {
734 "version": version_str
735 }
736 tar_process = multiprocessing.Process(
737 target=self._UploadSdkTarball,
738 args=(
739 board_path,
740 url_suffix,
741 prepackaged_board,
742 toolchains_overlay_tarballs,
743 toolchains_overlay_upload_path,
744 toolchain_tarballs,
745 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600746 sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600747 ),
748 )
749 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700750
Alex Klein1699fab2022-09-08 08:46:06 -0600751 # Upload prebuilts.
752 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700753
Alex Klein1699fab2022-09-08 08:46:06 -0600754 # Make sure we finished uploading the board tarballs.
755 if upload_board_tarball:
756 tar_process.join()
757 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800758
Alex Klein1699fab2022-09-08 08:46:06 -0600759 # Record URL where prebuilts were uploaded.
760 url_value = "%s/%s/" % (
761 self._binhost_base_url.rstrip("/"),
762 packages_url_suffix.rstrip("/"),
763 )
David Jamese2488642011-11-14 16:15:20 -0800764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 if git_sync:
766 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700767 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700768 git_file,
769 {key: url_value},
770 self._report,
771 dryrun=self._dryrun,
772 )
Matt Tennante8179042013-10-01 15:47:32 -0700773
Alex Klein1699fab2022-09-08 08:46:06 -0600774 if sync_binhost_conf:
775 # Update the binhost configuration file in git.
776 binhost_conf = os.path.join(
777 self._binhost_conf_dir,
778 "target",
779 "%s-%s.conf" % (target, key),
780 )
781 updated_binhosts.add(binhost_conf)
782 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800783
Alex Klein1699fab2022-09-08 08:46:06 -0600784 if sync_binhost_conf:
Alex Kleind7197402023-04-05 13:05:29 -0600785 # Clear all old binhosts. The files must be left empty in case
786 # anybody is referring to them.
Alex Klein1699fab2022-09-08 08:46:06 -0600787 all_binhosts = set(
788 glob.glob(
789 os.path.join(
790 self._binhost_conf_dir, "target", "*-%s.conf" % key
791 )
792 )
793 )
794 for binhost_conf in all_binhosts - updated_binhosts:
795 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800796
David James05bcb2b2011-02-09 09:25:47 -0800797
Mike Nicholsa1414162021-04-22 20:07:22 +0000798class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600799 """Callback that adds a slave board to the list of slave targets."""
800
801 def __call__(self, parser, namespace, values, option_string=None):
802 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800803
804
Mike Nicholsa1414162021-04-22 20:07:22 +0000805class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600806 """Callback that adds a slave profile to the list of slave targets."""
807
808 def __call__(self, parser, namespace, values, option_string=None):
809 if not namespace.slave_targets:
810 parser.error("Must specify --slave-board before --slave-profile")
811 if namespace.slave_targets[-1].profile is not None:
812 parser.error("Cannot specify --slave-profile twice for same board")
813 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800814
815
Greg Edelston325e4412023-05-04 16:22:10 -0600816def ParseOptions(argv) -> Tuple[argparse.Namespace, Optional[BuildTarget]]:
Alex Klein1699fab2022-09-08 08:46:06 -0600817 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700818
Alex Klein1699fab2022-09-08 08:46:06 -0600819 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600820 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400821
Alex Klein1699fab2022-09-08 08:46:06 -0600822 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600823 A tuple containing a parsed options object and BuildTarget.
824 The target instance is None if no board is specified.
Alex Klein1699fab2022-09-08 08:46:06 -0600825 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500826 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600827 parser.add_argument(
828 "-H",
829 "--binhost-base-url",
830 default=_BINHOST_BASE_URL,
831 help="Base URL to use for binhost in make.conf updates",
832 )
833 parser.add_argument(
834 "--previous-binhost-url",
835 action="append",
836 default=[],
837 help="Previous binhost URL",
838 )
839 parser.add_argument(
840 "-b", "--board", help="Board type that was built on this machine"
841 )
842 parser.add_argument(
843 "-B",
844 "--prepackaged-tarball",
845 type="path",
846 help="Board tarball prebuilt outside of this script.",
847 )
848 parser.add_argument(
849 "--toolchains-overlay-tarball",
850 dest="toolchains_overlay_tarballs",
851 action="append",
852 default=[],
853 help="Toolchains overlay tarball specification to "
854 "upload. Takes the form "
855 '"toolchains_spec:/path/to/tarball".',
856 )
857 parser.add_argument(
858 "--toolchains-overlay-upload-path",
859 default="",
860 help="Path template for uploading toolchains overlays.",
861 )
862 parser.add_argument(
863 "--toolchain-tarball",
864 dest="toolchain_tarballs",
865 action="append",
866 default=[],
867 help="Redistributable toolchain tarball.",
868 )
869 parser.add_argument(
870 "--toolchain-upload-path",
871 default="",
872 help="Path to place toolchain tarballs in the sdk tree.",
873 )
874 parser.add_argument(
875 "--profile", help="Profile that was built on this machine"
876 )
877 parser.add_argument(
878 "--slave-board",
879 default=[],
880 action=_AddSlaveBoardAction,
881 dest="slave_targets",
882 help="Board type that was built on a slave machine. To "
883 "add a profile to this board, use --slave-profile.",
884 )
885 parser.add_argument(
886 "--slave-profile",
887 action=_AddSlaveProfileAction,
888 help="Board profile that was built on a slave machine. "
889 "Applies to previous slave board.",
890 )
891 parser.add_argument(
892 "-p",
893 "--build-path",
894 required=True,
895 help="Path to the directory containing the chroot",
896 )
897 parser.add_argument(
898 "--chroot",
899 help="Path where the chroot is located. "
900 "(Default: {build_path}/chroot)",
901 )
902 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700903 "--output",
904 type=Path,
905 help="Write a JSON report to the specified file. "
906 "(Default is not to write the report.)",
907 )
908 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600909 "--packages",
910 action="append",
911 default=[],
912 help="Only include the specified packages. "
913 "(Default is to include all packages.)",
914 )
915 parser.add_argument(
916 "-s",
917 "--sync-host",
918 default=False,
919 action="store_true",
920 help="Sync host prebuilts",
921 )
922 parser.add_argument(
923 "-g",
924 "--git-sync",
925 default=False,
926 action="store_true",
927 help="Enable git version sync (This commits to a repo.) "
928 "This is used by full builders to commit directly "
929 "to board overlays.",
930 )
Greg Edelston8da51ce2023-03-22 10:40:59 -0600931 parser.add_argument(
932 "--sync-remote-latest-sdk-file",
933 action="store_true",
Greg Edelston325e4412023-05-04 16:22:10 -0600934 default=True,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600935 help="Sync the remote latest SDK file on GS://. (Default)",
936 )
937 parser.add_argument(
938 "--no-sync-remote-latest-sdk-file",
939 dest="sync_remote_latest_sdk_file",
940 action="store_false",
941 help="Skip syncing the remote latest SDK file on GS://.",
942 )
Alex Klein1699fab2022-09-08 08:46:06 -0600943 parser.add_argument("-u", "--upload", help="Upload location")
944 parser.add_argument(
945 "-V",
946 "--prepend-version",
947 help="Add an identifier to the front of the version",
948 )
949 parser.add_argument(
950 "-f",
951 "--filters",
952 action="store_true",
953 default=False,
954 help="Turn on filtering of private ebuild packages",
955 )
956 parser.add_argument(
957 "-k",
958 "--key",
959 default="PORTAGE_BINHOST",
960 help="Key to update in make.conf / binhost.conf",
961 )
962 parser.add_argument("--set-version", help="Specify the version string")
963 parser.add_argument(
964 "--sync-binhost-conf",
965 default=False,
966 action="store_true",
967 help="Update binhost.conf in chromiumos-overlay or "
968 "chromeos-overlay. Commit the changes, but don't "
969 "push them. This is used for preflight binhosts.",
970 )
971 parser.add_argument(
972 "--binhost-conf-dir",
973 help="Directory to commit binhost config with " "--sync-binhost-conf.",
974 )
975 parser.add_argument(
976 "-P",
977 "--private",
978 action="store_true",
979 default=False,
980 help="Mark gs:// uploads as private.",
981 )
982 parser.add_argument(
983 "--skip-upload",
984 action="store_true",
985 default=False,
986 help="Skip upload step.",
987 )
988 parser.add_argument(
989 "--upload-board-tarball",
990 action="store_true",
991 default=False,
992 help="Upload board tarball to Google Storage.",
993 )
David James8c846492011-01-25 17:07:29 -0800994
Alex Klein1699fab2022-09-08 08:46:06 -0600995 options = parser.parse_args(argv)
996 if not options.upload and not options.skip_upload:
997 parser.error("you need to provide an upload location using -u")
998 if not options.set_version and options.skip_upload:
999 parser.error(
1000 "If you are using --skip-upload, you must specify a "
1001 "version number using --set-version."
1002 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001003
Alex Klein1699fab2022-09-08 08:46:06 -06001004 target = None
1005 if options.board:
1006 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -07001007
Alex Klein1699fab2022-09-08 08:46:06 -06001008 if target in options.slave_targets:
1009 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001010
Alex Klein1699fab2022-09-08 08:46:06 -06001011 if len(set(options.slave_targets)) != len(options.slave_targets):
1012 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001013
Alex Klein1699fab2022-09-08 08:46:06 -06001014 if options.slave_targets and options.git_sync:
1015 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001016
Alex Klein1699fab2022-09-08 08:46:06 -06001017 if (
1018 options.upload_board_tarball
1019 and options.skip_upload
1020 and options.board == "amd64-host"
1021 ):
1022 parser.error(
1023 "--skip-upload is not compatible with "
1024 "--upload-board-tarball and --board=amd64-host"
1025 )
David James8fa34ea2011-04-15 13:00:20 -07001026
Alex Klein1699fab2022-09-08 08:46:06 -06001027 if (
1028 options.upload_board_tarball
1029 and not options.skip_upload
1030 and not options.upload.startswith("gs://")
1031 ):
1032 parser.error(
1033 "--upload-board-tarball only works with gs:// URLs.\n"
1034 "--upload must be a gs:// URL."
1035 )
David James8fa34ea2011-04-15 13:00:20 -07001036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 if options.upload_board_tarball and options.prepackaged_tarball is None:
1038 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001039
Alex Klein1699fab2022-09-08 08:46:06 -06001040 if options.private:
1041 if options.sync_host:
1042 parser.error(
1043 "--private and --sync-host/-s cannot be specified "
1044 "together; we do not support private host prebuilts"
1045 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001046
Alex Klein1699fab2022-09-08 08:46:06 -06001047 if not options.upload or not options.upload.startswith("gs://"):
1048 parser.error(
1049 "--private is only valid for gs:// URLs; "
1050 "--upload must be a gs:// URL."
1051 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001052
Alex Klein1699fab2022-09-08 08:46:06 -06001053 if options.binhost_base_url != _BINHOST_BASE_URL:
1054 parser.error(
1055 "when using --private the --binhost-base-url "
1056 "is automatically derived."
1057 )
David James27fa7d12011-06-29 17:24:14 -07001058
Alex Klein1699fab2022-09-08 08:46:06 -06001059 if options.sync_binhost_conf and not options.binhost_conf_dir:
1060 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001061
Alex Klein1699fab2022-09-08 08:46:06 -06001062 if (
1063 options.toolchains_overlay_tarballs
1064 and not options.toolchains_overlay_upload_path
1065 ):
1066 parser.error(
1067 "--toolchains-overlay-tarball requires "
1068 "--toolchains-overlay-upload-path"
1069 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001070
Alex Klein1699fab2022-09-08 08:46:06 -06001071 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001072
Mike Frysingercc838832014-05-24 13:10:30 -04001073
Mike Frysinger86509232014-05-24 13:18:37 -04001074def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001075 # We accumulate information about actions taken and report it at the end
1076 # if asked to do so. Currently, this only records CL creation, which
1077 # is the only thing we need for now.
1078 report = {}
1079
Alex Klein1699fab2022-09-08 08:46:06 -06001080 # Set umask so that files created as root are readable.
1081 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001082
Alex Klein1699fab2022-09-08 08:46:06 -06001083 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001084
Alex Klein1699fab2022-09-08 08:46:06 -06001085 # Calculate a list of Packages index files to compare against. Whenever we
1086 # upload a package, we check to make sure it's not already stored in one of
1087 # the packages files we uploaded. This list of packages files might contain
1088 # both board and host packages.
1089 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001090
Alex Klein1699fab2022-09-08 08:46:06 -06001091 if options.set_version:
1092 version = options.set_version
1093 else:
1094 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001095
Alex Klein1699fab2022-09-08 08:46:06 -06001096 if options.prepend_version:
1097 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 acl = "public-read"
1100 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001101
Alex Klein1699fab2022-09-08 08:46:06 -06001102 if options.private:
1103 binhost_base_url = options.upload
1104 if target:
1105 acl = portage_util.FindOverlayFile(
1106 _GOOGLESTORAGE_GSUTIL_FILE,
1107 board=target.board_variant,
1108 buildroot=options.build_path,
1109 )
1110 if acl is None:
1111 cros_build_lib.Die(
1112 "No Google Storage ACL file %s found in %s overlay.",
1113 _GOOGLESTORAGE_GSUTIL_FILE,
1114 target.board_variant,
1115 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001116
Alex Klein1699fab2022-09-08 08:46:06 -06001117 binhost_conf_dir = None
1118 if options.binhost_conf_dir:
1119 binhost_conf_dir = os.path.join(
1120 options.build_path, options.binhost_conf_dir
1121 )
David Jamesb26b9312014-12-15 11:26:46 -08001122
Alex Klein1699fab2022-09-08 08:46:06 -06001123 uploader = PrebuiltUploader(
1124 options.upload,
1125 acl,
1126 binhost_base_url,
1127 pkg_indexes,
1128 options.build_path,
1129 options.packages,
1130 options.skip_upload,
1131 binhost_conf_dir,
1132 options.dryrun,
1133 target,
1134 options.slave_targets,
1135 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001136 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001137 options.chroot,
1138 )
David Jamesc0f158a2011-02-22 16:07:29 -08001139
Alex Klein1699fab2022-09-08 08:46:06 -06001140 if options.sync_host:
1141 uploader.SyncHostPrebuilts(
1142 options.key, options.git_sync, options.sync_binhost_conf
1143 )
David James8c846492011-01-25 17:07:29 -08001144
Alex Klein1699fab2022-09-08 08:46:06 -06001145 if options.board or options.slave_targets:
1146 uploader.SyncBoardPrebuilts(
1147 options.key,
1148 options.git_sync,
1149 options.sync_binhost_conf,
1150 options.upload_board_tarball,
1151 options.prepackaged_tarball,
1152 options.toolchains_overlay_tarballs,
1153 options.toolchains_overlay_upload_path,
1154 options.toolchain_tarballs,
1155 options.toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -06001156 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -06001157 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001158
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001159 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001160 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001161 pformat.json(report, fp=f)