blob: 2b21ad126b0fcee2513aaefcc6c2f8fc610d2fa4 [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 Haarmanc0082602022-09-20 16:12:43 -0700160def RevGitFile(filename, data, report, 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 """
169 prebuilt_branch = "prebuilt_branch"
170 cwd = os.path.abspath(os.path.dirname(filename))
171 remote_name = git.RunGit(cwd, ["remote"]).stdout.strip()
172 gerrit_helper = gerrit.GetGerritHelper(remote_name)
173 remote_url = git.RunGit(
174 cwd, ["config", "--get", f"remote.{remote_name}.url"]
175 ).stdout.strip()
176 description = "%s: updating %s" % (
177 os.path.basename(filename),
178 ", ".join(data.keys()),
179 )
180 # UpdateLocalFile will print out the keys/values for us.
181 print("Revving git file %s" % filename)
182 git.CreatePushBranch(prebuilt_branch, cwd)
183 for key, value in data.items():
184 UpdateLocalFile(filename, value, key)
185 git.RunGit(cwd, ["add", filename])
186 git.RunGit(cwd, ["commit", "-m", description])
Julio Hurtado2d4817d2021-04-29 16:03:58 +0000187
Alex Klein1699fab2022-09-08 08:46:06 -0600188 tracking_info = git.GetTrackingBranch(
189 cwd, prebuilt_branch, for_push=True, for_checkout=False
190 )
191 gpatch = gerrit_helper.CreateGerritPatch(
192 cwd, remote_url, ref=tracking_info.ref, notify="NONE"
193 )
Bob Haarmanc0082602022-09-20 16:12:43 -0700194 report.setdefault("created_cls", []).append(gpatch.PatchLink())
Alex Klein1699fab2022-09-08 08:46:06 -0600195 gerrit_helper.SetReview(
196 gpatch, labels={"Bot-Commit": 1}, dryrun=dryrun, notify="NONE"
197 )
198 gerrit_helper.SubmitChange(gpatch, dryrun=dryrun, notify="NONE")
David James8c846492011-01-25 17:07:29 -0800199
200
201def GetVersion():
Alex Klein1699fab2022-09-08 08:46:06 -0600202 """Get the version to put in LATEST and update the git version with."""
203 return datetime.datetime.now().strftime("%Y.%m.%d.%H%M%S")
David James8c846492011-01-25 17:07:29 -0800204
205
Mike Frysinger540883b2014-05-24 13:46:16 -0400206def _GsUpload(gs_context, acl, local_file, remote_file):
Alex Klein1699fab2022-09-08 08:46:06 -0600207 """Upload to GS bucket.
David James8c846492011-01-25 17:07:29 -0800208
Alex Klein1699fab2022-09-08 08:46:06 -0600209 Args:
210 gs_context: A lib.gs.GSContext instance.
211 acl: The ACL to use for uploading the file.
212 local_file: The local file to be uploaded.
213 remote_file: The remote location to upload to.
214 """
215 CANNED_ACLS = [
216 "public-read",
217 "private",
218 "bucket-owner-read",
219 "authenticated-read",
220 "bucket-owner-full-control",
221 "public-read-write",
222 ]
223 if acl in CANNED_ACLS:
224 gs_context.Copy(local_file, remote_file, acl=acl)
Gabe Black40169e62014-06-17 15:23:47 -0700225 else:
Alex Klein1699fab2022-09-08 08:46:06 -0600226 # For private uploads we assume that the overlay board is set up properly
227 # and a googlestore_acl.xml is present. Otherwise, this script errors.
228 # We set version=0 here to ensure that the ACL is set only once (see
229 # http://b/15883752#comment54).
230 try:
231 gs_context.Copy(local_file, remote_file, version=0)
232 except gs.GSContextPreconditionFailed as ex:
233 # If we received a GSContextPreconditionFailed error, we know that the
234 # file exists now, but we don't know whether our specific update
235 # succeeded. See http://b/15883752#comment62
236 logging.warning(
237 "Assuming upload succeeded despite PreconditionFailed errors: %s",
238 ex,
239 )
240
241 if acl.endswith(".xml"):
242 # Apply the passed in ACL xml file to the uploaded object.
243 gs_context.SetACL(remote_file, acl=acl)
244 else:
245 gs_context.ChangeACL(remote_file, acl_args_file=acl)
Scott Zawalskiab1bed32011-03-16 15:24:24 -0700246
Mike Frysingercc838832014-05-24 13:10:30 -0400247
Mike Frysinger540883b2014-05-24 13:46:16 -0400248def RemoteUpload(gs_context, acl, files, pool=10):
Alex Klein1699fab2022-09-08 08:46:06 -0600249 """Upload to google storage.
David James8c846492011-01-25 17:07:29 -0800250
Alex Klein1699fab2022-09-08 08:46:06 -0600251 Create a pool of process and call _GsUpload with the proper arguments.
David James8c846492011-01-25 17:07:29 -0800252
Alex Klein1699fab2022-09-08 08:46:06 -0600253 Args:
254 gs_context: A lib.gs.GSContext instance.
255 acl: The canned acl used for uploading. acl can be one of: "public-read",
256 "public-read-write", "authenticated-read", "bucket-owner-read",
257 "bucket-owner-full-control", or "private".
258 files: dictionary with keys to local files and values to remote path.
259 pool: integer of maximum proesses to have at the same time.
David James8c846492011-01-25 17:07:29 -0800260
Alex Klein1699fab2022-09-08 08:46:06 -0600261 Returns:
262 Return a set of tuple arguments of the failed uploads
263 """
264 upload = functools.partial(_GsUpload, gs_context, acl)
265 tasks = [[key, value] for key, value in files.items()]
266 parallel.RunTasksInProcessPool(upload, tasks, pool)
David James8c846492011-01-25 17:07:29 -0800267
268
269def GenerateUploadDict(base_local_path, base_remote_path, pkgs):
Alex Klein1699fab2022-09-08 08:46:06 -0600270 """Build a dictionary of local remote file key pairs to upload.
David James8c846492011-01-25 17:07:29 -0800271
Alex Klein1699fab2022-09-08 08:46:06 -0600272 Args:
273 base_local_path: The base path to the files on the local hard drive.
274 base_remote_path: The base path to the remote paths.
275 pkgs: The packages to upload.
David James8c846492011-01-25 17:07:29 -0800276
Alex Klein1699fab2022-09-08 08:46:06 -0600277 Returns:
278 Returns a dictionary of local_path/remote_path pairs
279 """
280 upload_files = {}
281 for pkg in pkgs:
282 suffix = pkg["CPV"] + ".tbz2"
283 local_path = os.path.join(base_local_path, suffix)
284 assert os.path.exists(local_path), "%s does not exist" % local_path
285 upload_files[local_path] = os.path.join(base_remote_path, suffix)
David James8c846492011-01-25 17:07:29 -0800286
Alex Klein1699fab2022-09-08 08:46:06 -0600287 if pkg.get("DEBUG_SYMBOLS") == "yes":
288 debugsuffix = pkg["CPV"] + ".debug.tbz2"
289 local_path = os.path.join(base_local_path, debugsuffix)
290 assert os.path.exists(local_path)
291 upload_files[local_path] = os.path.join(
292 base_remote_path, debugsuffix
293 )
Bertrand SIMONNET22e828b2014-11-11 16:27:06 -0800294
Alex Klein1699fab2022-09-08 08:46:06 -0600295 return upload_files
David James8c846492011-01-25 17:07:29 -0800296
Mike Frysingercc838832014-05-24 13:10:30 -0400297
Peter Mayo950e41a2014-02-06 21:07:33 +0000298def GetBoardOverlay(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600299 """Get the path to the board variant.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500300
Alex Klein1699fab2022-09-08 08:46:06 -0600301 Args:
302 build_path: The path to the root of the build directory
303 target: The target board as a BuildTarget object.
Mike Frysinger1a736a82013-12-12 01:50:59 -0500304
Alex Klein1699fab2022-09-08 08:46:06 -0600305 Returns:
306 The last overlay configured for the given board as a string.
307 """
308 board = target.board_variant
309 overlays = portage_util.FindOverlays(
310 constants.BOTH_OVERLAYS, board, buildroot=build_path
311 )
312 # We only care about the last entry.
313 return overlays[-1]
David James8c846492011-01-25 17:07:29 -0800314
315
316def DeterminePrebuiltConfFile(build_path, target):
Alex Klein1699fab2022-09-08 08:46:06 -0600317 """Determine the prebuilt.conf file that needs to be updated for prebuilts.
David James8c846492011-01-25 17:07:29 -0800318
Alex Klein1699fab2022-09-08 08:46:06 -0600319 Args:
320 build_path: The path to the root of the build directory
321 target: String representation of the board. This includes host and board
322 targets
David James8c846492011-01-25 17:07:29 -0800323
Alex Klein1699fab2022-09-08 08:46:06 -0600324 Returns:
325 A string path to a prebuilt.conf file to be updated.
326 """
327 if _HOST_ARCH == target:
328 # We are host.
329 # Without more examples of hosts this is a kludge for now.
330 # TODO(Scottz): as new host targets come online expand this to
331 # work more like boards.
332 make_path = _PREBUILT_MAKE_CONF[target]
333 else:
334 # We are a board
335 board = GetBoardOverlay(build_path, target)
336 make_path = os.path.join(board, "prebuilt.conf")
David James8c846492011-01-25 17:07:29 -0800337
Alex Klein1699fab2022-09-08 08:46:06 -0600338 return make_path
David James8c846492011-01-25 17:07:29 -0800339
340
341def UpdateBinhostConfFile(path, key, value):
Alex Klein1699fab2022-09-08 08:46:06 -0600342 """Update binhost config file file with key=value.
David James8c846492011-01-25 17:07:29 -0800343
Alex Klein1699fab2022-09-08 08:46:06 -0600344 Args:
345 path: Filename to update.
346 key: Key to update.
347 value: New value for key.
348 """
349 cwd, filename = os.path.split(os.path.abspath(path))
350 osutils.SafeMakedirs(cwd)
351 if not git.GetCurrentBranch(cwd):
352 git.CreatePushBranch(constants.STABLE_EBUILD_BRANCH, cwd, sync=False)
353 osutils.WriteFile(path, "", mode="a")
354 if UpdateLocalFile(path, value, key):
355 desc = "%s: %s %s" % (
356 filename,
357 "updating" if value else "clearing",
358 key,
359 )
360 git.AddPath(path)
361 git.Commit(cwd, desc)
362
David James8c846492011-01-25 17:07:29 -0800363
Achuith Bhandarkar03d924a2018-01-02 10:50:44 -0800364def GenerateHtmlIndex(files, index, board, version, remote_location):
Alex Klein1699fab2022-09-08 08:46:06 -0600365 """Given the list of |files|, generate an index.html at |index|.
Mike Frysinger212e4292014-05-24 15:15:44 -0400366
Alex Klein1699fab2022-09-08 08:46:06 -0600367 Args:
368 files: The list of files to link to.
369 index: The path to the html index.
370 board: Name of the board this index is for.
371 version: Build version this index is for.
372 remote_location: Remote gs location prebuilts are uploaded to.
373 """
374 title = "Package Prebuilt Index: %s / %s" % (board, version)
Mike Frysinger212e4292014-05-24 15:15:44 -0400375
Alex Klein1699fab2022-09-08 08:46:06 -0600376 files = files + [
377 ".|Google Storage Index",
378 "..|",
379 ]
380 commands.GenerateHtmlIndex(
381 index, files, title=title, url_base=gs.GsUrlToHttp(remote_location)
382 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400383
384
David Jamesce093af2011-02-23 15:21:58 -0800385def _GrabAllRemotePackageIndexes(binhost_urls):
Alex Klein1699fab2022-09-08 08:46:06 -0600386 """Grab all of the packages files associated with a list of binhost_urls.
David James05bcb2b2011-02-09 09:25:47 -0800387
Alex Klein1699fab2022-09-08 08:46:06 -0600388 Args:
389 binhost_urls: The URLs for the directories containing the Packages files we
390 want to grab.
David James05bcb2b2011-02-09 09:25:47 -0800391
Alex Klein1699fab2022-09-08 08:46:06 -0600392 Returns:
393 A list of PackageIndex objects.
394 """
395 pkg_indexes = []
396 for url in binhost_urls:
397 pkg_index = binpkg.GrabRemotePackageIndex(url)
398 if pkg_index:
399 pkg_indexes.append(pkg_index)
400 return pkg_indexes
David James05bcb2b2011-02-09 09:25:47 -0800401
402
David Jamesc0f158a2011-02-22 16:07:29 -0800403class PrebuiltUploader(object):
Alex Klein1699fab2022-09-08 08:46:06 -0600404 """Synchronize host and board prebuilts."""
David James8c846492011-01-25 17:07:29 -0800405
Alex Klein1699fab2022-09-08 08:46:06 -0600406 def __init__(
407 self,
408 upload_location,
409 acl,
410 binhost_base_url,
411 pkg_indexes,
412 build_path,
413 packages,
414 skip_upload,
415 binhost_conf_dir,
416 dryrun,
417 target,
418 slave_targets,
419 version,
Bob Haarmanc0082602022-09-20 16:12:43 -0700420 report,
Alex Klein1699fab2022-09-08 08:46:06 -0600421 chroot=None,
422 ):
423 """Constructor for prebuilt uploader object.
David James8c846492011-01-25 17:07:29 -0800424
Alex Klein1699fab2022-09-08 08:46:06 -0600425 This object can upload host or prebuilt files to Google Storage.
David James8c846492011-01-25 17:07:29 -0800426
Alex Klein1699fab2022-09-08 08:46:06 -0600427 Args:
428 upload_location: The upload location.
429 acl: The canned acl used for uploading to Google Storage. acl can be one
430 of: "public-read", "public-read-write", "authenticated-read",
431 "bucket-owner-read", "bucket-owner-full-control", "project-private",
432 or "private" (see "gsutil help acls"). If we are not uploading to
433 Google Storage, this parameter is unused.
434 binhost_base_url: The URL used for downloading the prebuilts.
435 pkg_indexes: Old uploaded prebuilts to compare against. Instead of
436 uploading duplicate files, we just link to the old files.
437 build_path: The path to the directory containing the chroot.
438 packages: Packages to upload.
439 skip_upload: Don't actually upload the tarballs.
440 binhost_conf_dir: Directory where to store binhost.conf files.
441 dryrun: Don't push or upload prebuilts.
442 target: BuildTarget managed by this builder.
443 slave_targets: List of BuildTargets managed by slave builders.
444 version: A unique string, intended to be included in the upload path,
445 which identifies the version number of the uploaded prebuilts.
Bob Haarmanc0082602022-09-20 16:12:43 -0700446 report: Dict in which to collect information to report to the user.
Alex Klein1699fab2022-09-08 08:46:06 -0600447 chroot: Path to the chroot containing the prebuilts.
448 """
449 self._upload_location = upload_location
450 self._acl = acl
451 self._binhost_base_url = binhost_base_url
452 self._pkg_indexes = pkg_indexes
453 self._build_path = build_path
454 self._packages = set(packages)
455 self._found_packages = set()
456 self._skip_upload = skip_upload
457 self._binhost_conf_dir = binhost_conf_dir
458 self._dryrun = dryrun
459 self._target = target
460 self._slave_targets = slave_targets
461 self._version = version
Bob Haarmanc0082602022-09-20 16:12:43 -0700462 self._report = report
Alex Klein1699fab2022-09-08 08:46:06 -0600463 self._chroot = chroot or os.path.join(
464 build_path, constants.DEFAULT_CHROOT_DIR
465 )
466 self._gs_context = gs.GSContext(
467 retries=_RETRIES, sleep=_SLEEP_TIME, dry_run=self._dryrun
468 )
Mike Frysinger540883b2014-05-24 13:46:16 -0400469
Alex Klein1699fab2022-09-08 08:46:06 -0600470 def _Upload(self, local_file, remote_file):
471 """Wrapper around _GsUpload"""
472 _GsUpload(self._gs_context, self._acl, local_file, remote_file)
David James615e5b52011-06-03 11:10:15 -0700473
Alex Klein1699fab2022-09-08 08:46:06 -0600474 def _ShouldFilterPackage(self, pkg):
475 if not self._packages:
476 return False
477 cpv = package_info.SplitCPV(pkg["CPV"])
478 self._found_packages.add(cpv.cp)
479 return (
480 cpv.package not in self._packages and cpv.cp not in self._packages
481 )
David James8c846492011-01-25 17:07:29 -0800482
Alex Klein1699fab2022-09-08 08:46:06 -0600483 def _UploadPrebuilt(self, package_path, url_suffix):
484 """Upload host or board prebuilt files to Google Storage space.
David James8c846492011-01-25 17:07:29 -0800485
Alex Klein1699fab2022-09-08 08:46:06 -0600486 Args:
487 package_path: The path to the packages dir.
488 url_suffix: The remote subdirectory where we should upload the packages.
489 """
490 # Process Packages file, removing duplicates and filtered packages.
491 pkg_index = binpkg.GrabLocalPackageIndex(package_path)
492 pkg_index.SetUploadLocation(self._binhost_base_url, url_suffix)
493 pkg_index.RemoveFilteredPackages(self._ShouldFilterPackage)
494 uploads = pkg_index.ResolveDuplicateUploads(self._pkg_indexes)
495 unmatched_pkgs = self._packages - self._found_packages
496 if unmatched_pkgs:
497 logging.warning("unable to match packages: %r", unmatched_pkgs)
David James05bcb2b2011-02-09 09:25:47 -0800498
Alex Klein1699fab2022-09-08 08:46:06 -0600499 # Write Packages file.
500 pkg_index.header["TTL"] = _BINPKG_TTL
501 tmp_packages_file = pkg_index.WriteToNamedTemporaryFile()
David James05bcb2b2011-02-09 09:25:47 -0800502
Alex Klein1699fab2022-09-08 08:46:06 -0600503 remote_location = "%s/%s" % (
504 self._upload_location.rstrip("/"),
505 url_suffix,
506 )
507 assert remote_location.startswith("gs://")
David James05bcb2b2011-02-09 09:25:47 -0800508
Alex Klein1699fab2022-09-08 08:46:06 -0600509 upload_files = GenerateUploadDict(
510 package_path, remote_location, uploads
511 )
512 remote_file = "%s/Packages" % remote_location.rstrip("/")
513 upload_files[tmp_packages_file.name] = remote_file
David James015af872012-06-19 15:24:36 -0700514
Alex Klein1699fab2022-09-08 08:46:06 -0600515 # Build list of files to upload. Manually include the dev-only files but
516 # skip them if not present.
517 dev_only = os.path.join(package_path, "dev-only-extras.tar.xz")
518 if os.path.exists(dev_only):
519 upload_files[dev_only] = "%s/%s" % (
520 remote_location.rstrip("/"),
521 os.path.basename(dev_only),
522 )
Mike Frysinger9bb6cd82020-08-20 03:02:23 -0400523
Alex Klein1699fab2022-09-08 08:46:06 -0600524 RemoteUpload(self._gs_context, self._acl, upload_files)
David James8c846492011-01-25 17:07:29 -0800525
Alex Klein1699fab2022-09-08 08:46:06 -0600526 with tempfile.NamedTemporaryFile(
527 prefix="chromite.upload_prebuilts.index."
528 ) as index:
529 GenerateHtmlIndex(
530 [x[len(remote_location) + 1 :] for x in upload_files.values()],
531 index.name,
532 self._target,
533 self._version,
534 remote_location,
535 )
536 self._Upload(
537 index.name, "%s/index.html" % remote_location.rstrip("/")
538 )
Mike Frysinger212e4292014-05-24 15:15:44 -0400539
Alex Klein1699fab2022-09-08 08:46:06 -0600540 link_name = "Prebuilts[%s]: %s" % (self._target, self._version)
541 url = "%s%s/index.html" % (
542 gs.PUBLIC_BASE_HTTPS_URL,
543 remote_location[len(gs.BASE_GS_URL) :],
544 )
545 cbuildbot_alerts.PrintBuildbotLink(link_name, url)
Mike Frysinger212e4292014-05-24 15:15:44 -0400546
Alex Klein1699fab2022-09-08 08:46:06 -0600547 def _UploadSdkTarball(
548 self,
549 board_path,
550 url_suffix,
551 prepackaged,
552 toolchains_overlay_tarballs,
553 toolchains_overlay_upload_path,
554 toolchain_tarballs,
555 toolchain_upload_path,
556 ):
557 """Upload a tarball of the sdk at the specified path to Google Storage.
David James8fa34ea2011-04-15 13:00:20 -0700558
Alex Klein1699fab2022-09-08 08:46:06 -0600559 Args:
560 board_path: The path to the board dir.
561 url_suffix: The remote subdirectory where we should upload the packages.
562 prepackaged: If given, a tarball that has been packaged outside of this
563 script and should be used.
564 toolchains_overlay_tarballs: List of toolchains overlay tarball
565 specifications to upload. Items take the form
566 "toolchains_spec:/path/to/tarball".
567 toolchains_overlay_upload_path: Path template under the bucket to place
568 toolchains overlay tarballs.
569 toolchain_tarballs: List of toolchain tarballs to upload.
570 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
571 """
572 remote_location = "%s/%s" % (
573 self._upload_location.rstrip("/"),
574 url_suffix,
575 )
576 assert remote_location.startswith("gs://")
577 boardname = os.path.basename(board_path.rstrip("/"))
578 # We do not upload non SDK board tarballs,
579 assert boardname == constants.CHROOT_BUILDER_BOARD
580 assert prepackaged is not None
Zdenek Behan62a57792012-08-31 15:09:08 +0200581
Alex Klein1699fab2022-09-08 08:46:06 -0600582 version_str = self._version[len("chroot-") :]
583 remote_tarfile = toolchain.GetSdkURL(
584 for_gsutil=True, suburl="cros-sdk-%s.tar.xz" % (version_str,)
585 )
586 # For SDK, also upload the manifest which is guaranteed to exist
587 # by the builderstage.
588 self._Upload(prepackaged + ".Manifest", remote_tarfile + ".Manifest")
589 self._Upload(prepackaged, remote_tarfile)
Zdenek Behan86c15e92012-10-13 00:55:47 +0200590
Alex Klein1699fab2022-09-08 08:46:06 -0600591 # Upload SDK toolchains overlays and toolchain tarballs, if given.
592 for tarball_list, upload_path, qualifier_name in (
593 (
594 toolchains_overlay_tarballs,
595 toolchains_overlay_upload_path,
596 "toolchains",
597 ),
598 (toolchain_tarballs, toolchain_upload_path, "target"),
599 ):
600 for tarball_spec in tarball_list:
601 qualifier_val, local_path = tarball_spec.split(":")
602 suburl = upload_path % {qualifier_name: qualifier_val}
603 remote_path = toolchain.GetSdkURL(
604 for_gsutil=True, suburl=suburl
605 )
606 self._Upload(local_path, remote_path)
Mike Frysinger9e979b92012-11-29 02:55:09 -0500607
Alex Klein1699fab2022-09-08 08:46:06 -0600608 # Finally, also update the pointer to the latest SDK on which polling
609 # scripts rely.
610 with osutils.TempDir() as tmpdir:
611 pointerfile = os.path.join(tmpdir, "cros-sdk-latest.conf")
612 remote_pointerfile = toolchain.GetSdkURL(
613 for_gsutil=True, suburl="cros-sdk-latest.conf"
614 )
615 osutils.WriteFile(pointerfile, 'LATEST_SDK="%s"' % version_str)
616 self._Upload(pointerfile, remote_pointerfile)
Zdenek Behan33a34112012-09-10 21:07:51 +0200617
Alex Klein1699fab2022-09-08 08:46:06 -0600618 def _GetTargets(self):
619 """Retuns the list of targets to use."""
620 targets = self._slave_targets[:]
621 if self._target:
622 targets.append(self._target)
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700623
Alex Klein1699fab2022-09-08 08:46:06 -0600624 return targets
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700625
Alex Klein1699fab2022-09-08 08:46:06 -0600626 def SyncHostPrebuilts(self, key, git_sync, sync_binhost_conf):
627 """Synchronize host prebuilt files.
David James05bcb2b2011-02-09 09:25:47 -0800628
Alex Klein1699fab2022-09-08 08:46:06 -0600629 This function will sync both the standard host packages, plus the host
630 packages associated with all targets that have been "setup" with the
631 current host's chroot. For instance, if this host has been used to build
632 x86-generic, it will sync the host packages associated with
633 'i686-pc-linux-gnu'. If this host has also been used to build arm-generic,
634 it will also sync the host packages associated with
635 'armv7a-cros-linux-gnueabi'.
David James05bcb2b2011-02-09 09:25:47 -0800636
Alex Klein1699fab2022-09-08 08:46:06 -0600637 Args:
638 key: The variable key to update in the git file.
639 git_sync: If set, update make.conf of target to reference the latest
640 prebuilt packages generated here.
641 sync_binhost_conf: If set, update binhost config file in
642 chromiumos-overlay for the host.
643 """
644 # Slave boards are listed before the master board so that the master board
645 # takes priority (i.e. x86-generic preflight host prebuilts takes priority
646 # over preflight host prebuilts from other builders.)
647 binhost_urls = []
648 for target in self._GetTargets():
649 url_suffix = _REL_HOST_PATH % {
650 "version": self._version,
651 "host_arch": _HOST_ARCH,
652 "target": target,
653 }
654 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James05bcb2b2011-02-09 09:25:47 -0800655
Alex Klein1699fab2022-09-08 08:46:06 -0600656 if self._target == target and not self._skip_upload:
657 # Upload prebuilts.
658 package_path = os.path.join(self._chroot, _HOST_PACKAGES_PATH)
659 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8ece7ee2011-06-29 16:02:30 -0700660
Alex Klein1699fab2022-09-08 08:46:06 -0600661 # Record URL where prebuilts were uploaded.
662 binhost_urls.append(
663 "%s/%s/"
664 % (
665 self._binhost_base_url.rstrip("/"),
666 packages_url_suffix.rstrip("/"),
667 )
668 )
David Jamese2488642011-11-14 16:15:20 -0800669
Alex Klein1699fab2022-09-08 08:46:06 -0600670 binhost = " ".join(binhost_urls)
671 if git_sync:
672 git_file = os.path.join(
673 self._build_path, _PREBUILT_MAKE_CONF[_HOST_ARCH]
674 )
Bob Haarmanc0082602022-09-20 16:12:43 -0700675 RevGitFile(
676 git_file, {key: binhost}, self._report, dryrun=self._dryrun
677 )
Alex Klein1699fab2022-09-08 08:46:06 -0600678 if sync_binhost_conf:
679 binhost_conf = os.path.join(
680 self._binhost_conf_dir, "host", "%s-%s.conf" % (_HOST_ARCH, key)
681 )
682 UpdateBinhostConfFile(binhost_conf, key, binhost)
David Jamesc0f158a2011-02-22 16:07:29 -0800683
Alex Klein1699fab2022-09-08 08:46:06 -0600684 def SyncBoardPrebuilts(
685 self,
686 key,
687 git_sync,
688 sync_binhost_conf,
689 upload_board_tarball,
690 prepackaged_board,
691 toolchains_overlay_tarballs,
692 toolchains_overlay_upload_path,
693 toolchain_tarballs,
694 toolchain_upload_path,
695 ):
696 """Synchronize board prebuilt files.
David Jamesc0f158a2011-02-22 16:07:29 -0800697
Alex Klein1699fab2022-09-08 08:46:06 -0600698 Args:
699 key: The variable key to update in the git file.
700 git_sync: If set, update make.conf of target to reference the latest
701 prebuilt packages generated here.
702 sync_binhost_conf: If set, update binhost config file in
703 chromiumos-overlay for the current board.
704 upload_board_tarball: Include a tarball of the board in our upload.
705 prepackaged_board: A tarball of the board built outside of this script.
706 toolchains_overlay_tarballs: List of toolchains overlay tarball
707 specifications to upload. Items take the form
708 "toolchains_spec:/path/to/tarball".
709 toolchains_overlay_upload_path: Path template under the bucket to place
710 toolchains overlay tarballs.
711 toolchain_tarballs: A list of toolchain tarballs to upload.
712 toolchain_upload_path: Path under the bucket to place toolchain tarballs.
713 """
714 updated_binhosts = set()
715 for target in self._GetTargets():
716 board_path = os.path.join(
717 self._build_path, _BOARD_PATH % {"board": target.board_variant}
718 )
719 package_path = os.path.join(board_path, "packages")
720 url_suffix = _REL_BOARD_PATH % {
721 "target": target,
722 "version": self._version,
723 }
724 packages_url_suffix = "%s/packages" % url_suffix.rstrip("/")
David James8fa34ea2011-04-15 13:00:20 -0700725
Alex Klein1699fab2022-09-08 08:46:06 -0600726 # Process the target board differently if it is the main --board.
727 if self._target == target and not self._skip_upload:
728 # This strips "chroot" prefix because that is sometimes added as the
729 # --prepend-version argument (e.g. by chromiumos-sdk bot).
730 # TODO(build): Clean it up to be less hard-coded.
731 version_str = self._version[len("chroot-") :]
Mike Frysinger9e979b92012-11-29 02:55:09 -0500732
Alex Klein1699fab2022-09-08 08:46:06 -0600733 # Upload board tarballs in the background.
734 if upload_board_tarball:
735 if toolchain_upload_path:
736 toolchain_upload_path %= {"version": version_str}
737 if toolchains_overlay_upload_path:
738 toolchains_overlay_upload_path %= {
739 "version": version_str
740 }
741 tar_process = multiprocessing.Process(
742 target=self._UploadSdkTarball,
743 args=(
744 board_path,
745 url_suffix,
746 prepackaged_board,
747 toolchains_overlay_tarballs,
748 toolchains_overlay_upload_path,
749 toolchain_tarballs,
750 toolchain_upload_path,
751 ),
752 )
753 tar_process.start()
David James8fa34ea2011-04-15 13:00:20 -0700754
Alex Klein1699fab2022-09-08 08:46:06 -0600755 # Upload prebuilts.
756 self._UploadPrebuilt(package_path, packages_url_suffix)
David James8fa34ea2011-04-15 13:00:20 -0700757
Alex Klein1699fab2022-09-08 08:46:06 -0600758 # Make sure we finished uploading the board tarballs.
759 if upload_board_tarball:
760 tar_process.join()
761 assert tar_process.exitcode == 0
David Jamesc0f158a2011-02-22 16:07:29 -0800762
Alex Klein1699fab2022-09-08 08:46:06 -0600763 # Record URL where prebuilts were uploaded.
764 url_value = "%s/%s/" % (
765 self._binhost_base_url.rstrip("/"),
766 packages_url_suffix.rstrip("/"),
767 )
David Jamese2488642011-11-14 16:15:20 -0800768
Alex Klein1699fab2022-09-08 08:46:06 -0600769 if git_sync:
770 git_file = DeterminePrebuiltConfFile(self._build_path, target)
Bob Haarmanc0082602022-09-20 16:12:43 -0700771 RevGitFile(
772 git_file,
773 {key: url_value},
774 self._report,
775 dryrun=self._dryrun,
776 )
Matt Tennante8179042013-10-01 15:47:32 -0700777
Alex Klein1699fab2022-09-08 08:46:06 -0600778 if sync_binhost_conf:
779 # Update the binhost configuration file in git.
780 binhost_conf = os.path.join(
781 self._binhost_conf_dir,
782 "target",
783 "%s-%s.conf" % (target, key),
784 )
785 updated_binhosts.add(binhost_conf)
786 UpdateBinhostConfFile(binhost_conf, key, url_value)
David James05bcb2b2011-02-09 09:25:47 -0800787
Alex Klein1699fab2022-09-08 08:46:06 -0600788 if sync_binhost_conf:
789 # Clear all old binhosts. The files must be left empty in case anybody
790 # is referring to them.
791 all_binhosts = set(
792 glob.glob(
793 os.path.join(
794 self._binhost_conf_dir, "target", "*-%s.conf" % key
795 )
796 )
797 )
798 for binhost_conf in all_binhosts - updated_binhosts:
799 UpdateBinhostConfFile(binhost_conf, key, "")
David Jamesb26b9312014-12-15 11:26:46 -0800800
David James05bcb2b2011-02-09 09:25:47 -0800801
Mike Nicholsa1414162021-04-22 20:07:22 +0000802class _AddSlaveBoardAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600803 """Callback that adds a slave board to the list of slave targets."""
804
805 def __call__(self, parser, namespace, values, option_string=None):
806 getattr(namespace, self.dest).append(BuildTarget(values))
David James4058b0d2011-12-08 21:24:50 -0800807
808
Mike Nicholsa1414162021-04-22 20:07:22 +0000809class _AddSlaveProfileAction(argparse.Action):
Alex Klein1699fab2022-09-08 08:46:06 -0600810 """Callback that adds a slave profile to the list of slave targets."""
811
812 def __call__(self, parser, namespace, values, option_string=None):
813 if not namespace.slave_targets:
814 parser.error("Must specify --slave-board before --slave-profile")
815 if namespace.slave_targets[-1].profile is not None:
816 parser.error("Cannot specify --slave-profile twice for same board")
817 namespace.slave_targets[-1].profile = values
David James4058b0d2011-12-08 21:24:50 -0800818
819
Mike Frysinger86509232014-05-24 13:18:37 -0400820def ParseOptions(argv):
Alex Klein1699fab2022-09-08 08:46:06 -0600821 """Returns options given by the user and the target specified.
Chris Sosa6a5dceb2012-05-14 13:48:56 -0700822
Alex Klein1699fab2022-09-08 08:46:06 -0600823 Args:
824 argv: The args to parse.
Mike Frysinger86509232014-05-24 13:18:37 -0400825
Alex Klein1699fab2022-09-08 08:46:06 -0600826 Returns:
827 A tuple containing a parsed options object and BuildTarget.
828 The target instance is None if no board is specified.
829 """
830 parser = commandline.ArgumentParser()
831 parser.add_argument(
832 "-H",
833 "--binhost-base-url",
834 default=_BINHOST_BASE_URL,
835 help="Base URL to use for binhost in make.conf updates",
836 )
837 parser.add_argument(
838 "--previous-binhost-url",
839 action="append",
840 default=[],
841 help="Previous binhost URL",
842 )
843 parser.add_argument(
844 "-b", "--board", help="Board type that was built on this machine"
845 )
846 parser.add_argument(
847 "-B",
848 "--prepackaged-tarball",
849 type="path",
850 help="Board tarball prebuilt outside of this script.",
851 )
852 parser.add_argument(
853 "--toolchains-overlay-tarball",
854 dest="toolchains_overlay_tarballs",
855 action="append",
856 default=[],
857 help="Toolchains overlay tarball specification to "
858 "upload. Takes the form "
859 '"toolchains_spec:/path/to/tarball".',
860 )
861 parser.add_argument(
862 "--toolchains-overlay-upload-path",
863 default="",
864 help="Path template for uploading toolchains overlays.",
865 )
866 parser.add_argument(
867 "--toolchain-tarball",
868 dest="toolchain_tarballs",
869 action="append",
870 default=[],
871 help="Redistributable toolchain tarball.",
872 )
873 parser.add_argument(
874 "--toolchain-upload-path",
875 default="",
876 help="Path to place toolchain tarballs in the sdk tree.",
877 )
878 parser.add_argument(
879 "--profile", help="Profile that was built on this machine"
880 )
881 parser.add_argument(
882 "--slave-board",
883 default=[],
884 action=_AddSlaveBoardAction,
885 dest="slave_targets",
886 help="Board type that was built on a slave machine. To "
887 "add a profile to this board, use --slave-profile.",
888 )
889 parser.add_argument(
890 "--slave-profile",
891 action=_AddSlaveProfileAction,
892 help="Board profile that was built on a slave machine. "
893 "Applies to previous slave board.",
894 )
895 parser.add_argument(
896 "-p",
897 "--build-path",
898 required=True,
899 help="Path to the directory containing the chroot",
900 )
901 parser.add_argument(
902 "--chroot",
903 help="Path where the chroot is located. "
904 "(Default: {build_path}/chroot)",
905 )
906 parser.add_argument(
Bob Haarmanc0082602022-09-20 16:12:43 -0700907 "--output",
908 type=Path,
909 help="Write a JSON report to the specified file. "
910 "(Default is not to write the report.)",
911 )
912 parser.add_argument(
Alex Klein1699fab2022-09-08 08:46:06 -0600913 "--packages",
914 action="append",
915 default=[],
916 help="Only include the specified packages. "
917 "(Default is to include all packages.)",
918 )
919 parser.add_argument(
920 "-s",
921 "--sync-host",
922 default=False,
923 action="store_true",
924 help="Sync host prebuilts",
925 )
926 parser.add_argument(
927 "-g",
928 "--git-sync",
929 default=False,
930 action="store_true",
931 help="Enable git version sync (This commits to a repo.) "
932 "This is used by full builders to commit directly "
933 "to board overlays.",
934 )
935 parser.add_argument("-u", "--upload", help="Upload location")
936 parser.add_argument(
937 "-V",
938 "--prepend-version",
939 help="Add an identifier to the front of the version",
940 )
941 parser.add_argument(
942 "-f",
943 "--filters",
944 action="store_true",
945 default=False,
946 help="Turn on filtering of private ebuild packages",
947 )
948 parser.add_argument(
949 "-k",
950 "--key",
951 default="PORTAGE_BINHOST",
952 help="Key to update in make.conf / binhost.conf",
953 )
954 parser.add_argument("--set-version", help="Specify the version string")
955 parser.add_argument(
956 "--sync-binhost-conf",
957 default=False,
958 action="store_true",
959 help="Update binhost.conf in chromiumos-overlay or "
960 "chromeos-overlay. Commit the changes, but don't "
961 "push them. This is used for preflight binhosts.",
962 )
963 parser.add_argument(
964 "--binhost-conf-dir",
965 help="Directory to commit binhost config with " "--sync-binhost-conf.",
966 )
967 parser.add_argument(
968 "-P",
969 "--private",
970 action="store_true",
971 default=False,
972 help="Mark gs:// uploads as private.",
973 )
974 parser.add_argument(
975 "--skip-upload",
976 action="store_true",
977 default=False,
978 help="Skip upload step.",
979 )
980 parser.add_argument(
981 "--upload-board-tarball",
982 action="store_true",
983 default=False,
984 help="Upload board tarball to Google Storage.",
985 )
986 parser.add_argument(
987 "-n",
988 "--dry-run",
989 dest="dryrun",
990 action="store_true",
991 default=False,
992 help="Don't push or upload prebuilts.",
993 )
David James8c846492011-01-25 17:07:29 -0800994
Alex Klein1699fab2022-09-08 08:46:06 -0600995 options = parser.parse_args(argv)
996 if not options.upload and not options.skip_upload:
997 parser.error("you need to provide an upload location using -u")
998 if not options.set_version and options.skip_upload:
999 parser.error(
1000 "If you are using --skip-upload, you must specify a "
1001 "version number using --set-version."
1002 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001003
Alex Klein1699fab2022-09-08 08:46:06 -06001004 target = None
1005 if options.board:
1006 target = BuildTarget(options.board, options.profile)
Chris Sosa6a5dceb2012-05-14 13:48:56 -07001007
Alex Klein1699fab2022-09-08 08:46:06 -06001008 if target in options.slave_targets:
1009 parser.error("--board/--profile must not also be a slave target.")
David Jamese2488642011-11-14 16:15:20 -08001010
Alex Klein1699fab2022-09-08 08:46:06 -06001011 if len(set(options.slave_targets)) != len(options.slave_targets):
1012 parser.error("--slave-boards must not have duplicates.")
David Jamese2488642011-11-14 16:15:20 -08001013
Alex Klein1699fab2022-09-08 08:46:06 -06001014 if options.slave_targets and options.git_sync:
1015 parser.error("--slave-boards is not compatible with --git-sync")
David Jamese2488642011-11-14 16:15:20 -08001016
Alex Klein1699fab2022-09-08 08:46:06 -06001017 if (
1018 options.upload_board_tarball
1019 and options.skip_upload
1020 and options.board == "amd64-host"
1021 ):
1022 parser.error(
1023 "--skip-upload is not compatible with "
1024 "--upload-board-tarball and --board=amd64-host"
1025 )
David James8fa34ea2011-04-15 13:00:20 -07001026
Alex Klein1699fab2022-09-08 08:46:06 -06001027 if (
1028 options.upload_board_tarball
1029 and not options.skip_upload
1030 and not options.upload.startswith("gs://")
1031 ):
1032 parser.error(
1033 "--upload-board-tarball only works with gs:// URLs.\n"
1034 "--upload must be a gs:// URL."
1035 )
David James8fa34ea2011-04-15 13:00:20 -07001036
Alex Klein1699fab2022-09-08 08:46:06 -06001037 if options.upload_board_tarball and options.prepackaged_tarball is None:
1038 parser.error("--upload-board-tarball requires --prepackaged-tarball")
Zdenek Behan86c15e92012-10-13 00:55:47 +02001039
Alex Klein1699fab2022-09-08 08:46:06 -06001040 if options.private:
1041 if options.sync_host:
1042 parser.error(
1043 "--private and --sync-host/-s cannot be specified "
1044 "together; we do not support private host prebuilts"
1045 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001046
Alex Klein1699fab2022-09-08 08:46:06 -06001047 if not options.upload or not options.upload.startswith("gs://"):
1048 parser.error(
1049 "--private is only valid for gs:// URLs; "
1050 "--upload must be a gs:// URL."
1051 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001052
Alex Klein1699fab2022-09-08 08:46:06 -06001053 if options.binhost_base_url != _BINHOST_BASE_URL:
1054 parser.error(
1055 "when using --private the --binhost-base-url "
1056 "is automatically derived."
1057 )
David James27fa7d12011-06-29 17:24:14 -07001058
Alex Klein1699fab2022-09-08 08:46:06 -06001059 if options.sync_binhost_conf and not options.binhost_conf_dir:
1060 parser.error("--sync-binhost-conf requires --binhost-conf-dir")
David Jamesc31168e2014-06-05 14:40:05 -07001061
Alex Klein1699fab2022-09-08 08:46:06 -06001062 if (
1063 options.toolchains_overlay_tarballs
1064 and not options.toolchains_overlay_upload_path
1065 ):
1066 parser.error(
1067 "--toolchains-overlay-tarball requires "
1068 "--toolchains-overlay-upload-path"
1069 )
Gilad Arnoldad333182015-05-27 15:50:41 -07001070
Alex Klein1699fab2022-09-08 08:46:06 -06001071 return options, target
David Jamesc0f158a2011-02-22 16:07:29 -08001072
Mike Frysingercc838832014-05-24 13:10:30 -04001073
Mike Frysinger86509232014-05-24 13:18:37 -04001074def main(argv):
Bob Haarmanc0082602022-09-20 16:12:43 -07001075 # We accumulate information about actions taken and report it at the end
1076 # if asked to do so. Currently, this only records CL creation, which
1077 # is the only thing we need for now.
1078 report = {}
1079
Alex Klein1699fab2022-09-08 08:46:06 -06001080 # Set umask so that files created as root are readable.
1081 os.umask(0o22)
David Jamesdb401072011-06-10 12:17:16 -07001082
Alex Klein1699fab2022-09-08 08:46:06 -06001083 options, target = ParseOptions(argv)
David Jamesc0f158a2011-02-22 16:07:29 -08001084
Alex Klein1699fab2022-09-08 08:46:06 -06001085 # Calculate a list of Packages index files to compare against. Whenever we
1086 # upload a package, we check to make sure it's not already stored in one of
1087 # the packages files we uploaded. This list of packages files might contain
1088 # both board and host packages.
1089 pkg_indexes = _GrabAllRemotePackageIndexes(options.previous_binhost_url)
David James8c846492011-01-25 17:07:29 -08001090
Alex Klein1699fab2022-09-08 08:46:06 -06001091 if options.set_version:
1092 version = options.set_version
1093 else:
1094 version = GetVersion()
Matt Tennante8179042013-10-01 15:47:32 -07001095
Alex Klein1699fab2022-09-08 08:46:06 -06001096 if options.prepend_version:
1097 version = "%s-%s" % (options.prepend_version, version)
David Jamesc0f158a2011-02-22 16:07:29 -08001098
Alex Klein1699fab2022-09-08 08:46:06 -06001099 acl = "public-read"
1100 binhost_base_url = options.binhost_base_url
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001101
Alex Klein1699fab2022-09-08 08:46:06 -06001102 if options.private:
1103 binhost_base_url = options.upload
1104 if target:
1105 acl = portage_util.FindOverlayFile(
1106 _GOOGLESTORAGE_GSUTIL_FILE,
1107 board=target.board_variant,
1108 buildroot=options.build_path,
1109 )
1110 if acl is None:
1111 cros_build_lib.Die(
1112 "No Google Storage ACL file %s found in %s overlay.",
1113 _GOOGLESTORAGE_GSUTIL_FILE,
1114 target.board_variant,
1115 )
Scott Zawalskiab1bed32011-03-16 15:24:24 -07001116
Alex Klein1699fab2022-09-08 08:46:06 -06001117 binhost_conf_dir = None
1118 if options.binhost_conf_dir:
1119 binhost_conf_dir = os.path.join(
1120 options.build_path, options.binhost_conf_dir
1121 )
David Jamesb26b9312014-12-15 11:26:46 -08001122
Alex Klein1699fab2022-09-08 08:46:06 -06001123 uploader = PrebuiltUploader(
1124 options.upload,
1125 acl,
1126 binhost_base_url,
1127 pkg_indexes,
1128 options.build_path,
1129 options.packages,
1130 options.skip_upload,
1131 binhost_conf_dir,
1132 options.dryrun,
1133 target,
1134 options.slave_targets,
1135 version,
Bob Haarmanc0082602022-09-20 16:12:43 -07001136 report,
Alex Klein1699fab2022-09-08 08:46:06 -06001137 options.chroot,
1138 )
David Jamesc0f158a2011-02-22 16:07:29 -08001139
Alex Klein1699fab2022-09-08 08:46:06 -06001140 if options.sync_host:
1141 uploader.SyncHostPrebuilts(
1142 options.key, options.git_sync, options.sync_binhost_conf
1143 )
David James8c846492011-01-25 17:07:29 -08001144
Alex Klein1699fab2022-09-08 08:46:06 -06001145 if options.board or options.slave_targets:
1146 uploader.SyncBoardPrebuilts(
1147 options.key,
1148 options.git_sync,
1149 options.sync_binhost_conf,
1150 options.upload_board_tarball,
1151 options.prepackaged_tarball,
1152 options.toolchains_overlay_tarballs,
1153 options.toolchains_overlay_upload_path,
1154 options.toolchain_tarballs,
1155 options.toolchain_upload_path,
1156 )
Bob Haarmanc0082602022-09-20 16:12:43 -07001157
1158 if options.json:
1159 with options.json.open("w") as f:
1160 pformat.json(report, fp=f)