blob: aa561f300da9295907d77666ffc73e8c68130819 [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
Chris Sosa1dc96132012-05-11 15:40:50 -070029
Chris McDonaldb55b7032021-06-17 16:41:32 -060030from chromite.cbuildbot import cbuildbot_alerts
David James14e97772014-06-04 18:44:49 -070031from chromite.cbuildbot import commands
David James6450a0a2012-12-04 07:59:53 -080032from chromite.lib import binpkg
Mike Frysinger86509232014-05-24 13:18:37 -040033from chromite.lib import commandline
Chris McDonaldb55b7032021-06-17 16:41:32 -060034from chromite.lib import constants
Chris Sosa1dc96132012-05-11 15:40:50 -070035from chromite.lib import cros_build_lib
Julio Hurtadofad17992021-05-20 21:24:24 +000036from chromite.lib import gerrit
Mike Frysinger807d8282022-04-28 22:45:17 -040037from chromite.lib import git
Brian Harring7904b482012-08-08 02:54:12 -070038from chromite.lib import gs
Brian Harringaf019fb2012-05-10 15:06:13 -070039from chromite.lib import osutils
David James6450a0a2012-12-04 07:59:53 -080040from chromite.lib import parallel
Alex Deymo075c2292014-09-04 18:31:50 -070041from chromite.lib import portage_util
Mike Frysinger8e727a32013-01-16 16:57:53 -050042from chromite.lib import toolchain
Alex Klein18a60af2020-06-11 12:08:47 -060043from chromite.lib.parser import package_info
Bob Haarmanc0082602022-09-20 16:12:43 -070044from chromite.utils import pformat
Chris Sosa1dc96132012-05-11 15:40:50 -070045
Mike Frysinger72b7cf92020-04-19 06:00:51 -040046
David James015af872012-06-19 15:24:36 -070047# How many times to retry uploads.
48_RETRIES = 10
49
50# Multiplier for how long to sleep (in seconds) between retries; will delay
51# (1*sleep) the first time, then (2*sleep), continuing via attempt * sleep.
52_SLEEP_TIME = 60
53
David James5ab67e32014-10-24 08:19:59 -070054# The length of time (in seconds) that Portage should wait before refetching
55# binpkgs from the same binhost. We don't ever modify binhosts, so this should
56# be something big.
57_BINPKG_TTL = 60 * 60 * 24 * 365
58
Alex Klein1699fab2022-09-08 08:46:06 -060059_HOST_PACKAGES_PATH = "var/lib/portage/pkgs"
60_CATEGORIES_PATH = "chroot/etc/portage/categories"
61_PYM_PATH = "chroot/usr/lib/portage/pym"
62_HOST_ARCH = "amd64"
63_BOARD_PATH = "chroot/build/%(board)s"
64_REL_BOARD_PATH = "board/%(target)s/%(version)s"
65_REL_HOST_PATH = "host/%(host_arch)s/%(target)s/%(version)s"
David James8c846492011-01-25 17:07:29 -080066# Private overlays to look at for builds to filter
67# relative to build path
Alex Klein1699fab2022-09-08 08:46:06 -060068_PRIVATE_OVERLAY_DIR = "src/private-overlays"
69_GOOGLESTORAGE_GSUTIL_FILE = "googlestorage_acl.txt"
70_BINHOST_BASE_URL = "gs://chromeos-prebuilt"
71_PREBUILT_BASE_DIR = "src/third_party/chromiumos-overlay/chromeos/config/"
David James8c846492011-01-25 17:07:29 -080072# Created in the event of new host targets becoming available
Alex Klein1699fab2022-09-08 08:46:06 -060073_PREBUILT_MAKE_CONF = {
74 "amd64": os.path.join(_PREBUILT_BASE_DIR, "make.conf.amd64-host")
75}
David James8c846492011-01-25 17:07:29 -080076
77
David James4058b0d2011-12-08 21:24:50 -080078class BuildTarget(object):
Alex Klein1699fab2022-09-08 08:46:06 -060079 """A board/variant/profile tuple."""
David James4058b0d2011-12-08 21:24:50 -080080
Alex Klein1699fab2022-09-08 08:46:06 -060081 def __init__(self, board_variant, profile=None):
82 self.board_variant = board_variant
83 self.board, _, self.variant = board_variant.partition("_")
84 self.profile = profile
David James4058b0d2011-12-08 21:24:50 -080085
Alex Klein1699fab2022-09-08 08:46:06 -060086 def __str__(self):
87 if self.profile:
88 return "%s_%s" % (self.board_variant, self.profile)
89 else:
90 return self.board_variant
David James4058b0d2011-12-08 21:24:50 -080091
Alex Klein1699fab2022-09-08 08:46:06 -060092 def __eq__(self, other):
93 return str(other) == str(self)
David James4058b0d2011-12-08 21:24:50 -080094
Alex Klein1699fab2022-09-08 08:46:06 -060095 def __hash__(self):
96 return hash(str(self))
David James4058b0d2011-12-08 21:24:50 -080097
98
Alex Klein1699fab2022-09-08 08:46:06 -060099def UpdateLocalFile(filename, value, key="PORTAGE_BINHOST"):
100 """Update the key in file with the value passed.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500101
Alex Klein1699fab2022-09-08 08:46:06 -0600102 File format:
103 key="value"
104 Note quotes are added automatically
David James8c846492011-01-25 17:07:29 -0800105
Alex Klein1699fab2022-09-08 08:46:06 -0600106 Args:
107 filename: Name of file to modify.
108 value: Value to write with the key.
109 key: The variable key to update. (Default: PORTAGE_BINHOST)
David Jamesb26b9312014-12-15 11:26:46 -0800110
Alex Klein1699fab2022-09-08 08:46:06 -0600111 Returns:
112 True if changes were made to the file.
113 """
Sergey Frolov09280f12022-06-06 17:47:32 -0600114
Alex Klein1699fab2022-09-08 08:46:06 -0600115 keyval_str = "%(key)s=%(value)s"
Sergey Frolov09280f12022-06-06 17:47:32 -0600116
Alex Klein1699fab2022-09-08 08:46:06 -0600117 # Add quotes around the value, if missing.
118 if not value or value[0] != '"' or value[-1] != '"':
119 value = f'"{value}"'
Sergey Frolov09280f12022-06-06 17:47:32 -0600120
Alex Klein1699fab2022-09-08 08:46:06 -0600121 # new_lines is the content to be used to overwrite/create the config file
122 # at the end of this function.
123 made_changes = False
124 new_lines = []
Sergey Frolov09280f12022-06-06 17:47:32 -0600125
Alex Klein1699fab2022-09-08 08:46:06 -0600126 # Read current lines.
127 try:
128 current_lines = osutils.ReadFile(filename).splitlines()
129 except FileNotFoundError:
130 current_lines = []
131 print(f"Creating new file {filename}")
Sergey Frolov09280f12022-06-06 17:47:32 -0600132
Alex Klein1699fab2022-09-08 08:46:06 -0600133 # Scan current lines, copy all vars to new_lines, change the line with |key|.
134 found = False
135 for line in current_lines:
136 # Strip newlines from end of line. We already add newlines below.
137 line = line.rstrip("\n")
138 if len(line.split("=")) != 2:
139 # Skip any line that doesn't fit key=val.
140 new_lines.append(line)
141 continue
142 file_var, file_val = line.split("=")
143 if file_var == key:
144 found = True
145 print(f"Updating {file_var}={file_val} to {key}={value}")
146 made_changes |= file_val != value
147 new_lines.append(keyval_str % {"key": key, "value": value})
148 else:
149 new_lines.append(keyval_str % {"key": file_var, "value": file_val})
150 if not found:
151 print(f"Adding new variable {key}={value}")
152 made_changes = True
153 new_lines.append(keyval_str % {"key": key, "value": value})
Jack Neusf30f6722022-06-09 16:23:49 +0000154
Alex Klein1699fab2022-09-08 08:46:06 -0600155 # Write out new file.
156 osutils.WriteFile(filename, "\n".join(new_lines) + "\n")
157 return made_changes
David James8c846492011-01-25 17:07:29 -0800158
159
Bob Haarmanf6b79502022-10-03 14:01:00 -0700160def RevGitFile(filename, data, report=None, dryrun=False):
Alex Klein1699fab2022-09-08 08:46:06 -0600161 """Update and push the git file.
David James8c846492011-01-25 17:07:29 -0800162
Alex Klein1699fab2022-09-08 08:46:06 -0600163 Args:
164 filename: file to modify that is in a git repo already
165 data: A dict of key/values to update in |filename|
Bob Haarmanc0082602022-09-20 16:12:43 -0700166 report: Dict in which to collect information to report to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600167 dryrun: If True, do not actually commit the change.
168 """
Bob Haarmanf6b79502022-10-03 14:01:00 -0700169 if report is None:
170 report = {}
Alex Klein1699fab2022-09-08 08:46:06 -0600171 prebuilt_branch = "prebuilt_branch"
172 cwd = os.path.abspath(os.path.dirname(filename))
173 remote_name = git.RunGit(cwd, ["remote"]).stdout.strip()
174 gerrit_helper = gerrit.GetGerritHelper(remote_name)
175 remote_url = git.RunGit(
176 cwd, ["config", "--get", f"remote.{remote_name}.url"]
177 ).stdout.strip()
178 description = "%s: updating %s" % (
179 os.path.basename(filename),
180 ", ".join(data.keys()),
181 )
182 # UpdateLocalFile will print out the keys/values for us.
183 print("Revving git file %s" % filename)
184 git.CreatePushBranch(prebuilt_branch, cwd)
185 for key, value in data.items():
186 UpdateLocalFile(filename, value, key)
187 git.RunGit(cwd, ["add", filename])
188 git.RunGit(cwd, ["commit", "-m", description])
Julio Hurtado2d4817d2021-04-29 16:03:58 +0000189
Alex Klein1699fab2022-09-08 08:46:06 -0600190 tracking_info = git.GetTrackingBranch(
191 cwd, prebuilt_branch, for_push=True, for_checkout=False
192 )
193 gpatch = gerrit_helper.CreateGerritPatch(
194 cwd, remote_url, ref=tracking_info.ref, notify="NONE"
195 )
Bob Haarmanc0082602022-09-20 16:12:43 -0700196 report.setdefault("created_cls", []).append(gpatch.PatchLink())
Alex Klein1699fab2022-09-08 08:46:06 -0600197 gerrit_helper.SetReview(
198 gpatch, labels={"Bot-Commit": 1}, dryrun=dryrun, notify="NONE"
199 )
200 gerrit_helper.SubmitChange(gpatch, dryrun=dryrun, notify="NONE")
David James8c846492011-01-25 17:07:29 -0800201
202
203def GetVersion():
Alex Klein1699fab2022-09-08 08:46:06 -0600204 """Get the version to put in LATEST and update the git version with."""
205 return datetime.datetime.now().strftime("%Y.%m.%d.%H%M%S")
David James8c846492011-01-25 17:07:29 -0800206
207
Mike Frysinger540883b2014-05-24 13:46:16 -0400208def _GsUpload(gs_context, acl, local_file, remote_file):
Alex Klein1699fab2022-09-08 08:46:06 -0600209 """Upload to GS bucket.
David James8c846492011-01-25 17:07:29 -0800210
Alex Klein1699fab2022-09-08 08:46:06 -0600211 Args:
212 gs_context: A lib.gs.GSContext instance.
213 acl: The ACL to use for uploading the file.
214 local_file: The local file to be uploaded.
215 remote_file: The remote location to upload to.
216 """
217 CANNED_ACLS = [
218 "public-read",
219 "private",
220 "bucket-owner-read",
221 "authenticated-read",
222 "bucket-owner-full-control",
223 "public-read-write",
224 ]
225 if acl in CANNED_ACLS:
226 gs_context.Copy(local_file, remote_file, acl=acl)
Gabe Black40169e62014-06-17 15:23:47 -0700227 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600228 # For private uploads we assume that the overlay board is set up properly
229 # and a googlestore_acl.xml is present. Otherwise, this script errors.
230 # We set version=0 here to ensure that the ACL is set only once (see
231 # http://b/15883752#comment54).
232 try:
233 gs_context.Copy(local_file, remote_file, version=0)
234 except gs.GSContextPreconditionFailed as ex:
235 # If we received a GSContextPreconditionFailed error, we know that the
236 # file exists now, but we don't know whether our specific update
237 # succeeded. See http://b/15883752#comment62
238 logging.warning(
239 "Assuming upload succeeded despite PreconditionFailed errors: %s",
240 ex,
241 )
242
243 if acl.endswith(".xml"):
244 # Apply the passed in ACL xml file to the uploaded object.
245 gs_context.SetACL(remote_file, acl=acl)
246 else:
247 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700248
Mike Frysingercc838832014-05-24 13:10:30 -0400249
Mike Frysinger540883b2014-05-24 13:46:16 -0400250def RemoteUpload(gs_context, acl, files, pool=10):
Alex Klein1699fab2022-09-08 08:46:06 -0600251 """Upload to google storage.
David James8c846492011-01-25 17:07:29 -0800252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 Create a pool of process and call _GsUpload with the proper arguments.
David James8c846492011-01-25 17:07:29 -0800254
Alex Klein1699fab2022-09-08 08:46:06 -0600255 Args:
256 gs_context: A lib.gs.GSContext instance.
257 acl: The canned acl used for uploading. acl can be one of: "public-read",
258 "public-read-write", "authenticated-read", "bucket-owner-read",
259 "bucket-owner-full-control", or "private".
260 files: dictionary with keys to local files and values to remote path.
261 pool: integer of maximum proesses to have at the same time.
David James8c846492011-01-25 17:07:29 -0800262
Alex Klein1699fab2022-09-08 08:46:06 -0600263 Returns:
264 Return a set of tuple arguments of the failed uploads
265 """
266 upload = functools.partial(_GsUpload, gs_context, acl)
267 tasks = [[key, value] for key, value in files.items()]
268 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800269
270
271def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
Alex Klein1699fab2022-09-08 08:46:06 -0600272 """Build a dictionary of local remote file key pairs to upload.
David James8c846492011-01-25 17:07:29 -0800273
Alex Klein1699fab2022-09-08 08:46:06 -0600274 Args:
275 base_local_path: The base path to the files on the local hard drive.
276 base_remote_path: The base path to the remote paths.
277 pkgs: The packages to upload.
David James8c846492011-01-25 17:07:29 -0800278
Alex Klein1699fab2022-09-08 08:46:06 -0600279 Returns:
280 Returns a dictionary of local_path/remote_path pairs
281 """
282 upload_files = {}
283 for pkg in pkgs:
284 suffix = pkg["CPV"] + ".tbz2"
285 local_path = os.path.join(base_local_path, suffix)
286 assert os.path.exists(local_path), "%s does not exist" % local_path
287 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800288
Alex Klein1699fab2022-09-08 08:46:06 -0600289 if pkg.get("DEBUG_SYMBOLS") == "yes":
290 debugsuffix = pkg["CPV"] + ".debug.tbz2"
291 local_path = os.path.join(base_local_path, debugsuffix)
292 assert os.path.exists(local_path)
293 upload_files[local_path] = os.path.join(
294 base_remote_path, debugsuffix
295 )
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800296
Alex Klein1699fab2022-09-08 08:46:06 -0600297 return upload_files
David James8c846492011-01-25 17:07:29 -0800298
Mike Frysingercc838832014-05-24 13:10:30 -0400299
Peter Mayo950e41a2014-02-06 21:07:33 +0000300def GetBoardOverlay(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600301 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500302
Alex Klein1699fab2022-09-08 08:46:06 -0600303 Args:
304 build_path: The path to the root of the build directory
305 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500306
Alex Klein1699fab2022-09-08 08:46:06 -0600307 Returns:
308 The last overlay configured for the given board as a string.
309 """
310 board = target.board_variant
311 overlays = portage_util.FindOverlays(
312 constants.BOTH_OVERLAYS, board, buildroot=build_path
313 )
314 # We only care about the last entry.
315 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800316
317
318def DeterminePrebuiltConfFile(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600319 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
David James8c846492011-01-25 17:07:29 -0800320
Alex Klein1699fab2022-09-08 08:46:06 -0600321 Args:
322 build_path: The path to the root of the build directory
323 target: String representation of the board. This includes host and board
324 targets
David James8c846492011-01-25 17:07:29 -0800325
Alex Klein1699fab2022-09-08 08:46:06 -0600326 Returns:
327 A string path to a prebuilt.conf file to be updated.
328 """
329 if _HOST_ARCH == target:
330 # We are host.
331 # Without more examples of hosts this is a kludge for now.
332 # TODO(Scottz): as new host targets come online expand this to
333 # work more like boards.
334 make_path = _PREBUILT_MAKE_CONF[target]
335 else:
336 # We are a board
337 board = GetBoardOverlay(build_path, target)
338 make_path = os.path.join(board, "prebuilt.conf")
David James8c846492011-01-25 17:07:29 -0800339
Alex Klein1699fab2022-09-08 08:46:06 -0600340 return make_path
David James8c846492011-01-25 17:07:29 -0800341
342
343def UpdateBinhostConfFile(path, key, value):
Alex Klein1699fab2022-09-08 08:46:06 -0600344 """Update binhost config file file with key=value.
David James8c846492011-01-25 17:07:29 -0800345
Alex Klein1699fab2022-09-08 08:46:06 -0600346 Args:
347 path: Filename to update.
348 key: Key to update.
349 value: New value for key.
350 """
351 cwd, filename = os.path.split(os.path.abspath(path))
352 osutils.SafeMakedirs(cwd)
353 if not git.GetCurrentBranch(cwd):
354 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
355 osutils.WriteFile(path, "", mode="a")
356 if UpdateLocalFile(path, value, key):
357 desc = "%s: %s %s" % (
358 filename,
359 "updating" if value else "clearing",
360 key,
361 )
362 git.AddPath(path)
363 git.Commit(cwd, desc)
364
David James8c846492011-01-25 17:07:29 -0800365
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800366def GenerateHtmlIndex(files, index, board, version, remote_location):
Alex Klein1699fab2022-09-08 08:46:06 -0600367 """Given the list of |files|, generate an index.html at |index|.
Mike Frysinger212e4292014-05-24 15:15:44 -0400368
Alex Klein1699fab2022-09-08 08:46:06 -0600369 Args:
370 files: The list of files to link to.
371 index: The path to the html index.
372 board: Name of the board this index is for.
373 version: Build version this index is for.
374 remote_location: Remote gs location prebuilts are uploaded to.
375 """
376 title = "Package Prebuilt Index: %s / %s" % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400377
Alex Klein1699fab2022-09-08 08:46:06 -0600378 files = files + [
379 ".|Google Storage Index",
380 "..|",
381 ]
382 commands.GenerateHtmlIndex(
383 index, files, title=title, url_base=gs.GsUrlToHttp(remote_location)
384 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400385
386
David Jamesce093af2011-02-23 15:21:58 -0800387def _GrabAllRemotePackageIndexes(binhost_urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600388 """Grab all of the packages files associated with a list of binhost_urls.
David James05bcb2b2011-02-09 09:25:47 -0800389
Alex Klein1699fab2022-09-08 08:46:06 -0600390 Args:
391 binhost_urls: The URLs for the directories containing the Packages files we
392 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800393
Alex Klein1699fab2022-09-08 08:46:06 -0600394 Returns:
395 A list of PackageIndex objects.
396 """
397 pkg_indexes = []
398 for url in binhost_urls:
399 pkg_index = binpkg.GrabRemotePackageIndex(url)
400 if pkg_index:
401 pkg_indexes.append(pkg_index)
402 return pkg_indexes
David James05bcb2b2011-02-09 09:25:47 -0800403
404
David Jamesc0f158a2011-02-22 16:07:29 -0800405class PrebuiltUploader(object):
Alex Klein1699fab2022-09-08 08:46:06 -0600406 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800407
Alex Klein1699fab2022-09-08 08:46:06 -0600408 def __init__(
409 self,
410 upload_location,
411 acl,
412 binhost_base_url,
413 pkg_indexes,
414 build_path,
415 packages,
416 skip_upload,
417 binhost_conf_dir,
418 dryrun,
419 target,
420 slave_targets,
421 version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700422 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600423 chroot=None,
424 ):
425 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800426
Alex Klein1699fab2022-09-08 08:46:06 -0600427 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800428
Alex Klein1699fab2022-09-08 08:46:06 -0600429 Args:
430 upload_location: The upload location.
431 acl: The canned acl used for uploading to Google Storage. acl can be one
432 of: "public-read", "public-read-write", "authenticated-read",
433 "bucket-owner-read", "bucket-owner-full-control", "project-private",
434 or "private" (see "gsutil help acls"). If we are not uploading to
435 Google Storage, this parameter is unused.
436 binhost_base_url: The URL used for downloading the prebuilts.
437 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
438 uploading duplicate files, we just link to the old files.
439 build_path: The path to the directory containing the chroot.
440 packages: Packages to upload.
441 skip_upload: Don't actually upload the tarballs.
442 binhost_conf_dir: Directory where to store binhost.conf files.
443 dryrun: Don't push or upload prebuilts.
444 target: BuildTarget managed by this builder.
445 slave_targets: List of BuildTargets managed by slave builders.
446 version: A unique string, intended to be included in the upload path,
447 which identifies the version number of the uploaded prebuilts.
Bob Haarmanc0082602022-09-20 16:12:43 -0700448 report: Dict in which to collect information to report to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600449 chroot: Path to the chroot containing the prebuilts.
450 """
451 self._upload_location = upload_location
452 self._acl = acl
453 self._binhost_base_url = binhost_base_url
454 self._pkg_indexes = pkg_indexes
455 self._build_path = build_path
456 self._packages = set(packages)
457 self._found_packages = set()
458 self._skip_upload = skip_upload
459 self._binhost_conf_dir = binhost_conf_dir
460 self._dryrun = dryrun
461 self._target = target
462 self._slave_targets = slave_targets
463 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700464 self._report = report
Alex Klein1699fab2022-09-08 08:46:06 -0600465 self._chroot = chroot or os.path.join(
466 build_path, constants.DEFAULT_CHROOT_DIR
467 )
468 self._gs_context = gs.GSContext(
469 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
470 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400471
Alex Klein1699fab2022-09-08 08:46:06 -0600472 def _Upload(self, local_file, remote_file):
473 """Wrapper around _GsUpload"""
474 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700475
Alex Klein1699fab2022-09-08 08:46:06 -0600476 def _ShouldFilterPackage(self, pkg):
477 if not self._packages:
478 return False
479 cpv = package_info.SplitCPV(pkg["CPV"])
480 self._found_packages.add(cpv.cp)
481 return (
482 cpv.package not in self._packages and cpv.cp not in self._packages
483 )
David James8c846492011-01-25 17:07:29 -0800484
Alex Klein1699fab2022-09-08 08:46:06 -0600485 def _UploadPrebuilt(self, package_path, url_suffix):
486 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800487
Alex Klein1699fab2022-09-08 08:46:06 -0600488 Args:
489 package_path: The path to the packages dir.
490 url_suffix: The remote subdirectory where we should upload the packages.
491 """
492 # Process Packages file, removing duplicates and filtered packages.
493 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
494 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
495 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
496 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
497 unmatched_pkgs = self._packages - self._found_packages
498 if unmatched_pkgs:
499 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800500
Alex Klein1699fab2022-09-08 08:46:06 -0600501 # Write Packages file.
502 pkg_index.header["TTL"] = _BINPKG_TTL
503 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800504
Alex Klein1699fab2022-09-08 08:46:06 -0600505 remote_location = "%s/%s" % (
506 self._upload_location.rstrip("/"),
507 url_suffix,
508 )
509 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800510
Alex Klein1699fab2022-09-08 08:46:06 -0600511 upload_files = GenerateUploadDict(
512 package_path, remote_location, uploads
513 )
514 remote_file = "%s/Packages" % remote_location.rstrip("/")
515 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700516
Alex Klein1699fab2022-09-08 08:46:06 -0600517 # Build list of files to upload. Manually include the dev-only files but
518 # skip them if not present.
519 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
520 if os.path.exists(dev_only):
521 upload_files[dev_only] = "%s/%s" % (
522 remote_location.rstrip("/"),
523 os.path.basename(dev_only),
524 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400525
Alex Klein1699fab2022-09-08 08:46:06 -0600526 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800527
Alex Klein1699fab2022-09-08 08:46:06 -0600528 with tempfile.NamedTemporaryFile(
529 prefix="chromite.upload_prebuilts.index."
530 ) as index:
531 GenerateHtmlIndex(
532 [x[len(remote_location) + 1 :] for x in upload_files.values()],
533 index.name,
534 self._target,
535 self._version,
536 remote_location,
537 )
538 self._Upload(
539 index.name, "%s/index.html" % remote_location.rstrip("/")
540 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400541
Alex Klein1699fab2022-09-08 08:46:06 -0600542 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
543 url = "%s%s/index.html" % (
544 gs.PUBLIC_BASE_HTTPS_URL,
545 remote_location[len(gs.BASE_GS_URL) :],
546 )
547 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400548
Alex Klein1699fab2022-09-08 08:46:06 -0600549 def _UploadSdkTarball(
550 self,
551 board_path,
552 url_suffix,
553 prepackaged,
554 toolchains_overlay_tarballs,
555 toolchains_overlay_upload_path,
556 toolchain_tarballs,
557 toolchain_upload_path,
558 ):
559 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700560
Alex Klein1699fab2022-09-08 08:46:06 -0600561 Args:
562 board_path: The path to the board dir.
563 url_suffix: The remote subdirectory where we should upload the packages.
564 prepackaged: If given, a tarball that has been packaged outside of this
565 script and should be used.
566 toolchains_overlay_tarballs: List of toolchains overlay tarball
567 specifications to upload. Items take the form
568 "toolchains_spec:/path/to/tarball".
569 toolchains_overlay_upload_path: Path template under the bucket to place
570 toolchains overlay tarballs.
571 toolchain_tarballs: List of toolchain tarballs to upload.
572 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
573 """
574 remote_location = "%s/%s" % (
575 self._upload_location.rstrip("/"),
576 url_suffix,
577 )
578 assert remote_location.startswith("gs://")
579 boardname = os.path.basename(board_path.rstrip("/"))
580 # We do not upload non SDK board tarballs,
581 assert boardname == constants.CHROOT_BUILDER_BOARD
582 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200583
Alex Klein1699fab2022-09-08 08:46:06 -0600584 version_str = self._version[len("chroot-") :]
585 remote_tarfile = toolchain.GetSdkURL(
586 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
587 )
588 # For SDK, also upload the manifest which is guaranteed to exist
589 # by the builderstage.
590 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
591 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200592
Alex Klein1699fab2022-09-08 08:46:06 -0600593 # Upload SDK toolchains overlays and toolchain tarballs, if given.
594 for tarball_list, upload_path, qualifier_name in (
595 (
596 toolchains_overlay_tarballs,
597 toolchains_overlay_upload_path,
598 "toolchains",
599 ),
600 (toolchain_tarballs, toolchain_upload_path, "target"),
601 ):
602 for tarball_spec in tarball_list:
603 qualifier_val, local_path = tarball_spec.split(":")
604 suburl = upload_path % {qualifier_name: qualifier_val}
605 remote_path = toolchain.GetSdkURL(
606 for_gsutil=True, suburl=suburl
607 )
608 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500609
Alex Klein1699fab2022-09-08 08:46:06 -0600610 # Finally, also update the pointer to the latest SDK on which polling
611 # scripts rely.
612 with osutils.TempDir() as tmpdir:
613 pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
614 remote_pointerfile = toolchain.GetSdkURL(
615 for_gsutil=True, suburl="cros-sdk-latest.conf"
616 )
Greg Edelstonf3916f92023-02-22 15:49:38 -0700617 osutils.WriteFile(
618 pointerfile,
619 f"""# The most recent SDK that is tested and ready for use.
620LATEST_SDK=\"{version_str}\"
621
622# The most recently built version. New uprev attempts should target this.
623# Warning: This version may not be tested yet.
624LATEST_SDK_UPREV_TARGET=\"None\"""",
625 )
Alex Klein1699fab2022-09-08 08:46:06 -0600626 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200627
Alex Klein1699fab2022-09-08 08:46:06 -0600628 def _GetTargets(self):
629 """Retuns the list of targets to use."""
630 targets = self._slave_targets[:]
631 if self._target:
632 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700633
Alex Klein1699fab2022-09-08 08:46:06 -0600634 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700635
Alex Klein1699fab2022-09-08 08:46:06 -0600636 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
637 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800638
Alex Klein1699fab2022-09-08 08:46:06 -0600639 This function will sync both the standard host packages, plus the host
640 packages associated with all targets that have been "setup" with the
641 current host's chroot. For instance, if this host has been used to build
642 x86-generic, it will sync the host packages associated with
643 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
644 it will also sync the host packages associated with
645 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800646
Alex Klein1699fab2022-09-08 08:46:06 -0600647 Args:
648 key: The variable key to update in the git file.
649 git_sync: If set, update make.conf of target to reference the latest
650 prebuilt packages generated here.
651 sync_binhost_conf: If set, update binhost config file in
652 chromiumos-overlay for the host.
653 """
654 # Slave boards are listed before the master board so that the master board
655 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
656 # over preflight host prebuilts from other builders.)
657 binhost_urls = []
658 for target in self._GetTargets():
659 url_suffix = _REL_HOST_PATH % {
660 "version": self._version,
661 "host_arch": _HOST_ARCH,
662 "target": target,
663 }
664 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800665
Alex Klein1699fab2022-09-08 08:46:06 -0600666 if self._target == target and not self._skip_upload:
667 # Upload prebuilts.
668 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
669 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700670
Alex Klein1699fab2022-09-08 08:46:06 -0600671 # Record URL where prebuilts were uploaded.
672 binhost_urls.append(
673 "%s/%s/"
674 % (
675 self._binhost_base_url.rstrip("/"),
676 packages_url_suffix.rstrip("/"),
677 )
678 )
David Jamese2488642011-11-14 16:15:20 -0800679
Alex Klein1699fab2022-09-08 08:46:06 -0600680 binhost = " ".join(binhost_urls)
681 if git_sync:
682 git_file = os.path.join(
683 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
684 )
Bob Haarmanc0082602022-09-20 16:12:43 -0700685 RevGitFile(
686 git_file, {key: binhost}, self._report, dryrun=self._dryrun
687 )
Alex Klein1699fab2022-09-08 08:46:06 -0600688 if sync_binhost_conf:
689 binhost_conf = os.path.join(
690 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
691 )
692 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800693
Alex Klein1699fab2022-09-08 08:46:06 -0600694 def SyncBoardPrebuilts(
695 self,
696 key,
697 git_sync,
698 sync_binhost_conf,
699 upload_board_tarball,
700 prepackaged_board,
701 toolchains_overlay_tarballs,
702 toolchains_overlay_upload_path,
703 toolchain_tarballs,
704 toolchain_upload_path,
705 ):
706 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800707
Alex Klein1699fab2022-09-08 08:46:06 -0600708 Args:
709 key: The variable key to update in the git file.
710 git_sync: If set, update make.conf of target to reference the latest
711 prebuilt packages generated here.
712 sync_binhost_conf: If set, update binhost config file in
713 chromiumos-overlay for the current board.
714 upload_board_tarball: Include a tarball of the board in our upload.
715 prepackaged_board: A tarball of the board built outside of this script.
716 toolchains_overlay_tarballs: List of toolchains overlay tarball
717 specifications to upload. Items take the form
718 "toolchains_spec:/path/to/tarball".
719 toolchains_overlay_upload_path: Path template under the bucket to place
720 toolchains overlay tarballs.
721 toolchain_tarballs: A list of toolchain tarballs to upload.
722 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
723 """
724 updated_binhosts = set()
725 for target in self._GetTargets():
726 board_path = os.path.join(
727 self._build_path, _BOARD_PATH % {"board": target.board_variant}
728 )
729 package_path = os.path.join(board_path, "packages")
730 url_suffix = _REL_BOARD_PATH % {
731 "target": target,
732 "version": self._version,
733 }
734 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700735
Alex Klein1699fab2022-09-08 08:46:06 -0600736 # Process the target board differently if it is the main --board.
737 if self._target == target and not self._skip_upload:
738 # This strips "chroot" prefix because that is sometimes added as the
739 # --prepend-version argument (e.g. by chromiumos-sdk bot).
740 # TODO(build): Clean it up to be less hard-coded.
741 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500742
Alex Klein1699fab2022-09-08 08:46:06 -0600743 # Upload board tarballs in the background.
744 if upload_board_tarball:
745 if toolchain_upload_path:
746 toolchain_upload_path %= {"version": version_str}
747 if toolchains_overlay_upload_path:
748 toolchains_overlay_upload_path %= {
749 "version": version_str
750 }
751 tar_process = multiprocessing.Process(
752 target=self._UploadSdkTarball,
753 args=(
754 board_path,
755 url_suffix,
756 prepackaged_board,
757 toolchains_overlay_tarballs,
758 toolchains_overlay_upload_path,
759 toolchain_tarballs,
760 toolchain_upload_path,
761 ),
762 )
763 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700764
Alex Klein1699fab2022-09-08 08:46:06 -0600765 # Upload prebuilts.
766 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700767
Alex Klein1699fab2022-09-08 08:46:06 -0600768 # Make sure we finished uploading the board tarballs.
769 if upload_board_tarball:
770 tar_process.join()
771 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800772
Alex Klein1699fab2022-09-08 08:46:06 -0600773 # Record URL where prebuilts were uploaded.
774 url_value = "%s/%s/" % (
775 self._binhost_base_url.rstrip("/"),
776 packages_url_suffix.rstrip("/"),
777 )
David Jamese2488642011-11-14 16:15:20 -0800778
Alex Klein1699fab2022-09-08 08:46:06 -0600779 if git_sync:
780 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Bob Haarmanc0082602022-09-20 16:12:43 -0700781 RevGitFile(
782 git_file,
783 {key: url_value},
784 self._report,
785 dryrun=self._dryrun,
786 )
Matt Tennante8179042013-10-01 15:47:32 -0700787
Alex Klein1699fab2022-09-08 08:46:06 -0600788 if sync_binhost_conf:
789 # Update the binhost configuration file in git.
790 binhost_conf = os.path.join(
791 self._binhost_conf_dir,
792 "target",
793 "%s-%s.conf" % (target, key),
794 )
795 updated_binhosts.add(binhost_conf)
796 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800797
Alex Klein1699fab2022-09-08 08:46:06 -0600798 if sync_binhost_conf:
799 # Clear all old binhosts. The files must be left empty in case anybody
800 # is referring to them.
801 all_binhosts = set(
802 glob.glob(
803 os.path.join(
804 self._binhost_conf_dir, "target", "*-%s.conf" % key
805 )
806 )
807 )
808 for binhost_conf in all_binhosts - updated_binhosts:
809 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800810
David James05bcb2b2011-02-09 09:25:47 -0800811
Mike Nicholsa1414162021-04-22 20:07:22 +0000812class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600813 """Callback that adds a slave board to the list of slave targets."""
814
815 def __call__(self, parser, namespace, values, option_string=None):
816 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800817
818
Mike Nicholsa1414162021-04-22 20:07:22 +0000819class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600820 """Callback that adds a slave profile to the list of slave targets."""
821
822 def __call__(self, parser, namespace, values, option_string=None):
823 if not namespace.slave_targets:
824 parser.error("Must specify --slave-board before --slave-profile")
825 if namespace.slave_targets[-1].profile is not None:
826 parser.error("Cannot specify --slave-profile twice for same board")
827 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800828
829
Mike Frysinger86509232014-05-24 13:18:37 -0400830def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600831 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700832
Alex Klein1699fab2022-09-08 08:46:06 -0600833 Args:
834 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400835
Alex Klein1699fab2022-09-08 08:46:06 -0600836 Returns:
837 A tuple containing a parsed options object and BuildTarget.
838 The target instance is None if no board is specified.
839 """
Mike Frysinger4e86f862023-02-02 09:01:33 -0500840 parser = commandline.ArgumentParser(description=__doc__, dryrun=True)
Alex Klein1699fab2022-09-08 08:46:06 -0600841 parser.add_argument(
842 "-H",
843 "--binhost-base-url",
844 default=_BINHOST_BASE_URL,
845 help="Base URL to use for binhost in make.conf updates",
846 )
847 parser.add_argument(
848 "--previous-binhost-url",
849 action="append",
850 default=[],
851 help="Previous binhost URL",
852 )
853 parser.add_argument(
854 "-b", "--board", help="Board type that was built on this machine"
855 )
856 parser.add_argument(
857 "-B",
858 "--prepackaged-tarball",
859 type="path",
860 help="Board tarball prebuilt outside of this script.",
861 )
862 parser.add_argument(
863 "--toolchains-overlay-tarball",
864 dest="toolchains_overlay_tarballs",
865 action="append",
866 default=[],
867 help="Toolchains overlay tarball specification to "
868 "upload. Takes the form "
869 '"toolchains_spec:/path/to/tarball".',
870 )
871 parser.add_argument(
872 "--toolchains-overlay-upload-path",
873 default="",
874 help="Path template for uploading toolchains overlays.",
875 )
876 parser.add_argument(
877 "--toolchain-tarball",
878 dest="toolchain_tarballs",
879 action="append",
880 default=[],
881 help="Redistributable toolchain tarball.",
882 )
883 parser.add_argument(
884 "--toolchain-upload-path",
885 default="",
886 help="Path to place toolchain tarballs in the sdk tree.",
887 )
888 parser.add_argument(
889 "--profile", help="Profile that was built on this machine"
890 )
891 parser.add_argument(
892 "--slave-board",
893 default=[],
894 action=_AddSlaveBoardAction,
895 dest="slave_targets",
896 help="Board type that was built on a slave machine. To "
897 "add a profile to this board, use --slave-profile.",
898 )
899 parser.add_argument(
900 "--slave-profile",
901 action=_AddSlaveProfileAction,
902 help="Board profile that was built on a slave machine. "
903 "Applies to previous slave board.",
904 )
905 parser.add_argument(
906 "-p",
907 "--build-path",
908 required=True,
909 help="Path to the directory containing the chroot",
910 )
911 parser.add_argument(
912 "--chroot",
913 help="Path where the chroot is located. "
914 "(Default: {build_path}/chroot)",
915 )
916 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700917 "--output",
918 type=Path,
919 help="Write a JSON report to the specified file. "
920 "(Default is not to write the report.)",
921 )
922 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600923 "--packages",
924 action="append",
925 default=[],
926 help="Only include the specified packages. "
927 "(Default is to include all packages.)",
928 )
929 parser.add_argument(
930 "-s",
931 "--sync-host",
932 default=False,
933 action="store_true",
934 help="Sync host prebuilts",
935 )
936 parser.add_argument(
937 "-g",
938 "--git-sync",
939 default=False,
940 action="store_true",
941 help="Enable git version sync (This commits to a repo.) "
942 "This is used by full builders to commit directly "
943 "to board overlays.",
944 )
945 parser.add_argument("-u", "--upload", help="Upload location")
946 parser.add_argument(
947 "-V",
948 "--prepend-version",
949 help="Add an identifier to the front of the version",
950 )
951 parser.add_argument(
952 "-f",
953 "--filters",
954 action="store_true",
955 default=False,
956 help="Turn on filtering of private ebuild packages",
957 )
958 parser.add_argument(
959 "-k",
960 "--key",
961 default="PORTAGE_BINHOST",
962 help="Key to update in make.conf / binhost.conf",
963 )
964 parser.add_argument("--set-version", help="Specify the version string")
965 parser.add_argument(
966 "--sync-binhost-conf",
967 default=False,
968 action="store_true",
969 help="Update binhost.conf in chromiumos-overlay or "
970 "chromeos-overlay. Commit the changes, but don't "
971 "push them. This is used for preflight binhosts.",
972 )
973 parser.add_argument(
974 "--binhost-conf-dir",
975 help="Directory to commit binhost config with " "--sync-binhost-conf.",
976 )
977 parser.add_argument(
978 "-P",
979 "--private",
980 action="store_true",
981 default=False,
982 help="Mark gs:// uploads as private.",
983 )
984 parser.add_argument(
985 "--skip-upload",
986 action="store_true",
987 default=False,
988 help="Skip upload step.",
989 )
990 parser.add_argument(
991 "--upload-board-tarball",
992 action="store_true",
993 default=False,
994 help="Upload board tarball to Google Storage.",
995 )
David James8c846492011-01-25 17:07:29 -0800996
Alex Klein1699fab2022-09-08 08:46:06 -0600997 options = parser.parse_args(argv)
998 if not options.upload and not options.skip_upload:
999 parser.error("you need to provide an upload location using -u")
1000 if not options.set_version and options.skip_upload:
1001 parser.error(
1002 "If you are using --skip-upload, you must specify a "
1003 "version number using --set-version."
1004 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001005
Alex Klein1699fab2022-09-08 08:46:06 -06001006 target = None
1007 if options.board:
1008 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -07001009
Alex Klein1699fab2022-09-08 08:46:06 -06001010 if target in options.slave_targets:
1011 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001012
Alex Klein1699fab2022-09-08 08:46:06 -06001013 if len(set(options.slave_targets)) != len(options.slave_targets):
1014 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001015
Alex Klein1699fab2022-09-08 08:46:06 -06001016 if options.slave_targets and options.git_sync:
1017 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001018
Alex Klein1699fab2022-09-08 08:46:06 -06001019 if (
1020 options.upload_board_tarball
1021 and options.skip_upload
1022 and options.board == "amd64-host"
1023 ):
1024 parser.error(
1025 "--skip-upload is not compatible with "
1026 "--upload-board-tarball and --board=amd64-host"
1027 )
David James8fa34ea2011-04-15 13:00:20 -07001028
Alex Klein1699fab2022-09-08 08:46:06 -06001029 if (
1030 options.upload_board_tarball
1031 and not options.skip_upload
1032 and not options.upload.startswith("gs://")
1033 ):
1034 parser.error(
1035 "--upload-board-tarball only works with gs:// URLs.\n"
1036 "--upload must be a gs:// URL."
1037 )
David James8fa34ea2011-04-15 13:00:20 -07001038
Alex Klein1699fab2022-09-08 08:46:06 -06001039 if options.upload_board_tarball and options.prepackaged_tarball is None:
1040 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001041
Alex Klein1699fab2022-09-08 08:46:06 -06001042 if options.private:
1043 if options.sync_host:
1044 parser.error(
1045 "--private and --sync-host/-s cannot be specified "
1046 "together; we do not support private host prebuilts"
1047 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001048
Alex Klein1699fab2022-09-08 08:46:06 -06001049 if not options.upload or not options.upload.startswith("gs://"):
1050 parser.error(
1051 "--private is only valid for gs:// URLs; "
1052 "--upload must be a gs:// URL."
1053 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001054
Alex Klein1699fab2022-09-08 08:46:06 -06001055 if options.binhost_base_url != _BINHOST_BASE_URL:
1056 parser.error(
1057 "when using --private the --binhost-base-url "
1058 "is automatically derived."
1059 )
David James27fa7d12011-06-29 17:24:14 -07001060
Alex Klein1699fab2022-09-08 08:46:06 -06001061 if options.sync_binhost_conf and not options.binhost_conf_dir:
1062 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001063
Alex Klein1699fab2022-09-08 08:46:06 -06001064 if (
1065 options.toolchains_overlay_tarballs
1066 and not options.toolchains_overlay_upload_path
1067 ):
1068 parser.error(
1069 "--toolchains-overlay-tarball requires "
1070 "--toolchains-overlay-upload-path"
1071 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001072
Alex Klein1699fab2022-09-08 08:46:06 -06001073 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001074
Mike Frysingercc838832014-05-24 13:10:30 -04001075
Mike Frysinger86509232014-05-24 13:18:37 -04001076def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001077 # We accumulate information about actions taken and report it at the end
1078 # if asked to do so. Currently, this only records CL creation, which
1079 # is the only thing we need for now.
1080 report = {}
1081
Alex Klein1699fab2022-09-08 08:46:06 -06001082 # Set umask so that files created as root are readable.
1083 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001084
Alex Klein1699fab2022-09-08 08:46:06 -06001085 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001086
Alex Klein1699fab2022-09-08 08:46:06 -06001087 # Calculate a list of Packages index files to compare against. Whenever we
1088 # upload a package, we check to make sure it's not already stored in one of
1089 # the packages files we uploaded. This list of packages files might contain
1090 # both board and host packages.
1091 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001092
Alex Klein1699fab2022-09-08 08:46:06 -06001093 if options.set_version:
1094 version = options.set_version
1095 else:
1096 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001097
Alex Klein1699fab2022-09-08 08:46:06 -06001098 if options.prepend_version:
1099 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001100
Alex Klein1699fab2022-09-08 08:46:06 -06001101 acl = "public-read"
1102 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001103
Alex Klein1699fab2022-09-08 08:46:06 -06001104 if options.private:
1105 binhost_base_url = options.upload
1106 if target:
1107 acl = portage_util.FindOverlayFile(
1108 _GOOGLESTORAGE_GSUTIL_FILE,
1109 board=target.board_variant,
1110 buildroot=options.build_path,
1111 )
1112 if acl is None:
1113 cros_build_lib.Die(
1114 "No Google Storage ACL file %s found in %s overlay.",
1115 _GOOGLESTORAGE_GSUTIL_FILE,
1116 target.board_variant,
1117 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001118
Alex Klein1699fab2022-09-08 08:46:06 -06001119 binhost_conf_dir = None
1120 if options.binhost_conf_dir:
1121 binhost_conf_dir = os.path.join(
1122 options.build_path, options.binhost_conf_dir
1123 )
David Jamesb26b9312014-12-15 11:26:46 -08001124
Alex Klein1699fab2022-09-08 08:46:06 -06001125 uploader = PrebuiltUploader(
1126 options.upload,
1127 acl,
1128 binhost_base_url,
1129 pkg_indexes,
1130 options.build_path,
1131 options.packages,
1132 options.skip_upload,
1133 binhost_conf_dir,
1134 options.dryrun,
1135 target,
1136 options.slave_targets,
1137 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001138 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001139 options.chroot,
1140 )
David Jamesc0f158a2011-02-22 16:07:29 -08001141
Alex Klein1699fab2022-09-08 08:46:06 -06001142 if options.sync_host:
1143 uploader.SyncHostPrebuilts(
1144 options.key, options.git_sync, options.sync_binhost_conf
1145 )
David James8c846492011-01-25 17:07:29 -08001146
Alex Klein1699fab2022-09-08 08:46:06 -06001147 if options.board or options.slave_targets:
1148 uploader.SyncBoardPrebuilts(
1149 options.key,
1150 options.git_sync,
1151 options.sync_binhost_conf,
1152 options.upload_board_tarball,
1153 options.prepackaged_tarball,
1154 options.toolchains_overlay_tarballs,
1155 options.toolchains_overlay_upload_path,
1156 options.toolchain_tarballs,
1157 options.toolchain_upload_path,
1158 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001159
Denis Nikitinf7e5e9c2022-09-30 14:34:47 -07001160 if options.output:
1161 with open(options.output, "w") as f:
Bob Haarmanc0082602022-09-20 16:12:43 -07001162 pformat.json(report, fp=f)