blob: 1c685b0c9f62e1ddeefbfbcd359687042fefc771 [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
Bob Haarman193ab2e2023-07-10 20:02:09 +000028import re
Mike Frysinger212e4292014-05-24 15:15:44 -040029import tempfile
Greg Edelston325e4412023-05-04 16:22:10 -060030from typing import Optional, Tuple
Chris Sosa1dc96132012-05-11 15:40:50 -070031
Chris McDonaldb55b7032021-06-17 16:41:32 -060032from chromite.cbuildbot import cbuildbot_alerts
David James14e97772014-06-04 18:44:49 -070033from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080034from chromite.lib import binpkg
Brian Norris74d1af92023-06-14 11:28:12 -070035from chromite.lib import chroot_lib
Mike Frysinger86509232014-05-24 13:18:37 -040036from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060037from chromite.lib import constants
Chris Sosa1dc96132012-05-11 15:40:50 -070038from chromite.lib import cros_build_lib
Mike Frysinger807d8282022-04-28 22:45:17 -040039from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070040from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070041from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080042from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070043from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050044from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060045from chromite.lib.parser import package_info
Bob Haarmanc0082602022-09-20 16:12:43 -070046from chromite.utils import pformat
Chris Sosa1dc96132012-05-11 15:40:50 -070047
Mike Frysinger72b7cf92020-04-19 06:00:51 -040048
David James015af872012-06-19 15:24:36 -070049# How many times to retry uploads.
50_RETRIES = 10
51
52# Multiplier for how long to sleep (in seconds) between retries; will delay
53# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
54_SLEEP_TIME = 60
55
David James5ab67e32014-10-24 08:19:59 -070056# The length of time (in seconds) that Portage should wait before refetching
57# binpkgs from the same binhost. We don't ever modify binhosts, so this should
58# be something big.
59_BINPKG_TTL = 60 * 60 * 24 * 365
60
Alex Klein1699fab2022-09-08 08:46:06 -060061_HOST_PACKAGES_PATH = "var/lib/portage/pkgs"
Alex Klein1699fab2022-09-08 08:46:06 -060062_HOST_ARCH = "amd64"
Brian Norris74d1af92023-06-14 11:28:12 -070063_BOARD_PATH = "build/%(board)s"
Alex Klein1699fab2022-09-08 08:46:06 -060064_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
Alex Klein074f94f2023-06-22 10:32:06 -060078class BuildTarget:
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
Alex Klein074f94f2023-06-22 10:32:06 -0600302class PrebuiltUploader:
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,
Brian Norris74d1af92023-06-14 11:28:12 -0700321 out_dir=None,
Alex Klein1699fab2022-09-08 08:46:06 -0600322 ):
323 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800324
Alex Klein1699fab2022-09-08 08:46:06 -0600325 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600328 upload_location: The upload location.
329 acl: The canned acl used for uploading to Google Storage. acl can be
330 one of: "public-read", "public-read-write",
331 "authenticated-read", "bucket-owner-read",
332 "bucket-owner-full-control", "project-private", or "private"
333 (see "gsutil help acls"). If we are not uploading to Google
334 Storage, this parameter is unused.
335 binhost_base_url: The URL used for downloading the prebuilts.
336 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
337 uploading duplicate files, we just link to the old files.
338 build_path: The path to the directory containing the chroot.
339 packages: Packages to upload.
340 skip_upload: Don't actually upload the tarballs.
341 binhost_conf_dir: Directory where to store binhost.conf files.
342 dryrun: Don't push or upload prebuilts.
343 target: BuildTarget managed by this builder.
344 slave_targets: List of BuildTargets managed by slave builders.
345 version: A unique string, intended to be included in the upload
346 path, which identifies the version number of the uploaded
347 prebuilts.
348 report: Dict in which to collect information to report to the user.
Brian Norris74d1af92023-06-14 11:28:12 -0700349 chroot: Path to the chroot.
350 out_dir: Path to the SDK output directory for the chroot.
Alex Klein1699fab2022-09-08 08:46:06 -0600351 """
352 self._upload_location = upload_location
353 self._acl = acl
354 self._binhost_base_url = binhost_base_url
355 self._pkg_indexes = pkg_indexes
356 self._build_path = build_path
357 self._packages = set(packages)
358 self._found_packages = set()
359 self._skip_upload = skip_upload
360 self._binhost_conf_dir = binhost_conf_dir
361 self._dryrun = dryrun
362 self._target = target
363 self._slave_targets = slave_targets
364 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700365 self._report = report
Brian Norris74d1af92023-06-14 11:28:12 -0700366 chroot_path = chroot or os.path.join(
Alex Klein1699fab2022-09-08 08:46:06 -0600367 build_path, constants.DEFAULT_CHROOT_DIR
368 )
Brian Norris74d1af92023-06-14 11:28:12 -0700369 out_path = Path(
370 out_dir or os.path.join(build_path, constants.DEFAULT_OUT_DIR)
371 )
372 self._chroot = chroot_lib.Chroot(path=chroot_path, out_path=out_path)
Alex Klein1699fab2022-09-08 08:46:06 -0600373 self._gs_context = gs.GSContext(
374 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
375 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400376
Alex Klein1699fab2022-09-08 08:46:06 -0600377 def _Upload(self, local_file, remote_file):
378 """Wrapper around _GsUpload"""
379 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700380
Alex Klein1699fab2022-09-08 08:46:06 -0600381 def _ShouldFilterPackage(self, pkg):
382 if not self._packages:
383 return False
384 cpv = package_info.SplitCPV(pkg["CPV"])
385 self._found_packages.add(cpv.cp)
386 return (
387 cpv.package not in self._packages and cpv.cp not in self._packages
388 )
David James8c846492011-01-25 17:07:29 -0800389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 def _UploadPrebuilt(self, package_path, url_suffix):
391 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800392
Alex Klein1699fab2022-09-08 08:46:06 -0600393 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600394 package_path: The path to the packages dir.
395 url_suffix: The remote subdirectory where we should upload the
396 packages.
Alex Klein1699fab2022-09-08 08:46:06 -0600397 """
398 # Process Packages file, removing duplicates and filtered packages.
399 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
400 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
401 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
402 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
403 unmatched_pkgs = self._packages - self._found_packages
404 if unmatched_pkgs:
405 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800406
Alex Klein1699fab2022-09-08 08:46:06 -0600407 # Write Packages file.
408 pkg_index.header["TTL"] = _BINPKG_TTL
409 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800410
Alex Klein1699fab2022-09-08 08:46:06 -0600411 remote_location = "%s/%s" % (
412 self._upload_location.rstrip("/"),
413 url_suffix,
414 )
415 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800416
Alex Klein1699fab2022-09-08 08:46:06 -0600417 upload_files = GenerateUploadDict(
418 package_path, remote_location, uploads
419 )
420 remote_file = "%s/Packages" % remote_location.rstrip("/")
421 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700422
Alex Klein1699fab2022-09-08 08:46:06 -0600423 # Build list of files to upload. Manually include the dev-only files but
424 # skip them if not present.
425 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
426 if os.path.exists(dev_only):
427 upload_files[dev_only] = "%s/%s" % (
428 remote_location.rstrip("/"),
429 os.path.basename(dev_only),
430 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400431
Alex Klein1699fab2022-09-08 08:46:06 -0600432 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800433
Alex Klein1699fab2022-09-08 08:46:06 -0600434 with tempfile.NamedTemporaryFile(
435 prefix="chromite.upload_prebuilts.index."
436 ) as index:
437 GenerateHtmlIndex(
438 [x[len(remote_location) + 1 :] for x in upload_files.values()],
439 index.name,
440 self._target,
441 self._version,
442 remote_location,
443 )
444 self._Upload(
445 index.name, "%s/index.html" % remote_location.rstrip("/")
446 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400447
Alex Klein1699fab2022-09-08 08:46:06 -0600448 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
449 url = "%s%s/index.html" % (
450 gs.PUBLIC_BASE_HTTPS_URL,
451 remote_location[len(gs.BASE_GS_URL) :],
452 )
453 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400454
Alex Klein1699fab2022-09-08 08:46:06 -0600455 def _UploadSdkTarball(
456 self,
457 board_path,
458 url_suffix,
459 prepackaged,
460 toolchains_overlay_tarballs,
461 toolchains_overlay_upload_path,
462 toolchain_tarballs,
463 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600464 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600465 ):
466 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700467
Alex Klein1699fab2022-09-08 08:46:06 -0600468 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600469 board_path: The path to the board dir.
470 url_suffix: The remote subdirectory where we should upload the
471 packages.
472 prepackaged: If given, a tarball that has been packaged outside of
473 this script and should be used.
474 toolchains_overlay_tarballs: List of toolchains overlay tarball
475 specifications to upload. Items take the form
476 "toolchains_spec:/path/to/tarball".
477 toolchains_overlay_upload_path: Path template under the bucket to
478 place toolchains overlay tarballs.
479 toolchain_tarballs: List of toolchain tarballs to upload.
480 toolchain_upload_path: Path under the bucket to place toolchain
481 tarballs.
482 sync_remote_latest_sdk_file: If True, update the remote latest SDK
483 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600484 """
485 remote_location = "%s/%s" % (
486 self._upload_location.rstrip("/"),
487 url_suffix,
488 )
489 assert remote_location.startswith("gs://")
490 boardname = os.path.basename(board_path.rstrip("/"))
491 # We do not upload non SDK board tarballs,
492 assert boardname == constants.CHROOT_BUILDER_BOARD
493 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200494
Bob Haarman193ab2e2023-07-10 20:02:09 +0000495 # _version consists of a prefix followed by the actual version.
496 # We want the version without the prefix. It starts with the
497 # year, month, and date in YYYY.MM.DD format, so we match that.
498 m = re.match(r"(.*-)?(\d\d\d\d\.\d\d\.\d\d.*)", self._version)
499 assert m, "version does not match format .*YYYY.MM.DD.*"
500 version_str = m[2]
Alex Klein1699fab2022-09-08 08:46:06 -0600501 remote_tarfile = toolchain.GetSdkURL(
502 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
503 )
504 # For SDK, also upload the manifest which is guaranteed to exist
505 # by the builderstage.
506 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
507 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 # Upload SDK toolchains overlays and toolchain tarballs, if given.
510 for tarball_list, upload_path, qualifier_name in (
511 (
512 toolchains_overlay_tarballs,
513 toolchains_overlay_upload_path,
514 "toolchains",
515 ),
516 (toolchain_tarballs, toolchain_upload_path, "target"),
517 ):
518 for tarball_spec in tarball_list:
519 qualifier_val, local_path = tarball_spec.split(":")
520 suburl = upload_path % {qualifier_name: qualifier_val}
521 remote_path = toolchain.GetSdkURL(
522 for_gsutil=True, suburl=suburl
523 )
524 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500525
Alex Klein1699fab2022-09-08 08:46:06 -0600526 # Finally, also update the pointer to the latest SDK on which polling
527 # scripts rely.
Greg Edelston8da51ce2023-03-22 10:40:59 -0600528 if sync_remote_latest_sdk_file:
529 self._UpdateRemoteSdkLatestFile(latest_sdk=version_str)
Greg Edelston64620d12023-02-23 15:29:49 -0700530
531 def _UpdateRemoteSdkLatestFile(
532 self,
533 latest_sdk: Optional[str] = None,
534 latest_sdk_uprev_target: Optional[str] = None,
535 ) -> None:
536 """Update the remote SDK pointer file on GS://.
537
538 The remote file contains multiple key-value pairs. This function can
539 update one or more of them; Nones will retain their existing values.
540
541 Args:
542 latest_sdk: The latest SDK that is tested and ready to be used. If
543 None, then the existing value on GS:// will be retained.
544 latest_sdk_uprev_target: The latest SDK that has been built, which
545 new PUprs can try to test and uprev. If None, then the existing
546 value on GS:// will be retained.
547 """
Greg Edelston119cbe12023-03-29 14:26:52 -0600548 # This would be a noop in dryrun mode -- or worse, it would fail to
549 # parse the remote key-value store after pretending to download it.
550 # Instead of side-stepping errors, return early with descriptive logs.
551 if self._dryrun:
552 logging.debug("Not updating remote SDK latest file in dryrun mode.")
553 if latest_sdk is not None:
554 logging.debug("Would have set LATEST_SDK=%s", latest_sdk)
555 if latest_sdk_uprev_target is not None:
556 logging.debug(
557 "Would have set LATEST_SDK_UPREV_TARGET=%s",
558 latest_sdk_uprev_target,
559 )
560 return
561
Greg Edelston64620d12023-02-23 15:29:49 -0700562 # Get existing values from the remote file.
563 remote_pointerfile = toolchain.GetSdkURL(
564 for_gsutil=True, suburl="cros-sdk-latest.conf"
565 )
566 existing_keyval = self._gs_context.LoadKeyValueStore(
567 remote_pointerfile, acl=self._acl
568 )
Greg Edelston2469ad32023-03-20 13:55:21 -0600569
570 # TODO(b/274196697): When LATEST_SDK_UPREV_TARGET is more reliably found
571 # in the remote latest file, require that key too.
572 for required_key in ("LATEST_SDK",):
573 if required_key not in existing_keyval:
574 raise ValueError(
Alex Kleind7197402023-04-05 13:05:29 -0600575 f"Remote pointer file {remote_pointerfile} missing "
576 f"expected key {required_key}:\n{existing_keyval}"
Greg Edelston2469ad32023-03-20 13:55:21 -0600577 )
Greg Edelston64620d12023-02-23 15:29:49 -0700578
579 # If any values were not specified in args, use the existing values.
580 if latest_sdk is None:
581 latest_sdk = existing_keyval["LATEST_SDK"]
582 if latest_sdk_uprev_target is None:
Greg Edelston2469ad32023-03-20 13:55:21 -0600583 latest_sdk_uprev_target = existing_keyval.get(
584 "LATEST_SDK_UPREV_TARGET", None
585 )
Greg Edelston64620d12023-02-23 15:29:49 -0700586
587 # Write a new local latest file with target values, and upload.
588 new_file_contents = self._CreateRemoteSdkLatestFileContents(
589 latest_sdk, latest_sdk_uprev_target
590 )
591 with osutils.TempDir() as tmpdir:
592 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
593 osutils.WriteFile(local_pointerfile, new_file_contents)
594 self._Upload(local_pointerfile, remote_pointerfile)
595
596 @staticmethod
597 def _CreateRemoteSdkLatestFileContents(
598 latest_sdk: str, latest_sdk_uprev_target: str
599 ) -> str:
600 """Generate file contents for a remote SDK file.
601
602 Args:
603 latest_sdk: The latest SDK that is tested and ready to be used.
604 latest_sdk_uprev_target: The latest SDK that has been built, which
605 new PUprs can try to test and uprev.
606
607 Returns:
608 The contents of a remote SDK latest file containing the given args
609 as a key-value store.
610 """
611 return f"""\
612# The most recent SDK that is tested and ready for use.
613LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700614
615# The most recently built version. New uprev attempts should target this.
616# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700617LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200618
Alex Klein1699fab2022-09-08 08:46:06 -0600619 def _GetTargets(self):
620 """Retuns the list of targets to use."""
621 targets = self._slave_targets[:]
622 if self._target:
623 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700624
Alex Klein1699fab2022-09-08 08:46:06 -0600625 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700626
Alex Klein1699fab2022-09-08 08:46:06 -0600627 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
628 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800629
Alex Klein1699fab2022-09-08 08:46:06 -0600630 This function will sync both the standard host packages, plus the host
631 packages associated with all targets that have been "setup" with the
632 current host's chroot. For instance, if this host has been used to build
633 x86-generic, it will sync the host packages associated with
Alex Kleind7197402023-04-05 13:05:29 -0600634 'i686-pc-linux-gnu'. If this host has also been used to build
635 arm-generic, it will also sync the host packages associated with
Alex Klein1699fab2022-09-08 08:46:06 -0600636 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800637
Alex Klein1699fab2022-09-08 08:46:06 -0600638 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600639 key: The variable key to update in the git file.
640 git_sync: If set, update make.conf of target to reference the latest
641 prebuilt packages generated here.
642 sync_binhost_conf: If set, update binhost config file in
643 chromiumos-overlay for the host.
Alex Klein1699fab2022-09-08 08:46:06 -0600644 """
Alex Kleind7197402023-04-05 13:05:29 -0600645 # Slave boards are listed before the master board so that the master
646 # board takes priority (i.e. x86-generic preflight host prebuilts takes
647 # priority over preflight host prebuilts from other builders.)
Alex Klein1699fab2022-09-08 08:46:06 -0600648 binhost_urls = []
649 for target in self._GetTargets():
650 url_suffix = _REL_HOST_PATH % {
651 "version": self._version,
652 "host_arch": _HOST_ARCH,
653 "target": target,
654 }
655 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800656
Alex Klein1699fab2022-09-08 08:46:06 -0600657 if self._target == target and not self._skip_upload:
658 # Upload prebuilts.
Brian Norris74d1af92023-06-14 11:28:12 -0700659 package_path = self._chroot.full_path(_HOST_PACKAGES_PATH)
Alex Klein1699fab2022-09-08 08:46:06 -0600660 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700661
Alex Klein1699fab2022-09-08 08:46:06 -0600662 # Record URL where prebuilts were uploaded.
663 binhost_urls.append(
664 "%s/%s/"
665 % (
666 self._binhost_base_url.rstrip("/"),
667 packages_url_suffix.rstrip("/"),
668 )
669 )
David Jamese2488642011-11-14 16:15:20 -0800670
Alex Klein1699fab2022-09-08 08:46:06 -0600671 binhost = " ".join(binhost_urls)
672 if git_sync:
673 git_file = os.path.join(
674 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
675 )
Greg Edelston86aea7b2023-02-15 16:50:25 -0700676 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700677 git_file, {key: binhost}, self._report, dryrun=self._dryrun
678 )
Alex Klein1699fab2022-09-08 08:46:06 -0600679 if sync_binhost_conf:
680 binhost_conf = os.path.join(
681 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
682 )
683 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800684
Alex Klein1699fab2022-09-08 08:46:06 -0600685 def SyncBoardPrebuilts(
686 self,
687 key,
688 git_sync,
689 sync_binhost_conf,
690 upload_board_tarball,
691 prepackaged_board,
692 toolchains_overlay_tarballs,
693 toolchains_overlay_upload_path,
694 toolchain_tarballs,
695 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600696 sync_remote_latest_sdk_file: bool,
Alex Klein1699fab2022-09-08 08:46:06 -0600697 ):
698 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800699
Alex Klein1699fab2022-09-08 08:46:06 -0600700 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600701 key: The variable key to update in the git file.
702 git_sync: If set, update make.conf of target to reference the latest
703 prebuilt packages generated here.
704 sync_binhost_conf: If set, update binhost config file in
705 chromiumos-overlay for the current board.
706 upload_board_tarball: Include a tarball of the board in our upload.
707 prepackaged_board: A tarball of the board built outside of this
708 script.
709 toolchains_overlay_tarballs: List of toolchains overlay tarball
710 specifications to upload. Items take the form
711 "toolchains_spec:/path/to/tarball".
712 toolchains_overlay_upload_path: Path template under the bucket to
713 place toolchains overlay tarballs.
714 toolchain_tarballs: A list of toolchain tarballs to upload.
715 toolchain_upload_path: Path under the bucket to place toolchain
716 tarballs.
717 sync_remote_latest_sdk_file: If True, update the remote latest SDK
718 file in Google Storage to point to the newly uploaded SDK.
Alex Klein1699fab2022-09-08 08:46:06 -0600719 """
720 updated_binhosts = set()
721 for target in self._GetTargets():
Brian Norris74d1af92023-06-14 11:28:12 -0700722 board_path = self._chroot.full_path(
723 _BOARD_PATH % {"board": target.board_variant}
Alex Klein1699fab2022-09-08 08:46:06 -0600724 )
725 package_path = os.path.join(board_path, "packages")
726 url_suffix = _REL_BOARD_PATH % {
727 "target": target,
728 "version": self._version,
729 }
730 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700731
Alex Klein1699fab2022-09-08 08:46:06 -0600732 # Process the target board differently if it is the main --board.
733 if self._target == target and not self._skip_upload:
Alex Kleind7197402023-04-05 13:05:29 -0600734 # This strips "chroot" prefix because that is sometimes added as
735 # the --prepend-version argument (e.g. by chromiumos-sdk bot).
Alex Klein1699fab2022-09-08 08:46:06 -0600736 # TODO(build): Clean it up to be less hard-coded.
737 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500738
Alex Klein1699fab2022-09-08 08:46:06 -0600739 # Upload board tarballs in the background.
740 if upload_board_tarball:
741 if toolchain_upload_path:
742 toolchain_upload_path %= {"version": version_str}
743 if toolchains_overlay_upload_path:
744 toolchains_overlay_upload_path %= {
745 "version": version_str
746 }
747 tar_process = multiprocessing.Process(
748 target=self._UploadSdkTarball,
749 args=(
750 board_path,
751 url_suffix,
752 prepackaged_board,
753 toolchains_overlay_tarballs,
754 toolchains_overlay_upload_path,
755 toolchain_tarballs,
756 toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600757 sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -0600758 ),
759 )
760 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700761
Alex Klein1699fab2022-09-08 08:46:06 -0600762 # Upload prebuilts.
763 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 # Make sure we finished uploading the board tarballs.
766 if upload_board_tarball:
767 tar_process.join()
768 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800769
Alex Klein1699fab2022-09-08 08:46:06 -0600770 # Record URL where prebuilts were uploaded.
771 url_value = "%s/%s/" % (
772 self._binhost_base_url.rstrip("/"),
773 packages_url_suffix.rstrip("/"),
774 )
David Jamese2488642011-11-14 16:15:20 -0800775
Alex Klein1699fab2022-09-08 08:46:06 -0600776 if git_sync:
777 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Greg Edelston86aea7b2023-02-15 16:50:25 -0700778 binpkg.UpdateAndSubmitKeyValueFile(
Bob Haarmanc0082602022-09-20 16:12:43 -0700779 git_file,
780 {key: url_value},
781 self._report,
782 dryrun=self._dryrun,
783 )
Matt Tennante8179042013-10-01 15:47:32 -0700784
Alex Klein1699fab2022-09-08 08:46:06 -0600785 if sync_binhost_conf:
786 # Update the binhost configuration file in git.
787 binhost_conf = os.path.join(
788 self._binhost_conf_dir,
789 "target",
790 "%s-%s.conf" % (target, key),
791 )
792 updated_binhosts.add(binhost_conf)
793 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800794
Alex Klein1699fab2022-09-08 08:46:06 -0600795 if sync_binhost_conf:
Alex Kleind7197402023-04-05 13:05:29 -0600796 # Clear all old binhosts. The files must be left empty in case
797 # anybody is referring to them.
Alex Klein1699fab2022-09-08 08:46:06 -0600798 all_binhosts = set(
799 glob.glob(
800 os.path.join(
801 self._binhost_conf_dir, "target", "*-%s.conf" % key
802 )
803 )
804 )
805 for binhost_conf in all_binhosts - updated_binhosts:
806 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800807
David James05bcb2b2011-02-09 09:25:47 -0800808
Mike Nicholsa1414162021-04-22 20:07:22 +0000809class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600810 """Callback that adds a slave board to the list of slave targets."""
811
812 def __call__(self, parser, namespace, values, option_string=None):
813 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800814
815
Mike Nicholsa1414162021-04-22 20:07:22 +0000816class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600817 """Callback that adds a slave profile to the list of slave targets."""
818
819 def __call__(self, parser, namespace, values, option_string=None):
820 if not namespace.slave_targets:
821 parser.error("Must specify --slave-board before --slave-profile")
822 if namespace.slave_targets[-1].profile is not None:
823 parser.error("Cannot specify --slave-profile twice for same board")
824 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800825
826
Greg Edelston325e4412023-05-04 16:22:10 -0600827def ParseOptions(argv) -> Tuple[argparse.Namespace, Optional[BuildTarget]]:
Alex Klein1699fab2022-09-08 08:46:06 -0600828 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700829
Alex Klein1699fab2022-09-08 08:46:06 -0600830 Args:
Alex Kleind7197402023-04-05 13:05:29 -0600831 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400832
Alex Klein1699fab2022-09-08 08:46:06 -0600833 Returns:
Alex Kleind7197402023-04-05 13:05:29 -0600834 A tuple containing a parsed options object and BuildTarget.
835 The target instance is None if no board is specified.
Alex Klein1699fab2022-09-08 08:46:06 -0600836 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500837 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600838 parser.add_argument(
839 "-H",
840 "--binhost-base-url",
841 default=_BINHOST_BASE_URL,
842 help="Base URL to use for binhost in make.conf updates",
843 )
844 parser.add_argument(
845 "--previous-binhost-url",
846 action="append",
847 default=[],
848 help="Previous binhost URL",
849 )
850 parser.add_argument(
851 "-b", "--board", help="Board type that was built on this machine"
852 )
853 parser.add_argument(
854 "-B",
855 "--prepackaged-tarball",
856 type="path",
857 help="Board tarball prebuilt outside of this script.",
858 )
859 parser.add_argument(
860 "--toolchains-overlay-tarball",
861 dest="toolchains_overlay_tarballs",
862 action="append",
863 default=[],
864 help="Toolchains overlay tarball specification to "
865 "upload. Takes the form "
866 '"toolchains_spec:/path/to/tarball".',
867 )
868 parser.add_argument(
869 "--toolchains-overlay-upload-path",
870 default="",
871 help="Path template for uploading toolchains overlays.",
872 )
873 parser.add_argument(
874 "--toolchain-tarball",
875 dest="toolchain_tarballs",
876 action="append",
877 default=[],
878 help="Redistributable toolchain tarball.",
879 )
880 parser.add_argument(
881 "--toolchain-upload-path",
882 default="",
883 help="Path to place toolchain tarballs in the sdk tree.",
884 )
885 parser.add_argument(
886 "--profile", help="Profile that was built on this machine"
887 )
888 parser.add_argument(
889 "--slave-board",
890 default=[],
891 action=_AddSlaveBoardAction,
892 dest="slave_targets",
893 help="Board type that was built on a slave machine. To "
894 "add a profile to this board, use --slave-profile.",
895 )
896 parser.add_argument(
897 "--slave-profile",
898 action=_AddSlaveProfileAction,
899 help="Board profile that was built on a slave machine. "
900 "Applies to previous slave board.",
901 )
902 parser.add_argument(
903 "-p",
904 "--build-path",
905 required=True,
906 help="Path to the directory containing the chroot",
907 )
908 parser.add_argument(
909 "--chroot",
910 help="Path where the chroot is located. "
911 "(Default: {build_path}/chroot)",
912 )
913 parser.add_argument(
Brian Norris74d1af92023-06-14 11:28:12 -0700914 "--out-dir",
915 help="Path where the SDK output directory is located. "
916 "(Default: {build_path}/out)",
917 )
918 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700919 "--output",
920 type=Path,
921 help="Write a JSON report to the specified file. "
922 "(Default is not to write the report.)",
923 )
924 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600925 "--packages",
926 action="append",
927 default=[],
928 help="Only include the specified packages. "
929 "(Default is to include all packages.)",
930 )
931 parser.add_argument(
932 "-s",
933 "--sync-host",
934 default=False,
935 action="store_true",
936 help="Sync host prebuilts",
937 )
938 parser.add_argument(
939 "-g",
940 "--git-sync",
941 default=False,
942 action="store_true",
943 help="Enable git version sync (This commits to a repo.) "
944 "This is used by full builders to commit directly "
945 "to board overlays.",
946 )
Greg Edelston8da51ce2023-03-22 10:40:59 -0600947 parser.add_argument(
948 "--sync-remote-latest-sdk-file",
949 action="store_true",
Greg Edelston325e4412023-05-04 16:22:10 -0600950 default=True,
Greg Edelston8da51ce2023-03-22 10:40:59 -0600951 help="Sync the remote latest SDK file on GS://. (Default)",
952 )
953 parser.add_argument(
954 "--no-sync-remote-latest-sdk-file",
955 dest="sync_remote_latest_sdk_file",
956 action="store_false",
957 help="Skip syncing the remote latest SDK file on GS://.",
958 )
Alex Klein1699fab2022-09-08 08:46:06 -0600959 parser.add_argument("-u", "--upload", help="Upload location")
960 parser.add_argument(
961 "-V",
962 "--prepend-version",
963 help="Add an identifier to the front of the version",
964 )
965 parser.add_argument(
966 "-f",
967 "--filters",
968 action="store_true",
969 default=False,
970 help="Turn on filtering of private ebuild packages",
971 )
972 parser.add_argument(
973 "-k",
974 "--key",
975 default="PORTAGE_BINHOST",
976 help="Key to update in make.conf / binhost.conf",
977 )
978 parser.add_argument("--set-version", help="Specify the version string")
979 parser.add_argument(
980 "--sync-binhost-conf",
981 default=False,
982 action="store_true",
983 help="Update binhost.conf in chromiumos-overlay or "
984 "chromeos-overlay. Commit the changes, but don't "
985 "push them. This is used for preflight binhosts.",
986 )
987 parser.add_argument(
988 "--binhost-conf-dir",
989 help="Directory to commit binhost config with " "--sync-binhost-conf.",
990 )
991 parser.add_argument(
992 "-P",
993 "--private",
994 action="store_true",
995 default=False,
996 help="Mark gs:// uploads as private.",
997 )
998 parser.add_argument(
999 "--skip-upload",
1000 action="store_true",
1001 default=False,
1002 help="Skip upload step.",
1003 )
1004 parser.add_argument(
1005 "--upload-board-tarball",
1006 action="store_true",
1007 default=False,
1008 help="Upload board tarball to Google Storage.",
1009 )
David James8c846492011-01-25 17:07:29 -08001010
Alex Klein1699fab2022-09-08 08:46:06 -06001011 options = parser.parse_args(argv)
1012 if not options.upload and not options.skip_upload:
1013 parser.error("you need to provide an upload location using -u")
1014 if not options.set_version and options.skip_upload:
1015 parser.error(
1016 "If you are using --skip-upload, you must specify a "
1017 "version number using --set-version."
1018 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001019
Alex Klein1699fab2022-09-08 08:46:06 -06001020 target = None
1021 if options.board:
1022 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -07001023
Alex Klein1699fab2022-09-08 08:46:06 -06001024 if target in options.slave_targets:
1025 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001026
Alex Klein1699fab2022-09-08 08:46:06 -06001027 if len(set(options.slave_targets)) != len(options.slave_targets):
1028 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001029
Alex Klein1699fab2022-09-08 08:46:06 -06001030 if options.slave_targets and options.git_sync:
1031 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001032
Alex Klein1699fab2022-09-08 08:46:06 -06001033 if (
1034 options.upload_board_tarball
1035 and options.skip_upload
1036 and options.board == "amd64-host"
1037 ):
1038 parser.error(
1039 "--skip-upload is not compatible with "
1040 "--upload-board-tarball and --board=amd64-host"
1041 )
David James8fa34ea2011-04-15 13:00:20 -07001042
Alex Klein1699fab2022-09-08 08:46:06 -06001043 if (
1044 options.upload_board_tarball
1045 and not options.skip_upload
1046 and not options.upload.startswith("gs://")
1047 ):
1048 parser.error(
1049 "--upload-board-tarball only works with gs:// URLs.\n"
1050 "--upload must be a gs:// URL."
1051 )
David James8fa34ea2011-04-15 13:00:20 -07001052
Alex Klein1699fab2022-09-08 08:46:06 -06001053 if options.upload_board_tarball and options.prepackaged_tarball is None:
1054 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001055
Alex Klein1699fab2022-09-08 08:46:06 -06001056 if options.private:
1057 if options.sync_host:
1058 parser.error(
1059 "--private and --sync-host/-s cannot be specified "
1060 "together; we do not support private host prebuilts"
1061 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001062
Alex Klein1699fab2022-09-08 08:46:06 -06001063 if not options.upload or not options.upload.startswith("gs://"):
1064 parser.error(
1065 "--private is only valid for gs:// URLs; "
1066 "--upload must be a gs:// URL."
1067 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001068
Alex Klein1699fab2022-09-08 08:46:06 -06001069 if options.binhost_base_url != _BINHOST_BASE_URL:
1070 parser.error(
1071 "when using --private the --binhost-base-url "
1072 "is automatically derived."
1073 )
David James27fa7d12011-06-29 17:24:14 -07001074
Alex Klein1699fab2022-09-08 08:46:06 -06001075 if options.sync_binhost_conf and not options.binhost_conf_dir:
1076 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001077
Alex Klein1699fab2022-09-08 08:46:06 -06001078 if (
1079 options.toolchains_overlay_tarballs
1080 and not options.toolchains_overlay_upload_path
1081 ):
1082 parser.error(
1083 "--toolchains-overlay-tarball requires "
1084 "--toolchains-overlay-upload-path"
1085 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001086
Alex Klein1699fab2022-09-08 08:46:06 -06001087 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001088
Mike Frysingercc838832014-05-24 13:10:30 -04001089
Mike Frysinger86509232014-05-24 13:18:37 -04001090def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001091 # We accumulate information about actions taken and report it at the end
1092 # if asked to do so. Currently, this only records CL creation, which
1093 # is the only thing we need for now.
1094 report = {}
1095
Alex Klein1699fab2022-09-08 08:46:06 -06001096 # Set umask so that files created as root are readable.
1097 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001100
Alex Klein1699fab2022-09-08 08:46:06 -06001101 # Calculate a list of Packages index files to compare against. Whenever we
1102 # upload a package, we check to make sure it's not already stored in one of
1103 # the packages files we uploaded. This list of packages files might contain
1104 # both board and host packages.
1105 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001106
Alex Klein1699fab2022-09-08 08:46:06 -06001107 if options.set_version:
1108 version = options.set_version
1109 else:
1110 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001111
Alex Klein1699fab2022-09-08 08:46:06 -06001112 if options.prepend_version:
1113 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001114
Alex Klein1699fab2022-09-08 08:46:06 -06001115 acl = "public-read"
1116 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001117
Alex Klein1699fab2022-09-08 08:46:06 -06001118 if options.private:
1119 binhost_base_url = options.upload
1120 if target:
1121 acl = portage_util.FindOverlayFile(
1122 _GOOGLESTORAGE_GSUTIL_FILE,
1123 board=target.board_variant,
1124 buildroot=options.build_path,
1125 )
1126 if acl is None:
1127 cros_build_lib.Die(
1128 "No Google Storage ACL file %s found in %s overlay.",
1129 _GOOGLESTORAGE_GSUTIL_FILE,
1130 target.board_variant,
1131 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001132
Alex Klein1699fab2022-09-08 08:46:06 -06001133 binhost_conf_dir = None
1134 if options.binhost_conf_dir:
1135 binhost_conf_dir = os.path.join(
1136 options.build_path, options.binhost_conf_dir
1137 )
David Jamesb26b9312014-12-15 11:26:46 -08001138
Alex Klein1699fab2022-09-08 08:46:06 -06001139 uploader = PrebuiltUploader(
1140 options.upload,
1141 acl,
1142 binhost_base_url,
1143 pkg_indexes,
1144 options.build_path,
1145 options.packages,
1146 options.skip_upload,
1147 binhost_conf_dir,
1148 options.dryrun,
1149 target,
1150 options.slave_targets,
1151 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001152 report,
Brian Norris74d1af92023-06-14 11:28:12 -07001153 chroot=options.chroot,
1154 out_dir=options.out_dir,
Alex Klein1699fab2022-09-08 08:46:06 -06001155 )
David Jamesc0f158a2011-02-22 16:07:29 -08001156
Alex Klein1699fab2022-09-08 08:46:06 -06001157 if options.sync_host:
1158 uploader.SyncHostPrebuilts(
1159 options.key, options.git_sync, options.sync_binhost_conf
1160 )
David James8c846492011-01-25 17:07:29 -08001161
Alex Klein1699fab2022-09-08 08:46:06 -06001162 if options.board or options.slave_targets:
1163 uploader.SyncBoardPrebuilts(
1164 options.key,
1165 options.git_sync,
1166 options.sync_binhost_conf,
1167 options.upload_board_tarball,
1168 options.prepackaged_tarball,
1169 options.toolchains_overlay_tarballs,
1170 options.toolchains_overlay_upload_path,
1171 options.toolchain_tarballs,
1172 options.toolchain_upload_path,
Greg Edelston8da51ce2023-03-22 10:40:59 -06001173 options.sync_remote_latest_sdk_file,
Alex Klein1699fab2022-09-08 08:46:06 -06001174 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001175
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001176 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001177 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001178 pformat.json(report, fp=f)