blob: 27d9dd536ef7924e241811a4c11d4d7eb39f62b0 [file] [log] [blame]
Mike Frysingerf1ba7ad2022-09-12 05:42:57 -04001# Copyright 2012 The ChromiumOS Authors
David James8c846492011-01-25 17:07:29 -08002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
Brian Harringaf019fb2012-05-10 15:06:13 -07005"""This script is used to upload host prebuilts as well as board BINHOSTS.
David James8c846492011-01-25 17:07:29 -08006
David James015af872012-06-19 15:24:36 -07007Prebuilts are uploaded using gsutil to Google Storage. After these prebuilts
8are successfully uploaded, a file is updated with the proper BINHOST version.
David James8c846492011-01-25 17:07:29 -08009
10To read more about prebuilts/binhost binary packages please refer to:
David James015af872012-06-19 15:24:36 -070011http://goto/chromeos-prebuilts
David James8c846492011-01-25 17:07:29 -080012
13Example of uploading prebuilt amd64 host files to Google Storage:
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000014upload_prebuilts -p /b/cbuild/build -s -u gs://chromeos-prebuilt
David James8c846492011-01-25 17:07:29 -080015
16Example of uploading x86-dogfood binhosts to Google Storage:
Mike Nicholsd0acc7f2021-05-21 17:18:24 +000017upload_prebuilts -b x86-dogfood -p /b/cbuild/build/ -u gs://chromeos-prebuilt -g
David James8c846492011-01-25 17:07:29 -080018"""
19
Mike Frysingerb87bb782015-06-04 02:46:50 -040020import argparse
Chris Sosa1dc96132012-05-11 15:40:50 -070021import datetime
Mike Frysinger540883b2014-05-24 13:46:16 -040022import functools
David Jamesb26b9312014-12-15 11:26:46 -080023import glob
Chris McDonaldb55b7032021-06-17 16:41:32 -060024import logging
Chris Sosa1dc96132012-05-11 15:40:50 -070025import multiprocessing
Chris Sosa1dc96132012-05-11 15:40:50 -070026import os
Bob Haarmanc0082602022-09-20 16:12:43 -070027from pathlib import Path
Mike Frysinger212e4292014-05-24 15:15:44 -040028import tempfile
Greg Edelston64620d12023-02-23 15:29:49 -070029from typing import Optional
Chris Sosa1dc96132012-05-11 15:40:50 -070030
Chris McDonaldb55b7032021-06-17 16:41:32 -060031from chromite.cbuildbot import cbuildbot_alerts
David James14e97772014-06-04 18:44:49 -070032from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080033from chromite.lib import binpkg
Mike Frysinger86509232014-05-24 13:18:37 -040034from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060035from chromite.lib import constants
Chris Sosa1dc96132012-05-11 15:40:50 -070036from chromite.lib import cros_build_lib
Julio Hurtadofad17992021-05-20 21:24:24 +000037from chromite.lib import gerrit
Mike Frysinger807d8282022-04-28 22:45:17 -040038from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070039from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070040from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080041from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070042from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050043from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060044from chromite.lib.parser import package_info
Bob Haarmanc0082602022-09-20 16:12:43 -070045from chromite.utils import pformat
Chris Sosa1dc96132012-05-11 15:40:50 -070046
Mike Frysinger72b7cf92020-04-19 06:00:51 -040047
David James015af872012-06-19 15:24:36 -070048# How many times to retry uploads.
49_RETRIES = 10
50
51# Multiplier for how long to sleep (in seconds) between retries; will delay
52# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
53_SLEEP_TIME = 60
54
David James5ab67e32014-10-24 08:19:59 -070055# The length of time (in seconds) that Portage should wait before refetching
56# binpkgs from the same binhost. We don't ever modify binhosts, so this should
57# be something big.
58_BINPKG_TTL = 60 * 60 * 24 * 365
59
Alex Klein1699fab2022-09-08 08:46:06 -060060_HOST_PACKAGES_PATH = "var/lib/portage/pkgs"
61_CATEGORIES_PATH = "chroot/etc/portage/categories"
62_PYM_PATH = "chroot/usr/lib/portage/pym"
63_HOST_ARCH = "amd64"
64_BOARD_PATH = "chroot/build/%(board)s"
65_REL_BOARD_PATH = "board/%(target)s/%(version)s"
66_REL_HOST_PATH = "host/%(host_arch)s/%(target)s/%(version)s"
David James8c846492011-01-25 17:07:29 -080067# Private overlays to look at for builds to filter
68# relative to build path
Alex Klein1699fab2022-09-08 08:46:06 -060069_PRIVATE_OVERLAY_DIR = "src/private-overlays"
70_GOOGLESTORAGE_GSUTIL_FILE = "googlestorage_acl.txt"
71_BINHOST_BASE_URL = "gs://chromeos-prebuilt"
72_PREBUILT_BASE_DIR = "src/third_party/chromiumos-overlay/chromeos/config/"
David James8c846492011-01-25 17:07:29 -080073# Created in the event of new host targets becoming available
Alex Klein1699fab2022-09-08 08:46:06 -060074_PREBUILT_MAKE_CONF = {
75 "amd64": os.path.join(_PREBUILT_BASE_DIR, "make.conf.amd64-host")
76}
David James8c846492011-01-25 17:07:29 -080077
78
David James4058b0d2011-12-08 21:24:50 -080079class BuildTarget(object):
Alex Klein1699fab2022-09-08 08:46:06 -060080 """A board/variant/profile tuple."""
David James4058b0d2011-12-08 21:24:50 -080081
Alex Klein1699fab2022-09-08 08:46:06 -060082 def __init__(self, board_variant, profile=None):
83 self.board_variant = board_variant
84 self.board, _, self.variant = board_variant.partition("_")
85 self.profile = profile
David James4058b0d2011-12-08 21:24:50 -080086
Alex Klein1699fab2022-09-08 08:46:06 -060087 def __str__(self):
88 if self.profile:
89 return "%s_%s" % (self.board_variant, self.profile)
90 else:
91 return self.board_variant
David James4058b0d2011-12-08 21:24:50 -080092
Alex Klein1699fab2022-09-08 08:46:06 -060093 def __eq__(self, other):
94 return str(other) == str(self)
David James4058b0d2011-12-08 21:24:50 -080095
Alex Klein1699fab2022-09-08 08:46:06 -060096 def __hash__(self):
97 return hash(str(self))
David James4058b0d2011-12-08 21:24:50 -080098
99
Alex Klein1699fab2022-09-08 08:46:06 -0600100def UpdateLocalFile(filename, value, key="PORTAGE_BINHOST"):
101 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500102
Alex Klein1699fab2022-09-08 08:46:06 -0600103 File format:
104 key="value"
105 Note quotes are added automatically
David James8c846492011-01-25 17:07:29 -0800106
Alex Klein1699fab2022-09-08 08:46:06 -0600107 Args:
108 filename: Name of file to modify.
109 value: Value to write with the key.
110 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800111
Alex Klein1699fab2022-09-08 08:46:06 -0600112 Returns:
113 True if changes were made to the file.
114 """
Sergey Frolov09280f12022-06-06 17:47:32 -0600115
Alex Klein1699fab2022-09-08 08:46:06 -0600116 keyval_str = "%(key)s=%(value)s"
Sergey Frolov09280f12022-06-06 17:47:32 -0600117
Alex Klein1699fab2022-09-08 08:46:06 -0600118 # Add quotes around the value, if missing.
119 if not value or value[0] != '"' or value[-1] != '"':
120 value = f'"{value}"'
Sergey Frolov09280f12022-06-06 17:47:32 -0600121
Alex Klein1699fab2022-09-08 08:46:06 -0600122 # new_lines is the content to be used to overwrite/create the config file
123 # at the end of this function.
124 made_changes = False
125 new_lines = []
Sergey Frolov09280f12022-06-06 17:47:32 -0600126
Alex Klein1699fab2022-09-08 08:46:06 -0600127 # Read current lines.
128 try:
129 current_lines = osutils.ReadFile(filename).splitlines()
130 except FileNotFoundError:
131 current_lines = []
132 print(f"Creating new file {filename}")
Sergey Frolov09280f12022-06-06 17:47:32 -0600133
Alex Klein1699fab2022-09-08 08:46:06 -0600134 # Scan current lines, copy all vars to new_lines, change the line with |key|.
135 found = False
136 for line in current_lines:
137 # Strip newlines from end of line. We already add newlines below.
138 line = line.rstrip("\n")
139 if len(line.split("=")) != 2:
140 # Skip any line that doesn't fit key=val.
141 new_lines.append(line)
142 continue
143 file_var, file_val = line.split("=")
144 if file_var == key:
145 found = True
146 print(f"Updating {file_var}={file_val} to {key}={value}")
147 made_changes |= file_val != value
148 new_lines.append(keyval_str % {"key": key, "value": value})
149 else:
150 new_lines.append(keyval_str % {"key": file_var, "value": file_val})
151 if not found:
152 print(f"Adding new variable {key}={value}")
153 made_changes = True
154 new_lines.append(keyval_str % {"key": key, "value": value})
Jack Neusf30f6722022-06-09 16:23:49 +0000155
Alex Klein1699fab2022-09-08 08:46:06 -0600156 # Write out new file.
157 osutils.WriteFile(filename, "\n".join(new_lines) + "\n")
158 return made_changes
David James8c846492011-01-25 17:07:29 -0800159
160
Bob Haarmanf6b79502022-10-03 14:01:00 -0700161def RevGitFile(filename, data, report=None, dryrun=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600162 """Update and push the git file.
David James8c846492011-01-25 17:07:29 -0800163
Alex Klein1699fab2022-09-08 08:46:06 -0600164 Args:
165 filename: file to modify that is in a git repo already
166 data: A dict of key/values to update in |filename|
Bob Haarmanc0082602022-09-20 16:12:43 -0700167 report: Dict in which to collect information to report to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600168 dryrun: If True, do not actually commit the change.
169 """
Bob Haarmanf6b79502022-10-03 14:01:00 -0700170 if report is None:
171 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600172 prebuilt_branch = "prebuilt_branch"
173 cwd = os.path.abspath(os.path.dirname(filename))
174 remote_name = git.RunGit(cwd, ["remote"]).stdout.strip()
175 gerrit_helper = gerrit.GetGerritHelper(remote_name)
176 remote_url = git.RunGit(
177 cwd, ["config", "--get", f"remote.{remote_name}.url"]
178 ).stdout.strip()
179 description = "%s: updating %s" % (
180 os.path.basename(filename),
181 ", ".join(data.keys()),
182 )
183 # UpdateLocalFile will print out the keys/values for us.
184 print("Revving git file %s" % filename)
185 git.CreatePushBranch(prebuilt_branch, cwd)
186 for key, value in data.items():
187 UpdateLocalFile(filename, value, key)
188 git.RunGit(cwd, ["add", filename])
189 git.RunGit(cwd, ["commit", "-m", description])
Julio Hurtado2d4817d2021-04-29 16:03:58 +0000190
Alex Klein1699fab2022-09-08 08:46:06 -0600191 tracking_info = git.GetTrackingBranch(
192 cwd, prebuilt_branch, for_push=True, for_checkout=False
193 )
194 gpatch = gerrit_helper.CreateGerritPatch(
195 cwd, remote_url, ref=tracking_info.ref, notify="NONE"
196 )
Bob Haarmanc0082602022-09-20 16:12:43 -0700197 report.setdefault("created_cls", []).append(gpatch.PatchLink())
Alex Klein1699fab2022-09-08 08:46:06 -0600198 gerrit_helper.SetReview(
199 gpatch, labels={"Bot-Commit": 1}, dryrun=dryrun, notify="NONE"
200 )
201 gerrit_helper.SubmitChange(gpatch, dryrun=dryrun, notify="NONE")
David James8c846492011-01-25 17:07:29 -0800202
203
204def GetVersion():
Alex Klein1699fab2022-09-08 08:46:06 -0600205 """Get the version to put in LATEST and update the git version with."""
206 return datetime.datetime.now().strftime("%Y.%m.%d.%H%M%S")
David James8c846492011-01-25 17:07:29 -0800207
208
Mike Frysinger540883b2014-05-24 13:46:16 -0400209def _GsUpload(gs_context, acl, local_file, remote_file):
Alex Klein1699fab2022-09-08 08:46:06 -0600210 """Upload to GS bucket.
David James8c846492011-01-25 17:07:29 -0800211
Alex Klein1699fab2022-09-08 08:46:06 -0600212 Args:
213 gs_context: A lib.gs.GSContext instance.
214 acl: The ACL to use for uploading the file.
215 local_file: The local file to be uploaded.
216 remote_file: The remote location to upload to.
217 """
218 CANNED_ACLS = [
219 "public-read",
220 "private",
221 "bucket-owner-read",
222 "authenticated-read",
223 "bucket-owner-full-control",
224 "public-read-write",
225 ]
226 if acl in CANNED_ACLS:
227 gs_context.Copy(local_file, remote_file, acl=acl)
Gabe Black40169e62014-06-17 15:23:47 -0700228 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600229 # For private uploads we assume that the overlay board is set up properly
230 # and a googlestore_acl.xml is present. Otherwise, this script errors.
231 # We set version=0 here to ensure that the ACL is set only once (see
232 # http://b/15883752#comment54).
233 try:
234 gs_context.Copy(local_file, remote_file, version=0)
235 except gs.GSContextPreconditionFailed as ex:
236 # If we received a GSContextPreconditionFailed error, we know that the
237 # file exists now, but we don't know whether our specific update
238 # succeeded. See http://b/15883752#comment62
239 logging.warning(
240 "Assuming upload succeeded despite PreconditionFailed errors: %s",
241 ex,
242 )
243
244 if acl.endswith(".xml"):
245 # Apply the passed in ACL xml file to the uploaded object.
246 gs_context.SetACL(remote_file, acl=acl)
247 else:
248 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700249
Mike Frysingercc838832014-05-24 13:10:30 -0400250
Mike Frysinger540883b2014-05-24 13:46:16 -0400251def RemoteUpload(gs_context, acl, files, pool=10):
Alex Klein1699fab2022-09-08 08:46:06 -0600252 """Upload to google storage.
David James8c846492011-01-25 17:07:29 -0800253
Alex Klein1699fab2022-09-08 08:46:06 -0600254 Create a pool of process and call _GsUpload with the proper arguments.
David James8c846492011-01-25 17:07:29 -0800255
Alex Klein1699fab2022-09-08 08:46:06 -0600256 Args:
257 gs_context: A lib.gs.GSContext instance.
258 acl: The canned acl used for uploading. acl can be one of: "public-read",
259 "public-read-write", "authenticated-read", "bucket-owner-read",
260 "bucket-owner-full-control", or "private".
261 files: dictionary with keys to local files and values to remote path.
262 pool: integer of maximum proesses to have at the same time.
David James8c846492011-01-25 17:07:29 -0800263
Alex Klein1699fab2022-09-08 08:46:06 -0600264 Returns:
265 Return a set of tuple arguments of the failed uploads
266 """
267 upload = functools.partial(_GsUpload, gs_context, acl)
268 tasks = [[key, value] for key, value in files.items()]
269 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800270
271
272def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
Alex Klein1699fab2022-09-08 08:46:06 -0600273 """Build a dictionary of local remote file key pairs to upload.
David James8c846492011-01-25 17:07:29 -0800274
Alex Klein1699fab2022-09-08 08:46:06 -0600275 Args:
276 base_local_path: The base path to the files on the local hard drive.
277 base_remote_path: The base path to the remote paths.
278 pkgs: The packages to upload.
David James8c846492011-01-25 17:07:29 -0800279
Alex Klein1699fab2022-09-08 08:46:06 -0600280 Returns:
281 Returns a dictionary of local_path/remote_path pairs
282 """
283 upload_files = {}
284 for pkg in pkgs:
285 suffix = pkg["CPV"] + ".tbz2"
286 local_path = os.path.join(base_local_path, suffix)
287 assert os.path.exists(local_path), "%s does not exist" % local_path
288 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800289
Alex Klein1699fab2022-09-08 08:46:06 -0600290 if pkg.get("DEBUG_SYMBOLS") == "yes":
291 debugsuffix = pkg["CPV"] + ".debug.tbz2"
292 local_path = os.path.join(base_local_path, debugsuffix)
293 assert os.path.exists(local_path)
294 upload_files[local_path] = os.path.join(
295 base_remote_path, debugsuffix
296 )
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800297
Alex Klein1699fab2022-09-08 08:46:06 -0600298 return upload_files
David James8c846492011-01-25 17:07:29 -0800299
Mike Frysingercc838832014-05-24 13:10:30 -0400300
Peter Mayo950e41a2014-02-06 21:07:33 +0000301def GetBoardOverlay(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600302 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500303
Alex Klein1699fab2022-09-08 08:46:06 -0600304 Args:
305 build_path: The path to the root of the build directory
306 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500307
Alex Klein1699fab2022-09-08 08:46:06 -0600308 Returns:
309 The last overlay configured for the given board as a string.
310 """
311 board = target.board_variant
312 overlays = portage_util.FindOverlays(
313 constants.BOTH_OVERLAYS, board, buildroot=build_path
314 )
315 # We only care about the last entry.
316 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800317
318
319def DeterminePrebuiltConfFile(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600320 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
David James8c846492011-01-25 17:07:29 -0800321
Alex Klein1699fab2022-09-08 08:46:06 -0600322 Args:
323 build_path: The path to the root of the build directory
324 target: String representation of the board. This includes host and board
325 targets
David James8c846492011-01-25 17:07:29 -0800326
Alex Klein1699fab2022-09-08 08:46:06 -0600327 Returns:
328 A string path to a prebuilt.conf file to be updated.
329 """
330 if _HOST_ARCH == target:
331 # We are host.
332 # Without more examples of hosts this is a kludge for now.
333 # TODO(Scottz): as new host targets come online expand this to
334 # work more like boards.
335 make_path = _PREBUILT_MAKE_CONF[target]
336 else:
337 # We are a board
338 board = GetBoardOverlay(build_path, target)
339 make_path = os.path.join(board, "prebuilt.conf")
David James8c846492011-01-25 17:07:29 -0800340
Alex Klein1699fab2022-09-08 08:46:06 -0600341 return make_path
David James8c846492011-01-25 17:07:29 -0800342
343
344def UpdateBinhostConfFile(path, key, value):
Alex Klein1699fab2022-09-08 08:46:06 -0600345 """Update binhost config file file with key=value.
David James8c846492011-01-25 17:07:29 -0800346
Alex Klein1699fab2022-09-08 08:46:06 -0600347 Args:
348 path: Filename to update.
349 key: Key to update.
350 value: New value for key.
351 """
352 cwd, filename = os.path.split(os.path.abspath(path))
353 osutils.SafeMakedirs(cwd)
354 if not git.GetCurrentBranch(cwd):
355 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
356 osutils.WriteFile(path, "", mode="a")
357 if UpdateLocalFile(path, value, key):
358 desc = "%s: %s %s" % (
359 filename,
360 "updating" if value else "clearing",
361 key,
362 )
363 git.AddPath(path)
364 git.Commit(cwd, desc)
365
David James8c846492011-01-25 17:07:29 -0800366
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800367def GenerateHtmlIndex(files, index, board, version, remote_location):
Alex Klein1699fab2022-09-08 08:46:06 -0600368 """Given the list of |files|, generate an index.html at |index|.
Mike Frysinger212e4292014-05-24 15:15:44 -0400369
Alex Klein1699fab2022-09-08 08:46:06 -0600370 Args:
371 files: The list of files to link to.
372 index: The path to the html index.
373 board: Name of the board this index is for.
374 version: Build version this index is for.
375 remote_location: Remote gs location prebuilts are uploaded to.
376 """
377 title = "Package Prebuilt Index: %s / %s" % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400378
Alex Klein1699fab2022-09-08 08:46:06 -0600379 files = files + [
380 ".|Google Storage Index",
381 "..|",
382 ]
383 commands.GenerateHtmlIndex(
384 index, files, title=title, url_base=gs.GsUrlToHttp(remote_location)
385 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400386
387
David Jamesce093af2011-02-23 15:21:58 -0800388def _GrabAllRemotePackageIndexes(binhost_urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600389 """Grab all of the packages files associated with a list of binhost_urls.
David James05bcb2b2011-02-09 09:25:47 -0800390
Alex Klein1699fab2022-09-08 08:46:06 -0600391 Args:
392 binhost_urls: The URLs for the directories containing the Packages files we
393 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800394
Alex Klein1699fab2022-09-08 08:46:06 -0600395 Returns:
396 A list of PackageIndex objects.
397 """
398 pkg_indexes = []
399 for url in binhost_urls:
400 pkg_index = binpkg.GrabRemotePackageIndex(url)
401 if pkg_index:
402 pkg_indexes.append(pkg_index)
403 return pkg_indexes
David James05bcb2b2011-02-09 09:25:47 -0800404
405
David Jamesc0f158a2011-02-22 16:07:29 -0800406class PrebuiltUploader(object):
Alex Klein1699fab2022-09-08 08:46:06 -0600407 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800408
Alex Klein1699fab2022-09-08 08:46:06 -0600409 def __init__(
410 self,
411 upload_location,
412 acl,
413 binhost_base_url,
414 pkg_indexes,
415 build_path,
416 packages,
417 skip_upload,
418 binhost_conf_dir,
419 dryrun,
420 target,
421 slave_targets,
422 version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700423 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600424 chroot=None,
425 ):
426 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800427
Alex Klein1699fab2022-09-08 08:46:06 -0600428 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800429
Alex Klein1699fab2022-09-08 08:46:06 -0600430 Args:
431 upload_location: The upload location.
432 acl: The canned acl used for uploading to Google Storage. acl can be one
433 of: "public-read", "public-read-write", "authenticated-read",
434 "bucket-owner-read", "bucket-owner-full-control", "project-private",
435 or "private" (see "gsutil help acls"). If we are not uploading to
436 Google Storage, this parameter is unused.
437 binhost_base_url: The URL used for downloading the prebuilts.
438 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
439 uploading duplicate files, we just link to the old files.
440 build_path: The path to the directory containing the chroot.
441 packages: Packages to upload.
442 skip_upload: Don't actually upload the tarballs.
443 binhost_conf_dir: Directory where to store binhost.conf files.
444 dryrun: Don't push or upload prebuilts.
445 target: BuildTarget managed by this builder.
446 slave_targets: List of BuildTargets managed by slave builders.
447 version: A unique string, intended to be included in the upload path,
448 which identifies the version number of the uploaded prebuilts.
Bob Haarmanc0082602022-09-20 16:12:43 -0700449 report: Dict in which to collect information to report to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600450 chroot: Path to the chroot containing the prebuilts.
451 """
452 self._upload_location = upload_location
453 self._acl = acl
454 self._binhost_base_url = binhost_base_url
455 self._pkg_indexes = pkg_indexes
456 self._build_path = build_path
457 self._packages = set(packages)
458 self._found_packages = set()
459 self._skip_upload = skip_upload
460 self._binhost_conf_dir = binhost_conf_dir
461 self._dryrun = dryrun
462 self._target = target
463 self._slave_targets = slave_targets
464 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700465 self._report = report
Alex Klein1699fab2022-09-08 08:46:06 -0600466 self._chroot = chroot or os.path.join(
467 build_path, constants.DEFAULT_CHROOT_DIR
468 )
469 self._gs_context = gs.GSContext(
470 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
471 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400472
Alex Klein1699fab2022-09-08 08:46:06 -0600473 def _Upload(self, local_file, remote_file):
474 """Wrapper around _GsUpload"""
475 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700476
Alex Klein1699fab2022-09-08 08:46:06 -0600477 def _ShouldFilterPackage(self, pkg):
478 if not self._packages:
479 return False
480 cpv = package_info.SplitCPV(pkg["CPV"])
481 self._found_packages.add(cpv.cp)
482 return (
483 cpv.package not in self._packages and cpv.cp not in self._packages
484 )
David James8c846492011-01-25 17:07:29 -0800485
Alex Klein1699fab2022-09-08 08:46:06 -0600486 def _UploadPrebuilt(self, package_path, url_suffix):
487 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800488
Alex Klein1699fab2022-09-08 08:46:06 -0600489 Args:
490 package_path: The path to the packages dir.
491 url_suffix: The remote subdirectory where we should upload the packages.
492 """
493 # Process Packages file, removing duplicates and filtered packages.
494 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
495 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
496 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
497 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
498 unmatched_pkgs = self._packages - self._found_packages
499 if unmatched_pkgs:
500 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800501
Alex Klein1699fab2022-09-08 08:46:06 -0600502 # Write Packages file.
503 pkg_index.header["TTL"] = _BINPKG_TTL
504 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800505
Alex Klein1699fab2022-09-08 08:46:06 -0600506 remote_location = "%s/%s" % (
507 self._upload_location.rstrip("/"),
508 url_suffix,
509 )
510 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800511
Alex Klein1699fab2022-09-08 08:46:06 -0600512 upload_files = GenerateUploadDict(
513 package_path, remote_location, uploads
514 )
515 remote_file = "%s/Packages" % remote_location.rstrip("/")
516 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700517
Alex Klein1699fab2022-09-08 08:46:06 -0600518 # Build list of files to upload. Manually include the dev-only files but
519 # skip them if not present.
520 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
521 if os.path.exists(dev_only):
522 upload_files[dev_only] = "%s/%s" % (
523 remote_location.rstrip("/"),
524 os.path.basename(dev_only),
525 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400526
Alex Klein1699fab2022-09-08 08:46:06 -0600527 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800528
Alex Klein1699fab2022-09-08 08:46:06 -0600529 with tempfile.NamedTemporaryFile(
530 prefix="chromite.upload_prebuilts.index."
531 ) as index:
532 GenerateHtmlIndex(
533 [x[len(remote_location) + 1 :] for x in upload_files.values()],
534 index.name,
535 self._target,
536 self._version,
537 remote_location,
538 )
539 self._Upload(
540 index.name, "%s/index.html" % remote_location.rstrip("/")
541 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400542
Alex Klein1699fab2022-09-08 08:46:06 -0600543 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
544 url = "%s%s/index.html" % (
545 gs.PUBLIC_BASE_HTTPS_URL,
546 remote_location[len(gs.BASE_GS_URL) :],
547 )
548 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400549
Alex Klein1699fab2022-09-08 08:46:06 -0600550 def _UploadSdkTarball(
551 self,
552 board_path,
553 url_suffix,
554 prepackaged,
555 toolchains_overlay_tarballs,
556 toolchains_overlay_upload_path,
557 toolchain_tarballs,
558 toolchain_upload_path,
559 ):
560 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700561
Alex Klein1699fab2022-09-08 08:46:06 -0600562 Args:
563 board_path: The path to the board dir.
564 url_suffix: The remote subdirectory where we should upload the packages.
565 prepackaged: If given, a tarball that has been packaged outside of this
566 script and should be used.
567 toolchains_overlay_tarballs: List of toolchains overlay tarball
568 specifications to upload. Items take the form
569 "toolchains_spec:/path/to/tarball".
570 toolchains_overlay_upload_path: Path template under the bucket to place
571 toolchains overlay tarballs.
572 toolchain_tarballs: List of toolchain tarballs to upload.
573 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
574 """
575 remote_location = "%s/%s" % (
576 self._upload_location.rstrip("/"),
577 url_suffix,
578 )
579 assert remote_location.startswith("gs://")
580 boardname = os.path.basename(board_path.rstrip("/"))
581 # We do not upload non SDK board tarballs,
582 assert boardname == constants.CHROOT_BUILDER_BOARD
583 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200584
Alex Klein1699fab2022-09-08 08:46:06 -0600585 version_str = self._version[len("chroot-") :]
586 remote_tarfile = toolchain.GetSdkURL(
587 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
588 )
589 # For SDK, also upload the manifest which is guaranteed to exist
590 # by the builderstage.
591 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
592 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200593
Alex Klein1699fab2022-09-08 08:46:06 -0600594 # Upload SDK toolchains overlays and toolchain tarballs, if given.
595 for tarball_list, upload_path, qualifier_name in (
596 (
597 toolchains_overlay_tarballs,
598 toolchains_overlay_upload_path,
599 "toolchains",
600 ),
601 (toolchain_tarballs, toolchain_upload_path, "target"),
602 ):
603 for tarball_spec in tarball_list:
604 qualifier_val, local_path = tarball_spec.split(":")
605 suburl = upload_path % {qualifier_name: qualifier_val}
606 remote_path = toolchain.GetSdkURL(
607 for_gsutil=True, suburl=suburl
608 )
609 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500610
Alex Klein1699fab2022-09-08 08:46:06 -0600611 # Finally, also update the pointer to the latest SDK on which polling
612 # scripts rely.
Greg Edelston64620d12023-02-23 15:29:49 -0700613 self._UpdateRemoteSdkLatestFile(latest_sdk=version_str)
614
615 def _UpdateRemoteSdkLatestFile(
616 self,
617 latest_sdk: Optional[str] = None,
618 latest_sdk_uprev_target: Optional[str] = None,
619 ) -> None:
620 """Update the remote SDK pointer file on GS://.
621
622 The remote file contains multiple key-value pairs. This function can
623 update one or more of them; Nones will retain their existing values.
624
625 Args:
626 latest_sdk: The latest SDK that is tested and ready to be used. If
627 None, then the existing value on GS:// will be retained.
628 latest_sdk_uprev_target: The latest SDK that has been built, which
629 new PUprs can try to test and uprev. If None, then the existing
630 value on GS:// will be retained.
631 """
632 # Get existing values from the remote file.
633 remote_pointerfile = toolchain.GetSdkURL(
634 for_gsutil=True, suburl="cros-sdk-latest.conf"
635 )
636 existing_keyval = self._gs_context.LoadKeyValueStore(
637 remote_pointerfile, acl=self._acl
638 )
639 if not all(
640 key in existing_keyval
641 for key in ("LATEST_SDK", "LATEST_SDK_UPREV_TARGET")
642 ):
643 raise ValueError(
644 f"Remote pointerfile missing expected keys:\n{existing_keyval}"
Alex Klein1699fab2022-09-08 08:46:06 -0600645 )
Greg Edelston64620d12023-02-23 15:29:49 -0700646
647 # If any values were not specified in args, use the existing values.
648 if latest_sdk is None:
649 latest_sdk = existing_keyval["LATEST_SDK"]
650 if latest_sdk_uprev_target is None:
651 latest_sdk_uprev_target = existing_keyval["LATEST_SDK_UPREV_TARGET"]
652
653 # Write a new local latest file with target values, and upload.
654 new_file_contents = self._CreateRemoteSdkLatestFileContents(
655 latest_sdk, latest_sdk_uprev_target
656 )
657 with osutils.TempDir() as tmpdir:
658 local_pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
659 osutils.WriteFile(local_pointerfile, new_file_contents)
660 self._Upload(local_pointerfile, remote_pointerfile)
661
662 @staticmethod
663 def _CreateRemoteSdkLatestFileContents(
664 latest_sdk: str, latest_sdk_uprev_target: str
665 ) -> str:
666 """Generate file contents for a remote SDK file.
667
668 Args:
669 latest_sdk: The latest SDK that is tested and ready to be used.
670 latest_sdk_uprev_target: The latest SDK that has been built, which
671 new PUprs can try to test and uprev.
672
673 Returns:
674 The contents of a remote SDK latest file containing the given args
675 as a key-value store.
676 """
677 return f"""\
678# The most recent SDK that is tested and ready for use.
679LATEST_SDK=\"{latest_sdk}\"
Greg Edelstonf3916f92023-02-22 15:49:38 -0700680
681# The most recently built version. New uprev attempts should target this.
682# Warning: This version may not be tested yet.
Greg Edelston64620d12023-02-23 15:29:49 -0700683LATEST_SDK_UPREV_TARGET=\"{latest_sdk_uprev_target}\""""
Zdenek Behan33a34112012-09-10 21:07:51 +0200684
Alex Klein1699fab2022-09-08 08:46:06 -0600685 def _GetTargets(self):
686 """Retuns the list of targets to use."""
687 targets = self._slave_targets[:]
688 if self._target:
689 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700690
Alex Klein1699fab2022-09-08 08:46:06 -0600691 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700692
Alex Klein1699fab2022-09-08 08:46:06 -0600693 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
694 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800695
Alex Klein1699fab2022-09-08 08:46:06 -0600696 This function will sync both the standard host packages, plus the host
697 packages associated with all targets that have been "setup" with the
698 current host's chroot. For instance, if this host has been used to build
699 x86-generic, it will sync the host packages associated with
700 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
701 it will also sync the host packages associated with
702 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800703
Alex Klein1699fab2022-09-08 08:46:06 -0600704 Args:
705 key: The variable key to update in the git file.
706 git_sync: If set, update make.conf of target to reference the latest
707 prebuilt packages generated here.
708 sync_binhost_conf: If set, update binhost config file in
709 chromiumos-overlay for the host.
710 """
711 # Slave boards are listed before the master board so that the master board
712 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
713 # over preflight host prebuilts from other builders.)
714 binhost_urls = []
715 for target in self._GetTargets():
716 url_suffix = _REL_HOST_PATH % {
717 "version": self._version,
718 "host_arch": _HOST_ARCH,
719 "target": target,
720 }
721 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800722
Alex Klein1699fab2022-09-08 08:46:06 -0600723 if self._target == target and not self._skip_upload:
724 # Upload prebuilts.
725 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
726 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700727
Alex Klein1699fab2022-09-08 08:46:06 -0600728 # Record URL where prebuilts were uploaded.
729 binhost_urls.append(
730 "%s/%s/"
731 % (
732 self._binhost_base_url.rstrip("/"),
733 packages_url_suffix.rstrip("/"),
734 )
735 )
David Jamese2488642011-11-14 16:15:20 -0800736
Alex Klein1699fab2022-09-08 08:46:06 -0600737 binhost = " ".join(binhost_urls)
738 if git_sync:
739 git_file = os.path.join(
740 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
741 )
Bob Haarmanc0082602022-09-20 16:12:43 -0700742 RevGitFile(
743 git_file, {key: binhost}, self._report, dryrun=self._dryrun
744 )
Alex Klein1699fab2022-09-08 08:46:06 -0600745 if sync_binhost_conf:
746 binhost_conf = os.path.join(
747 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
748 )
749 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800750
Alex Klein1699fab2022-09-08 08:46:06 -0600751 def SyncBoardPrebuilts(
752 self,
753 key,
754 git_sync,
755 sync_binhost_conf,
756 upload_board_tarball,
757 prepackaged_board,
758 toolchains_overlay_tarballs,
759 toolchains_overlay_upload_path,
760 toolchain_tarballs,
761 toolchain_upload_path,
762 ):
763 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 Args:
766 key: The variable key to update in the git file.
767 git_sync: If set, update make.conf of target to reference the latest
768 prebuilt packages generated here.
769 sync_binhost_conf: If set, update binhost config file in
770 chromiumos-overlay for the current board.
771 upload_board_tarball: Include a tarball of the board in our upload.
772 prepackaged_board: A tarball of the board built outside of this script.
773 toolchains_overlay_tarballs: List of toolchains overlay tarball
774 specifications to upload. Items take the form
775 "toolchains_spec:/path/to/tarball".
776 toolchains_overlay_upload_path: Path template under the bucket to place
777 toolchains overlay tarballs.
778 toolchain_tarballs: A list of toolchain tarballs to upload.
779 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
780 """
781 updated_binhosts = set()
782 for target in self._GetTargets():
783 board_path = os.path.join(
784 self._build_path, _BOARD_PATH % {"board": target.board_variant}
785 )
786 package_path = os.path.join(board_path, "packages")
787 url_suffix = _REL_BOARD_PATH % {
788 "target": target,
789 "version": self._version,
790 }
791 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700792
Alex Klein1699fab2022-09-08 08:46:06 -0600793 # Process the target board differently if it is the main --board.
794 if self._target == target and not self._skip_upload:
795 # This strips "chroot" prefix because that is sometimes added as the
796 # --prepend-version argument (e.g. by chromiumos-sdk bot).
797 # TODO(build): Clean it up to be less hard-coded.
798 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500799
Alex Klein1699fab2022-09-08 08:46:06 -0600800 # Upload board tarballs in the background.
801 if upload_board_tarball:
802 if toolchain_upload_path:
803 toolchain_upload_path %= {"version": version_str}
804 if toolchains_overlay_upload_path:
805 toolchains_overlay_upload_path %= {
806 "version": version_str
807 }
808 tar_process = multiprocessing.Process(
809 target=self._UploadSdkTarball,
810 args=(
811 board_path,
812 url_suffix,
813 prepackaged_board,
814 toolchains_overlay_tarballs,
815 toolchains_overlay_upload_path,
816 toolchain_tarballs,
817 toolchain_upload_path,
818 ),
819 )
820 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700821
Alex Klein1699fab2022-09-08 08:46:06 -0600822 # Upload prebuilts.
823 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700824
Alex Klein1699fab2022-09-08 08:46:06 -0600825 # Make sure we finished uploading the board tarballs.
826 if upload_board_tarball:
827 tar_process.join()
828 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800829
Alex Klein1699fab2022-09-08 08:46:06 -0600830 # Record URL where prebuilts were uploaded.
831 url_value = "%s/%s/" % (
832 self._binhost_base_url.rstrip("/"),
833 packages_url_suffix.rstrip("/"),
834 )
David Jamese2488642011-11-14 16:15:20 -0800835
Alex Klein1699fab2022-09-08 08:46:06 -0600836 if git_sync:
837 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Bob Haarmanc0082602022-09-20 16:12:43 -0700838 RevGitFile(
839 git_file,
840 {key: url_value},
841 self._report,
842 dryrun=self._dryrun,
843 )
Matt Tennante8179042013-10-01 15:47:32 -0700844
Alex Klein1699fab2022-09-08 08:46:06 -0600845 if sync_binhost_conf:
846 # Update the binhost configuration file in git.
847 binhost_conf = os.path.join(
848 self._binhost_conf_dir,
849 "target",
850 "%s-%s.conf" % (target, key),
851 )
852 updated_binhosts.add(binhost_conf)
853 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800854
Alex Klein1699fab2022-09-08 08:46:06 -0600855 if sync_binhost_conf:
856 # Clear all old binhosts. The files must be left empty in case anybody
857 # is referring to them.
858 all_binhosts = set(
859 glob.glob(
860 os.path.join(
861 self._binhost_conf_dir, "target", "*-%s.conf" % key
862 )
863 )
864 )
865 for binhost_conf in all_binhosts - updated_binhosts:
866 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800867
David James05bcb2b2011-02-09 09:25:47 -0800868
Mike Nicholsa1414162021-04-22 20:07:22 +0000869class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600870 """Callback that adds a slave board to the list of slave targets."""
871
872 def __call__(self, parser, namespace, values, option_string=None):
873 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800874
875
Mike Nicholsa1414162021-04-22 20:07:22 +0000876class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600877 """Callback that adds a slave profile to the list of slave targets."""
878
879 def __call__(self, parser, namespace, values, option_string=None):
880 if not namespace.slave_targets:
881 parser.error("Must specify --slave-board before --slave-profile")
882 if namespace.slave_targets[-1].profile is not None:
883 parser.error("Cannot specify --slave-profile twice for same board")
884 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800885
886
Mike Frysinger86509232014-05-24 13:18:37 -0400887def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600888 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700889
Alex Klein1699fab2022-09-08 08:46:06 -0600890 Args:
891 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400892
Alex Klein1699fab2022-09-08 08:46:06 -0600893 Returns:
894 A tuple containing a parsed options object and BuildTarget.
895 The target instance is None if no board is specified.
896 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500897 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600898 parser.add_argument(
899 "-H",
900 "--binhost-base-url",
901 default=_BINHOST_BASE_URL,
902 help="Base URL to use for binhost in make.conf updates",
903 )
904 parser.add_argument(
905 "--previous-binhost-url",
906 action="append",
907 default=[],
908 help="Previous binhost URL",
909 )
910 parser.add_argument(
911 "-b", "--board", help="Board type that was built on this machine"
912 )
913 parser.add_argument(
914 "-B",
915 "--prepackaged-tarball",
916 type="path",
917 help="Board tarball prebuilt outside of this script.",
918 )
919 parser.add_argument(
920 "--toolchains-overlay-tarball",
921 dest="toolchains_overlay_tarballs",
922 action="append",
923 default=[],
924 help="Toolchains overlay tarball specification to "
925 "upload. Takes the form "
926 '"toolchains_spec:/path/to/tarball".',
927 )
928 parser.add_argument(
929 "--toolchains-overlay-upload-path",
930 default="",
931 help="Path template for uploading toolchains overlays.",
932 )
933 parser.add_argument(
934 "--toolchain-tarball",
935 dest="toolchain_tarballs",
936 action="append",
937 default=[],
938 help="Redistributable toolchain tarball.",
939 )
940 parser.add_argument(
941 "--toolchain-upload-path",
942 default="",
943 help="Path to place toolchain tarballs in the sdk tree.",
944 )
945 parser.add_argument(
946 "--profile", help="Profile that was built on this machine"
947 )
948 parser.add_argument(
949 "--slave-board",
950 default=[],
951 action=_AddSlaveBoardAction,
952 dest="slave_targets",
953 help="Board type that was built on a slave machine. To "
954 "add a profile to this board, use --slave-profile.",
955 )
956 parser.add_argument(
957 "--slave-profile",
958 action=_AddSlaveProfileAction,
959 help="Board profile that was built on a slave machine. "
960 "Applies to previous slave board.",
961 )
962 parser.add_argument(
963 "-p",
964 "--build-path",
965 required=True,
966 help="Path to the directory containing the chroot",
967 )
968 parser.add_argument(
969 "--chroot",
970 help="Path where the chroot is located. "
971 "(Default: {build_path}/chroot)",
972 )
973 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700974 "--output",
975 type=Path,
976 help="Write a JSON report to the specified file. "
977 "(Default is not to write the report.)",
978 )
979 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600980 "--packages",
981 action="append",
982 default=[],
983 help="Only include the specified packages. "
984 "(Default is to include all packages.)",
985 )
986 parser.add_argument(
987 "-s",
988 "--sync-host",
989 default=False,
990 action="store_true",
991 help="Sync host prebuilts",
992 )
993 parser.add_argument(
994 "-g",
995 "--git-sync",
996 default=False,
997 action="store_true",
998 help="Enable git version sync (This commits to a repo.) "
999 "This is used by full builders to commit directly "
1000 "to board overlays.",
1001 )
1002 parser.add_argument("-u", "--upload", help="Upload location")
1003 parser.add_argument(
1004 "-V",
1005 "--prepend-version",
1006 help="Add an identifier to the front of the version",
1007 )
1008 parser.add_argument(
1009 "-f",
1010 "--filters",
1011 action="store_true",
1012 default=False,
1013 help="Turn on filtering of private ebuild packages",
1014 )
1015 parser.add_argument(
1016 "-k",
1017 "--key",
1018 default="PORTAGE_BINHOST",
1019 help="Key to update in make.conf / binhost.conf",
1020 )
1021 parser.add_argument("--set-version", help="Specify the version string")
1022 parser.add_argument(
1023 "--sync-binhost-conf",
1024 default=False,
1025 action="store_true",
1026 help="Update binhost.conf in chromiumos-overlay or "
1027 "chromeos-overlay. Commit the changes, but don't "
1028 "push them. This is used for preflight binhosts.",
1029 )
1030 parser.add_argument(
1031 "--binhost-conf-dir",
1032 help="Directory to commit binhost config with " "--sync-binhost-conf.",
1033 )
1034 parser.add_argument(
1035 "-P",
1036 "--private",
1037 action="store_true",
1038 default=False,
1039 help="Mark gs:// uploads as private.",
1040 )
1041 parser.add_argument(
1042 "--skip-upload",
1043 action="store_true",
1044 default=False,
1045 help="Skip upload step.",
1046 )
1047 parser.add_argument(
1048 "--upload-board-tarball",
1049 action="store_true",
1050 default=False,
1051 help="Upload board tarball to Google Storage.",
1052 )
David James8c846492011-01-25 17:07:29 -08001053
Alex Klein1699fab2022-09-08 08:46:06 -06001054 options = parser.parse_args(argv)
1055 if not options.upload and not options.skip_upload:
1056 parser.error("you need to provide an upload location using -u")
1057 if not options.set_version and options.skip_upload:
1058 parser.error(
1059 "If you are using --skip-upload, you must specify a "
1060 "version number using --set-version."
1061 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001062
Alex Klein1699fab2022-09-08 08:46:06 -06001063 target = None
1064 if options.board:
1065 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -07001066
Alex Klein1699fab2022-09-08 08:46:06 -06001067 if target in options.slave_targets:
1068 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001069
Alex Klein1699fab2022-09-08 08:46:06 -06001070 if len(set(options.slave_targets)) != len(options.slave_targets):
1071 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001072
Alex Klein1699fab2022-09-08 08:46:06 -06001073 if options.slave_targets and options.git_sync:
1074 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001075
Alex Klein1699fab2022-09-08 08:46:06 -06001076 if (
1077 options.upload_board_tarball
1078 and options.skip_upload
1079 and options.board == "amd64-host"
1080 ):
1081 parser.error(
1082 "--skip-upload is not compatible with "
1083 "--upload-board-tarball and --board=amd64-host"
1084 )
David James8fa34ea2011-04-15 13:00:20 -07001085
Alex Klein1699fab2022-09-08 08:46:06 -06001086 if (
1087 options.upload_board_tarball
1088 and not options.skip_upload
1089 and not options.upload.startswith("gs://")
1090 ):
1091 parser.error(
1092 "--upload-board-tarball only works with gs:// URLs.\n"
1093 "--upload must be a gs:// URL."
1094 )
David James8fa34ea2011-04-15 13:00:20 -07001095
Alex Klein1699fab2022-09-08 08:46:06 -06001096 if options.upload_board_tarball and options.prepackaged_tarball is None:
1097 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 if options.private:
1100 if options.sync_host:
1101 parser.error(
1102 "--private and --sync-host/-s cannot be specified "
1103 "together; we do not support private host prebuilts"
1104 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001105
Alex Klein1699fab2022-09-08 08:46:06 -06001106 if not options.upload or not options.upload.startswith("gs://"):
1107 parser.error(
1108 "--private is only valid for gs:// URLs; "
1109 "--upload must be a gs:// URL."
1110 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001111
Alex Klein1699fab2022-09-08 08:46:06 -06001112 if options.binhost_base_url != _BINHOST_BASE_URL:
1113 parser.error(
1114 "when using --private the --binhost-base-url "
1115 "is automatically derived."
1116 )
David James27fa7d12011-06-29 17:24:14 -07001117
Alex Klein1699fab2022-09-08 08:46:06 -06001118 if options.sync_binhost_conf and not options.binhost_conf_dir:
1119 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001120
Alex Klein1699fab2022-09-08 08:46:06 -06001121 if (
1122 options.toolchains_overlay_tarballs
1123 and not options.toolchains_overlay_upload_path
1124 ):
1125 parser.error(
1126 "--toolchains-overlay-tarball requires "
1127 "--toolchains-overlay-upload-path"
1128 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001129
Alex Klein1699fab2022-09-08 08:46:06 -06001130 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001131
Mike Frysingercc838832014-05-24 13:10:30 -04001132
Mike Frysinger86509232014-05-24 13:18:37 -04001133def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001134 # We accumulate information about actions taken and report it at the end
1135 # if asked to do so. Currently, this only records CL creation, which
1136 # is the only thing we need for now.
1137 report = {}
1138
Alex Klein1699fab2022-09-08 08:46:06 -06001139 # Set umask so that files created as root are readable.
1140 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001141
Alex Klein1699fab2022-09-08 08:46:06 -06001142 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001143
Alex Klein1699fab2022-09-08 08:46:06 -06001144 # Calculate a list of Packages index files to compare against. Whenever we
1145 # upload a package, we check to make sure it's not already stored in one of
1146 # the packages files we uploaded. This list of packages files might contain
1147 # both board and host packages.
1148 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001149
Alex Klein1699fab2022-09-08 08:46:06 -06001150 if options.set_version:
1151 version = options.set_version
1152 else:
1153 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001154
Alex Klein1699fab2022-09-08 08:46:06 -06001155 if options.prepend_version:
1156 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001157
Alex Klein1699fab2022-09-08 08:46:06 -06001158 acl = "public-read"
1159 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001160
Alex Klein1699fab2022-09-08 08:46:06 -06001161 if options.private:
1162 binhost_base_url = options.upload
1163 if target:
1164 acl = portage_util.FindOverlayFile(
1165 _GOOGLESTORAGE_GSUTIL_FILE,
1166 board=target.board_variant,
1167 buildroot=options.build_path,
1168 )
1169 if acl is None:
1170 cros_build_lib.Die(
1171 "No Google Storage ACL file %s found in %s overlay.",
1172 _GOOGLESTORAGE_GSUTIL_FILE,
1173 target.board_variant,
1174 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001175
Alex Klein1699fab2022-09-08 08:46:06 -06001176 binhost_conf_dir = None
1177 if options.binhost_conf_dir:
1178 binhost_conf_dir = os.path.join(
1179 options.build_path, options.binhost_conf_dir
1180 )
David Jamesb26b9312014-12-15 11:26:46 -08001181
Alex Klein1699fab2022-09-08 08:46:06 -06001182 uploader = PrebuiltUploader(
1183 options.upload,
1184 acl,
1185 binhost_base_url,
1186 pkg_indexes,
1187 options.build_path,
1188 options.packages,
1189 options.skip_upload,
1190 binhost_conf_dir,
1191 options.dryrun,
1192 target,
1193 options.slave_targets,
1194 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001195 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001196 options.chroot,
1197 )
David Jamesc0f158a2011-02-22 16:07:29 -08001198
Alex Klein1699fab2022-09-08 08:46:06 -06001199 if options.sync_host:
1200 uploader.SyncHostPrebuilts(
1201 options.key, options.git_sync, options.sync_binhost_conf
1202 )
David James8c846492011-01-25 17:07:29 -08001203
Alex Klein1699fab2022-09-08 08:46:06 -06001204 if options.board or options.slave_targets:
1205 uploader.SyncBoardPrebuilts(
1206 options.key,
1207 options.git_sync,
1208 options.sync_binhost_conf,
1209 options.upload_board_tarball,
1210 options.prepackaged_tarball,
1211 options.toolchains_overlay_tarballs,
1212 options.toolchains_overlay_upload_path,
1213 options.toolchain_tarballs,
1214 options.toolchain_upload_path,
1215 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001216
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001217 if options.output:
Mike Frysinger31fdddd2023-02-24 15:50:55 -05001218 with open(options.output, "w", encoding="utf-8") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001219 pformat.json(report, fp=f)