blob: 61047ec6a0c4ecc78d4858166fa0485d55fb3f09 [file] [log] [blame]
Alex Kleineb77ffa2019-05-28 14:47:44 -06001# -*- coding: utf-8 -*-
2# Copyright 2019 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Package utility functionality."""
7
8from __future__ import print_function
9
Yaakov Shaul730814a2019-09-10 13:58:25 -060010import collections
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -060011import fileinput
Alex Klein87531182019-08-12 15:23:37 -060012import functools
Yaakov Shaul395ae832019-09-09 14:45:32 -060013import json
Evan Hernandezb51f1522019-08-15 11:29:40 -060014import os
Michael Mortensenb70e8a82019-10-10 18:43:41 -060015import re
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -060016import sys
Alex Klein87531182019-08-12 15:23:37 -060017
Alex Klein149fd3b2019-12-16 16:01:05 -070018import six
19
Andrew Lamb2bde9e42019-11-04 13:24:09 -070020from google.protobuf import json_format
Yaakov Shaul730814a2019-09-10 13:58:25 -060021
Andrew Lamb2bde9e42019-11-04 13:24:09 -070022from chromite.api.gen.config import replication_config_pb2
Michael Mortensen9fdb14b2019-10-17 11:17:30 -060023from chromite.cbuildbot import manifest_version
Alex Kleineb77ffa2019-05-28 14:47:44 -060024from chromite.lib import constants
Evan Hernandezb51f1522019-08-15 11:29:40 -060025from chromite.lib import cros_build_lib
Alex Klein4de25e82019-08-05 15:58:39 -060026from chromite.lib import cros_logging as logging
Alex Kleineb77ffa2019-05-28 14:47:44 -060027from chromite.lib import git
Michael Mortensende716a12020-05-15 11:27:00 -060028from chromite.lib import image_lib
Michael Mortensenb70e8a82019-10-10 18:43:41 -060029from chromite.lib import osutils
Alex Kleineb77ffa2019-05-28 14:47:44 -060030from chromite.lib import portage_util
Andrew Lamb2bde9e42019-11-04 13:24:09 -070031from chromite.lib import replication_lib
Alex Kleind6195b62019-08-06 16:01:16 -060032from chromite.lib import uprev_lib
Alex Kleineb77ffa2019-05-28 14:47:44 -060033
Alex Klein36b117f2019-09-30 15:13:46 -060034if cros_build_lib.IsInsideChroot():
35 from chromite.service import dependency
36
Mike Frysingerbafb3182020-02-21 03:15:43 -050037
Alex Klein87531182019-08-12 15:23:37 -060038# Registered handlers for uprevving versioned packages.
39_UPREV_FUNCS = {}
40
Alex Kleineb77ffa2019-05-28 14:47:44 -060041
42class Error(Exception):
43 """Module's base error class."""
44
45
Alex Klein4de25e82019-08-05 15:58:39 -060046class UnknownPackageError(Error):
47 """Uprev attempted for a package without a registered handler."""
48
49
Alex Kleineb77ffa2019-05-28 14:47:44 -060050class UprevError(Error):
51 """An error occurred while uprevving packages."""
52
53
Michael Mortensenb70e8a82019-10-10 18:43:41 -060054class NoAndroidVersionError(Error):
55 """An error occurred while trying to determine the android version."""
56
57
58class NoAndroidBranchError(Error):
59 """An error occurred while trying to determine the android branch."""
60
61
62class NoAndroidTargetError(Error):
63 """An error occurred while trying to determine the android target."""
64
65
Alex Klein4de25e82019-08-05 15:58:39 -060066class AndroidIsPinnedUprevError(UprevError):
67 """Raised when we try to uprev while Android is pinned."""
68
69 def __init__(self, new_android_atom):
70 """Initialize a AndroidIsPinnedUprevError.
71
72 Args:
73 new_android_atom: The Android atom that we failed to
74 uprev to, due to Android being pinned.
75 """
76 assert new_android_atom
77 msg = ('Failed up uprev to Android version %s as Android was pinned.' %
78 new_android_atom)
79 super(AndroidIsPinnedUprevError, self).__init__(msg)
80 self.new_android_atom = new_android_atom
Alex Klein87531182019-08-12 15:23:37 -060081
82
Yaakov Shaul1eafe832019-09-10 16:50:26 -060083class EbuildManifestError(Error):
84 """Error when running ebuild manifest."""
85
86
Andrew Lamb9563a152019-12-04 11:42:18 -070087class GeneratedCrosConfigFilesError(Error):
88 """Error when cros_config_schema does not produce expected files"""
89
90 def __init__(self, expected_files, found_files):
91 msg = ('Expected to find generated C files: %s. Actually found: %s' %
92 (expected_files, found_files))
93 super(GeneratedCrosConfigFilesError, self).__init__(msg)
94
Alex Klein7a3a7dd2020-01-08 16:44:38 -070095
Yaakov Shaul730814a2019-09-10 13:58:25 -060096UprevVersionedPackageModifications = collections.namedtuple(
97 'UprevVersionedPackageModifications', ('new_version', 'files'))
Alex Klein34afcbc2019-08-22 16:14:31 -060098
Yaakov Shaul730814a2019-09-10 13:58:25 -060099
100class UprevVersionedPackageResult(object):
101 """Data object for uprev_versioned_package."""
102
103 def __init__(self):
104 self.modified = []
105
106 def add_result(self, new_version, modified_files):
107 """Adds version/ebuilds tuple to result.
108
109 Args:
110 new_version: New version number of package.
111 modified_files: List of files modified for the given version.
112 """
113 result = UprevVersionedPackageModifications(new_version, modified_files)
114 self.modified.append(result)
115 return self
Alex Klein34afcbc2019-08-22 16:14:31 -0600116
117 @property
118 def uprevved(self):
Yaakov Shaul730814a2019-09-10 13:58:25 -0600119 return bool(self.modified)
Alex Klein34afcbc2019-08-22 16:14:31 -0600120
121
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600122def patch_ebuild_vars(ebuild_path, variables):
123 """Updates variables in ebuild.
124
125 Use this function rather than portage_util.EBuild.UpdateEBuild when you
126 want to preserve the variable position and quotes within the ebuild.
127
128 Args:
129 ebuild_path: The path of the ebuild.
130 variables: Dictionary of variables to update in ebuild.
131 """
132 try:
133 for line in fileinput.input(ebuild_path, inplace=1):
134 varname, eq, _ = line.partition('=')
135 if eq == '=' and varname.strip() in variables:
136 value = variables[varname]
137 sys.stdout.write('%s="%s"\n' % (varname, value))
138 else:
139 sys.stdout.write(line)
140 finally:
141 fileinput.close()
142
143
Alex Klein87531182019-08-12 15:23:37 -0600144def uprevs_versioned_package(package):
145 """Decorator to register package uprev handlers."""
146 assert package
147
148 def register(func):
149 """Registers |func| as a handler for |package|."""
150 _UPREV_FUNCS[package] = func
151
152 @functools.wraps(func)
153 def pass_through(*args, **kwargs):
154 return func(*args, **kwargs)
155
156 return pass_through
157
158 return register
159
160
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700161def uprev_android(tracking_branch,
162 android_package,
163 android_build_branch,
164 chroot,
165 build_targets=None,
Shao-Chuan Lee9c39e0c2020-04-24 11:40:34 +0900166 android_version=None):
Alex Klein4de25e82019-08-05 15:58:39 -0600167 """Returns the portage atom for the revved Android ebuild - see man emerge."""
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700168 command = [
169 'cros_mark_android_as_stable',
170 '--tracking_branch=%s' % tracking_branch,
171 '--android_package=%s' % android_package,
172 '--android_build_branch=%s' % android_build_branch,
173 ]
Alex Klein4de25e82019-08-05 15:58:39 -0600174 if build_targets:
175 command.append('--boards=%s' % ':'.join(bt.name for bt in build_targets))
176 if android_version:
177 command.append('--force_version=%s' % android_version)
Alex Klein4de25e82019-08-05 15:58:39 -0600178
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700179 result = cros_build_lib.run(
180 command,
181 stdout=True,
182 enter_chroot=True,
Mike Frysinger88d96362020-02-14 19:05:45 -0500183 encoding='utf-8',
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700184 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600185
Mike Frysinger88d96362020-02-14 19:05:45 -0500186 portage_atom_string = result.stdout.strip()
187 android_atom = None
188 if portage_atom_string:
189 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
Alex Klein4de25e82019-08-05 15:58:39 -0600190 if not android_atom:
191 logging.info('Found nothing to rev.')
192 return None
193
194 for target in build_targets or []:
195 # Sanity check: We should always be able to merge the version of
196 # Android we just unmasked.
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700197 command = ['emerge-%s' % target.name, '-p', '--quiet', '=%s' % android_atom]
Alex Klein4de25e82019-08-05 15:58:39 -0600198 try:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700199 cros_build_lib.run(
200 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600201 except cros_build_lib.RunCommandError:
202 logging.error(
203 'Cannot emerge-%s =%s\nIs Android pinned to an older '
204 'version?', target, android_atom)
205 raise AndroidIsPinnedUprevError(android_atom)
206
207 return android_atom
208
209
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700210def uprev_build_targets(build_targets,
211 overlay_type,
212 chroot=None,
Alex Kleineb77ffa2019-05-28 14:47:44 -0600213 output_dir=None):
214 """Uprev the set provided build targets, or all if not specified.
215
216 Args:
Alex Klein2960c752020-03-09 13:43:38 -0600217 build_targets (list[build_target_lib.BuildTarget]|None): The build targets
Alex Kleineb77ffa2019-05-28 14:47:44 -0600218 whose overlays should be uprevved, empty or None for all.
219 overlay_type (str): One of the valid overlay types except None (see
220 constants.VALID_OVERLAYS).
221 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
222 output_dir (str|None): The path to optionally dump result files.
223 """
224 # Need a valid overlay, but exclude None.
225 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
226
227 if build_targets:
228 overlays = portage_util.FindOverlaysForBoards(
229 overlay_type, boards=[t.name for t in build_targets])
230 else:
231 overlays = portage_util.FindOverlays(overlay_type)
232
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700233 return uprev_overlays(
234 overlays,
235 build_targets=build_targets,
236 chroot=chroot,
237 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600238
239
240def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
241 """Uprev the given overlays.
242
243 Args:
244 overlays (list[str]): The list of overlay paths.
Alex Klein2960c752020-03-09 13:43:38 -0600245 build_targets (list[build_target_lib.BuildTarget]|None): The build targets
Alex Kleineb77ffa2019-05-28 14:47:44 -0600246 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
247 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
248 output_dir (str|None): The path to optionally dump result files.
249
250 Returns:
251 list[str] - The paths to all of the modified ebuild files. This includes the
252 new files that were added (i.e. the new versions) and all of the removed
253 files (i.e. the old versions).
254 """
255 assert overlays
256
257 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
258
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700259 uprev_manager = uprev_lib.UprevOverlayManager(
260 overlays,
261 manifest,
262 build_targets=build_targets,
263 chroot=chroot,
264 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600265 uprev_manager.uprev()
266
267 return uprev_manager.modified_ebuilds
268
269
Alex Klein87531182019-08-12 15:23:37 -0600270def uprev_versioned_package(package, build_targets, refs, chroot):
271 """Call registered uprev handler function for the package.
272
273 Args:
274 package (portage_util.CPV): The package being uprevved.
Alex Klein2960c752020-03-09 13:43:38 -0600275 build_targets (list[build_target_lib.BuildTarget]): The build targets to
Alex Klein87531182019-08-12 15:23:37 -0600276 clean on a successful uprev.
277 refs (list[uprev_lib.GitRef]):
278 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
279
280 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600281 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600282 """
283 assert package
284
285 if package.cp not in _UPREV_FUNCS:
286 raise UnknownPackageError(
287 'Package "%s" does not have a registered handler.' % package.cp)
288
Andrew Lambea9a8a22019-12-12 14:03:43 -0700289 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
Alex Klein87531182019-08-12 15:23:37 -0600290
291
Navil Perezf57ba872020-06-04 22:38:37 +0000292@uprevs_versioned_package('media-libs/virglrenderer')
293def uprev_virglrenderer(_build_targets, refs, _chroot):
294 """Updates virglrenderer ebuilds.
295
296 See: uprev_versioned_package.
297
298 Returns:
299 UprevVersionedPackageResult: The result of updating virglrenderer ebuilds.
300 """
Navil Perezf57ba872020-06-04 22:38:37 +0000301 overlay = os.path.join(constants.SOURCE_ROOT,
302 constants.CHROMIUMOS_OVERLAY_DIR)
George Engelbrechte73f2782020-06-10 14:10:46 -0600303 repo_path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
304 'virglrenderer')
305 manifest = git.ManifestCheckout.Cached(repo_path)
Navil Perezf57ba872020-06-04 22:38:37 +0000306
307 uprev_manager = uprev_lib.UprevOverlayManager([overlay], manifest)
308 # TODO(crbug.com/1066242): Ebuilds for virglrenderer are currently
309 # blacklisted. Do not force uprevs after builder is stable and ebuilds are no
310 # longer blacklisted.
311 uprev_manager.uprev(package_list=['media-libs/virglrenderer'], force=True)
312
George Engelbrechte73f2782020-06-10 14:10:46 -0600313 updated_files = uprev_manager.modified_ebuilds
Navil Perezf57ba872020-06-04 22:38:37 +0000314 result = UprevVersionedPackageResult()
315 result.add_result(refs[0].revision, updated_files)
316 return result
317
318
Yaakov Shaul395ae832019-09-09 14:45:32 -0600319@uprevs_versioned_package('afdo/kernel-profiles')
320def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600321 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600322
323 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600324
325 Raises:
326 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600327 """
328 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
329 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
330
David Burger92485342019-09-10 17:52:45 -0600331 with open(path, 'r') as f:
332 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600333
Yaakov Shaul730814a2019-09-10 13:58:25 -0600334 result = UprevVersionedPackageResult()
Yaakov Shaul395ae832019-09-09 14:45:32 -0600335 for version, version_info in versions.items():
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600336 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
337 'sys-kernel', version)
338 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
339 '%s-9999.ebuild' % version)
Yaakov Shaula187b152019-09-11 12:41:32 -0600340 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
341 '%s-9999.ebuild' % version)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600342 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600343 patch_ebuild_vars(ebuild_path,
344 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600345
346 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600347 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400348 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600349 except cros_build_lib.RunCommandError as e:
350 raise EbuildManifestError(
351 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600352 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600353
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600354 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600355
Yaakov Shaul730814a2019-09-10 13:58:25 -0600356 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
357
358 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600359
360
Trent Begineb624182020-07-14 10:09:45 -0600361@uprevs_versioned_package('chromeos-base/termina-dlc')
362def uprev_termina_dlc(_build_targets, _refs, chroot):
363 """Updates shared termina-dlc ebuild - chromeos-base/termina-dlc.
Trent Beginaf51f1b2020-03-09 17:35:31 -0600364
365 See: uprev_versioned_package.
366 """
Trent Begineb624182020-07-14 10:09:45 -0600367 package = 'termina-dlc'
Trent Beginaf51f1b2020-03-09 17:35:31 -0600368 package_path = os.path.join(constants.CHROMIUMOS_OVERLAY_DIR, 'chromeos-base',
369 package)
Patrick Meiring5897add2020-09-16 16:30:17 +1000370
371 version_pin_src_path = _get_version_pin_src_path(package_path)
372 version_no_rev = osutils.ReadFile(version_pin_src_path).strip()
373
374 return uprev_ebuild_from_pin(package_path, version_no_rev, chroot)
375
376
377@uprevs_versioned_package('app-emulation/parallels-desktop')
378def uprev_parallels_desktop(_build_targets, _refs, chroot):
379 """Updates Parallels Desktop ebuild - app-emulation/parallels-desktop.
380
381 See: uprev_versioned_package
382
383 Returns:
384 UprevVersionedPackageResult: The result.
385 """
386 package = 'parallels-desktop'
387 package_path = os.path.join(constants.CHROMEOS_PARTNER_OVERLAY_DIR,
388 'app-emulation', package)
389 version_pin_src_path = _get_version_pin_src_path(package_path)
390
391 # Expect a JSON blob like the following:
392 # {
393 # "version": "1.2.3",
394 # "test_image": { "url": "...", "size": 12345678,
395 # "sha256sum": "<32 bytes of hexadecimal>" }
396 # }
397 with open(version_pin_src_path, 'r') as f:
398 pinned = json.load(f)
399
400 if 'version' not in pinned or 'test_image' not in pinned:
401 raise UprevError('VERSION-PIN for %s missing version and/or '
402 'test_image field' % package)
403
404 version = pinned['version']
405 if not isinstance(version, str):
406 raise UprevError('version in VERSION-PIN for %s not a string' % package)
407
408 # Update the ebuild.
409 result = uprev_ebuild_from_pin(package_path, version, chroot)
410
411 # Update the VM image used for testing.
412 test_image_path = ('src/platform/tast-tests-pita/src/chromiumos/tast/local/'
413 'bundles/pita/pita/data/pluginvm_image.zip.external')
414 test_image_src_path = os.path.join(constants.SOURCE_ROOT, test_image_path)
415 with open(test_image_src_path, 'w') as f:
416 json.dump(pinned['test_image'], f, indent=2)
417 result.add_result(version, [test_image_src_path])
418
419 return result
Trent Beginaf51f1b2020-03-09 17:35:31 -0600420
421
Trent Begin315d9d92019-12-03 21:55:53 -0700422@uprevs_versioned_package('chromeos-base/chromeos-dtc-vm')
Trent Beginaf51f1b2020-03-09 17:35:31 -0600423def uprev_sludge(_build_targets, _refs, chroot):
Trent Begin315d9d92019-12-03 21:55:53 -0700424 """Updates sludge VM - chromeos-base/chromeos-dtc-vm.
425
426 See: uprev_versioned_package.
427 """
428 package = 'chromeos-dtc-vm'
Trent Begind943df92020-02-25 10:30:10 -0700429 package_path = os.path.join('src', 'private-overlays',
430 'project-wilco-private', 'chromeos-base', package)
Patrick Meiring5897add2020-09-16 16:30:17 +1000431 version_pin_src_path = _get_version_pin_src_path(package_path)
432 version_no_rev = osutils.ReadFile(version_pin_src_path).strip()
Trent Begin315d9d92019-12-03 21:55:53 -0700433
Patrick Meiring5897add2020-09-16 16:30:17 +1000434 return uprev_ebuild_from_pin(package_path, version_no_rev, chroot)
Trent Begin315d9d92019-12-03 21:55:53 -0700435
436
Patrick Meiring5897add2020-09-16 16:30:17 +1000437def _get_version_pin_src_path(package_path):
438 """Returns the path to the VERSION-PIN file for the given package."""
439 return os.path.join(constants.SOURCE_ROOT, package_path, 'VERSION-PIN')
440
441
442def uprev_ebuild_from_pin(package_path, version_no_rev, chroot):
Trent Begin315d9d92019-12-03 21:55:53 -0700443 """Changes the package ebuild's version to match the version pin file.
444
445 Args:
446 package_path: The path of the package relative to the src root. This path
Patrick Meiring5897add2020-09-16 16:30:17 +1000447 should contain a stable and an unstable ebuild with the same name
448 as the package.
449 version_no_rev: The version string to uprev to (excluding revision). The
450 ebuild's version will be directly set to this number.
Trent Begin6daa8702020-01-29 14:58:12 -0700451 chroot (chroot_lib.Chroot): specify a chroot to enter.
Trent Begin315d9d92019-12-03 21:55:53 -0700452
453 Returns:
454 UprevVersionedPackageResult: The result.
455 """
456 package = os.path.basename(package_path)
Trent Begind943df92020-02-25 10:30:10 -0700457
458 package_src_path = os.path.join(constants.SOURCE_ROOT, package_path)
459 ebuild_paths = list(portage_util.EBuild.List(package_src_path))
Fergus Dall2209d0b2020-08-06 11:51:43 +1000460 stable_ebuild = None
461 unstable_ebuild = None
462 for path in ebuild_paths:
463 ebuild = portage_util.EBuild(path)
464 if ebuild.is_stable:
465 stable_ebuild = ebuild
466 else:
467 unstable_ebuild = ebuild
468
469 if stable_ebuild is None:
470 raise UprevError('No stable ebuild found for %s' % package)
471 if unstable_ebuild is None:
472 raise UprevError('No unstable ebuild found for %s' % package)
473 if len(ebuild_paths) > 2:
474 raise UprevError('Found too many ebuilds for %s: '
475 'expected one stable and one unstable' % package)
Trent Begin315d9d92019-12-03 21:55:53 -0700476
Fergus Dall2209d0b2020-08-06 11:51:43 +1000477 # If the new version is the same as the old version, bump the revision number,
478 # otherwise reset it to 1
Patrick Meiring5897add2020-09-16 16:30:17 +1000479 if version_no_rev == stable_ebuild.version_no_rev:
480 version = '%s-r%d' % (version_no_rev, stable_ebuild.current_revision + 1)
Fergus Dall2209d0b2020-08-06 11:51:43 +1000481 else:
Patrick Meiring5897add2020-09-16 16:30:17 +1000482 version = version_no_rev + '-r1'
Fergus Dall2209d0b2020-08-06 11:51:43 +1000483
Fergus Dall6d5b7472020-08-13 21:24:28 +1000484 new_ebuild_path = os.path.join(package_path,
485 '%s-%s.ebuild' % (package, version))
486 new_ebuild_src_path = os.path.join(constants.SOURCE_ROOT,
487 new_ebuild_path)
Trent Begin4a11a632020-02-28 12:59:58 -0700488 manifest_src_path = os.path.join(package_src_path, 'Manifest')
Fergus Dall2209d0b2020-08-06 11:51:43 +1000489
490 portage_util.EBuild.MarkAsStable(unstable_ebuild.ebuild_path,
491 new_ebuild_src_path, {})
492 osutils.SafeUnlink(stable_ebuild.ebuild_path)
Trent Begin315d9d92019-12-03 21:55:53 -0700493
Trent Begin6daa8702020-01-29 14:58:12 -0700494 try:
Fergus Dall6d5b7472020-08-13 21:24:28 +1000495 # UpdateEbuildManifest runs inside the chroot and therefore needs a
496 # chroot-relative path.
497 new_ebuild_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
498 new_ebuild_path)
499 portage_util.UpdateEbuildManifest(new_ebuild_chroot_path, chroot=chroot)
Trent Begin6daa8702020-01-29 14:58:12 -0700500 except cros_build_lib.RunCommandError as e:
Trent Begind943df92020-02-25 10:30:10 -0700501 raise EbuildManifestError(
Trent Begin6daa8702020-01-29 14:58:12 -0700502 'Unable to update manifest for %s: %s' % (package, e.stderr))
503
Trent Begin315d9d92019-12-03 21:55:53 -0700504 result = UprevVersionedPackageResult()
Trent Begin4a11a632020-02-28 12:59:58 -0700505 result.add_result(version,
Fergus Dall2209d0b2020-08-06 11:51:43 +1000506 [new_ebuild_src_path,
507 stable_ebuild.ebuild_path,
508 manifest_src_path])
Trent Begin315d9d92019-12-03 21:55:53 -0700509 return result
510
511
Alex Klein87531182019-08-12 15:23:37 -0600512@uprevs_versioned_package(constants.CHROME_CP)
Andrew Lambea9a8a22019-12-12 14:03:43 -0700513def uprev_chrome(build_targets, refs, chroot):
Alex Klein87531182019-08-12 15:23:37 -0600514 """Uprev chrome and its related packages.
515
516 See: uprev_versioned_package.
517 """
518 # Determine the version from the refs (tags), i.e. the chrome versions are the
519 # tag names.
520 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
Chris McDonald25881af2020-05-12 03:17:53 -0600521 logging.debug('Chrome version determined from refs: %s', chrome_version)
Alex Klein87531182019-08-12 15:23:37 -0600522
523 uprev_manager = uprev_lib.UprevChromeManager(
524 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600525 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600526 # Start with chrome itself, as we can't do anything else unless chrome
527 # uprevs successfully.
Chris McDonald25881af2020-05-12 03:17:53 -0600528 # TODO(crbug.com/1080429): Handle all possible outcomes of a Chrome uprev
529 # attempt. The expected behavior is documented in the following table:
530 #
531 # Outcome of Chrome uprev attempt:
532 # NEWER_VERSION_EXISTS:
533 # Do nothing.
534 # SAME_VERSION_EXISTS or REVISION_BUMP:
535 # Uprev followers
536 # Assert not VERSION_BUMP (any other outcome is fine)
537 # VERSION_BUMP or NEW_EBUILD_CREATED:
538 # Uprev followers
539 # Assert that Chrome & followers are at same package version
Alex Klein87531182019-08-12 15:23:37 -0600540 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600541 return result
Alex Klein87531182019-08-12 15:23:37 -0600542
543 # With a successful chrome rev, also uprev related packages.
544 for package in constants.OTHER_CHROME_PACKAGES:
545 uprev_manager.uprev(package)
546
David Burger37f48672019-09-18 17:07:56 -0600547 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600548
549
Andrew Lamb9563a152019-12-04 11:42:18 -0700550def _generate_platform_c_files(replication_config, chroot):
551 """Generates platform C files from a platform JSON payload.
552
553 Args:
554 replication_config (replication_config_pb2.ReplicationConfig): A
555 ReplicationConfig that has already been run. If it produced a
556 build_config.json file, that file will be used to generate platform C
557 files. Otherwise, nothing will be generated.
558 chroot (chroot_lib.Chroot): The chroot to use to generate.
559
560 Returns:
561 A list of generated files.
562 """
563 # Generate the platform C files from the build config. Note that it would be
564 # more intuitive to generate the platform C files from the platform config;
565 # however, cros_config_schema does not allow this, because the platform config
566 # payload is not always valid input. For example, if a property is both
567 # 'required' and 'build-only', it will fail schema validation. Thus, use the
568 # build config, and use '-f' to filter.
569 build_config_path = [
570 rule.destination_path
571 for rule in replication_config.file_replication_rules
572 if rule.destination_path.endswith('build_config.json')
573 ]
574
575 if not build_config_path:
576 logging.info(
Alex Kleinad6b48a2020-01-08 16:57:41 -0700577 'No build_config.json found, will not generate platform C files. '
578 'Replication config: %s', replication_config)
Andrew Lamb9563a152019-12-04 11:42:18 -0700579 return []
580
581 if len(build_config_path) > 1:
Alex Kleinad6b48a2020-01-08 16:57:41 -0700582 raise ValueError('Expected at most one build_config.json destination path. '
583 'Replication config: %s' % replication_config)
Andrew Lamb9563a152019-12-04 11:42:18 -0700584
585 build_config_path = build_config_path[0]
586
587 # Paths to the build_config.json and dir to output C files to, in the
588 # chroot.
589 build_config_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
590 build_config_path)
591 generated_output_chroot_dir = os.path.join(constants.CHROOT_SOURCE_ROOT,
592 os.path.dirname(build_config_path))
593
594 command = [
595 'cros_config_schema', '-m', build_config_chroot_path, '-g',
596 generated_output_chroot_dir, '-f', '"TRUE"'
597 ]
598
599 cros_build_lib.run(
600 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
601
602 # A relative (to the source root) path to the generated C files.
603 generated_output_dir = os.path.dirname(build_config_path)
604 generated_files = []
605 expected_c_files = ['config.c', 'ec_config.c', 'ec_config.h']
606 for f in expected_c_files:
607 if os.path.exists(
608 os.path.join(constants.SOURCE_ROOT, generated_output_dir, f)):
609 generated_files.append(os.path.join(generated_output_dir, f))
610
611 if len(expected_c_files) != len(generated_files):
612 raise GeneratedCrosConfigFilesError(expected_c_files, generated_files)
613
614 return generated_files
615
616
Andrew Lambe836f222019-12-09 12:27:38 -0700617def _get_private_overlay_package_root(ref, package):
618 """Returns the absolute path to the root of a given private overlay.
619
620 Args:
621 ref (uprev_lib.GitRef): GitRef for the private overlay.
622 package (str): Path to the package in the overlay.
623 """
624 # There might be a cleaner way to map from package -> path within the source
625 # tree. For now, just use string patterns.
Andrew Lamb4aa09912020-01-08 13:55:56 -0700626 private_overlay_ref_pattern = r'/chromeos\/overlays\/overlay-([\w-]+)-private'
Andrew Lambe836f222019-12-09 12:27:38 -0700627 match = re.match(private_overlay_ref_pattern, ref.path)
628 if not match:
629 raise ValueError('ref.path must match the pattern: %s. Actual ref: %s' %
630 (private_overlay_ref_pattern, ref))
631
632 overlay = match.group(1)
633
634 return os.path.join(constants.SOURCE_ROOT,
635 'src/private-overlays/overlay-%s-private' % overlay,
636 package)
637
638
Andrew Lambea9a8a22019-12-12 14:03:43 -0700639@uprevs_versioned_package('chromeos-base/chromeos-config-bsp')
640def replicate_private_config(_build_targets, refs, chroot):
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700641 """Replicate a private cros_config change to the corresponding public config.
642
Alex Kleinad6b48a2020-01-08 16:57:41 -0700643 See uprev_versioned_package for args
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700644 """
Andrew Lambea9a8a22019-12-12 14:03:43 -0700645 package = 'chromeos-base/chromeos-config-bsp'
646
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700647 if len(refs) != 1:
648 raise ValueError('Expected exactly one ref, actual %s' % refs)
649
650 # Expect a replication_config.jsonpb in the package root.
Andrew Lambe836f222019-12-09 12:27:38 -0700651 package_root = _get_private_overlay_package_root(refs[0], package)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700652 replication_config_path = os.path.join(package_root,
653 'replication_config.jsonpb')
654
655 try:
656 replication_config = json_format.Parse(
657 osutils.ReadFile(replication_config_path),
658 replication_config_pb2.ReplicationConfig())
659 except IOError:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700660 raise ValueError(
661 'Expected ReplicationConfig missing at %s' % replication_config_path)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700662
663 replication_lib.Replicate(replication_config)
664
665 modified_files = [
666 rule.destination_path
667 for rule in replication_config.file_replication_rules
668 ]
669
Andrew Lamb9563a152019-12-04 11:42:18 -0700670 # The generated platform C files are not easily filtered by replication rules,
671 # i.e. JSON / proto filtering can be described by a FieldMask, arbitrary C
672 # files cannot. Therefore, replicate and filter the JSON payloads, and then
673 # generate filtered C files from the JSON payload.
674 modified_files.extend(_generate_platform_c_files(replication_config, chroot))
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700675
676 # Use the private repo's commit hash as the new version.
677 new_private_version = refs[0].revision
678
Andrew Lamb988f4da2019-12-10 10:16:43 -0700679 # modified_files should contain only relative paths at this point, but the
680 # returned UprevVersionedPackageResult must contain only absolute paths.
681 for i, modified_file in enumerate(modified_files):
682 assert not os.path.isabs(modified_file)
683 modified_files[i] = os.path.join(constants.SOURCE_ROOT, modified_file)
684
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700685 return UprevVersionedPackageResult().add_result(new_private_version,
686 modified_files)
687
688
Alex Kleinbbef2b32019-08-27 10:38:50 -0600689def get_best_visible(atom, build_target=None):
690 """Returns the best visible CPV for the given atom.
691
692 Args:
693 atom (str): The atom to look up.
Alex Klein2960c752020-03-09 13:43:38 -0600694 build_target (build_target_lib.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600695 sysroot should be searched, or the SDK if not provided.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700696
697 Returns:
698 portage_util.CPV|None: The best visible package.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600699 """
David Burger1e0fe232019-07-01 14:52:07 -0600700 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600701
702 board = build_target.name if build_target else None
703 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600704
705
Alex Klein149fd3b2019-12-16 16:01:05 -0700706def has_prebuilt(atom, build_target=None, useflags=None):
Alex Kleinda39c6d2019-09-16 14:36:36 -0600707 """Check if a prebuilt exists.
708
709 Args:
710 atom (str): The package whose prebuilt is being queried.
Alex Klein2960c752020-03-09 13:43:38 -0600711 build_target (build_target_lib.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600712 sysroot should be searched, or the SDK if not provided.
Alex Klein149fd3b2019-12-16 16:01:05 -0700713 useflags: Any additional USE flags that should be set. May be a string
714 of properly formatted USE flags, or an iterable of individual flags.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700715
716 Returns:
717 bool: True iff there is an available prebuilt, False otherwise.
Alex Kleinda39c6d2019-09-16 14:36:36 -0600718 """
719 assert atom
720
721 board = build_target.name if build_target else None
Alex Klein149fd3b2019-12-16 16:01:05 -0700722 extra_env = None
723 if useflags:
724 new_flags = useflags
725 if not isinstance(useflags, six.string_types):
726 new_flags = ' '.join(useflags)
727
728 existing = os.environ.get('USE', '')
729 final_flags = '%s %s' % (existing, new_flags)
730 extra_env = {'USE': final_flags.strip()}
731 return portage_util.HasPrebuilt(atom, board=board, extra_env=extra_env)
Alex Klein36b117f2019-09-30 15:13:46 -0600732
733
David Burger0f9dd4e2019-10-08 12:33:42 -0600734def builds(atom, build_target, packages=None):
Alex Klein36b117f2019-09-30 15:13:46 -0600735 """Check if |build_target| builds |atom| (has it in its depgraph)."""
736 cros_build_lib.AssertInsideChroot()
737
Alex Kleind8cd4c62020-09-14 13:37:47 -0600738 pkgs = tuple(packages) if packages else None
LaMont Jones4cbecba2020-05-12 11:54:27 -0600739 # TODO(crbug/1081828): Receive and use sysroot.
740 graph, _sdk_graph = dependency.GetBuildDependency(
Alex Kleind8cd4c62020-09-14 13:37:47 -0600741 build_target.root, build_target.name, pkgs)
Alex Klein36b117f2019-09-30 15:13:46 -0600742 return any(atom in package for package in graph['package_deps'])
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600743
744
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600745def determine_chrome_version(build_target):
Michael Mortensenc2615b72019-10-15 08:12:24 -0600746 """Returns the current Chrome version for the board (or in buildroot).
747
748 Args:
Alex Klein2960c752020-03-09 13:43:38 -0600749 build_target (build_target_lib.BuildTarget): The board build target.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700750
751 Returns:
752 str|None: The chrome version if available.
Michael Mortensenc2615b72019-10-15 08:12:24 -0600753 """
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600754 # TODO(crbug/1019770): Long term we should not need the try/catch here once
755 # the builds function above only returns True for chrome when
756 # determine_chrome_version will succeed.
757 try:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700758 cpv = portage_util.PortageqBestVisible(
759 constants.CHROME_CP, build_target.name, cwd=constants.SOURCE_ROOT)
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600760 except cros_build_lib.RunCommandError as e:
761 # Return None because portage failed when trying to determine the chrome
762 # version.
763 logging.warning('Caught exception in determine_chrome_package: %s', e)
764 return None
Michael Mortensenc2615b72019-10-15 08:12:24 -0600765 # Something like 78.0.3877.4_rc -> 78.0.3877.4
766 return cpv.version_no_rev.partition('_')[0]
767
768
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600769def determine_android_package(board):
770 """Returns the active Android container package in use by the board.
771
772 Args:
773 board: The board name this is specific to.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700774
775 Returns:
776 str|None: The android package string if there is one.
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600777 """
Michael Mortensene0f4b542019-10-24 15:30:23 -0600778 try:
779 packages = portage_util.GetPackageDependencies(board, 'virtual/target-os')
Michael Mortensene0f4b542019-10-24 15:30:23 -0600780 except cros_build_lib.RunCommandError as e:
781 # Return None because a command (likely portage) failed when trying to
782 # determine the package.
783 logging.warning('Caught exception in determine_android_package: %s', e)
784 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600785
Alex Kleinad6b48a2020-01-08 16:57:41 -0700786 # We assume there is only one Android package in the depgraph.
787 for package in packages:
788 if package.startswith('chromeos-base/android-container-') or \
789 package.startswith('chromeos-base/android-vm-'):
790 return package
791 return None
792
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600793
794def determine_android_version(boards=None):
795 """Determine the current Android version in buildroot now and return it.
796
797 This uses the typical portage logic to determine which version of Android
798 is active right now in the buildroot.
799
800 Args:
801 boards: List of boards to check version of.
802
803 Returns:
804 The Android build ID of the container for the boards.
805
806 Raises:
807 NoAndroidVersionError: if no unique Android version can be determined.
808 """
809 if not boards:
810 return None
811 # Verify that all boards have the same version.
812 version = None
813 for board in boards:
814 package = determine_android_package(board)
815 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600816 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600817 cpv = portage_util.SplitCPV(package)
818 if not cpv:
819 raise NoAndroidVersionError(
820 'Android version could not be determined for %s' % board)
821 if not version:
822 version = cpv.version_no_rev
823 elif version != cpv.version_no_rev:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700824 raise NoAndroidVersionError('Different Android versions (%s vs %s) for %s'
825 % (version, cpv.version_no_rev, boards))
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600826 return version
827
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700828
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600829def determine_android_branch(board):
830 """Returns the Android branch in use by the active container ebuild."""
831 try:
832 android_package = determine_android_package(board)
833 except cros_build_lib.RunCommandError:
834 raise NoAndroidBranchError(
835 'Android branch could not be determined for %s' % board)
836 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600837 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600838 ebuild_path = portage_util.FindEbuildForBoardPackage(android_package, board)
839 # We assume all targets pull from the same branch and that we always
Federico 'Morg' Pareschicd9165a2020-05-29 09:45:55 +0900840 # have at least one of the following targets.
Shao-Chuan Lee73bba612020-06-17 11:47:04 +0900841 targets = constants.ANDROID_ALL_BUILD_TARGETS
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600842 ebuild_content = osutils.SourceEnvironment(ebuild_path, targets)
843 for target in targets:
844 if target in ebuild_content:
845 branch = re.search(r'(.*?)-linux-', ebuild_content[target])
846 if branch is not None:
847 return branch.group(1)
848 raise NoAndroidBranchError(
849 'Android branch could not be determined for %s (ebuild empty?)' % board)
850
851
852def determine_android_target(board):
Michael Mortensen14960d02019-10-18 07:53:59 -0600853 """Returns the Android target in use by the active container ebuild."""
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600854 try:
855 android_package = determine_android_package(board)
856 except cros_build_lib.RunCommandError:
857 raise NoAndroidTargetError(
858 'Android Target could not be determined for %s' % board)
859 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600860 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600861 if android_package.startswith('chromeos-base/android-vm-'):
862 return 'bertha'
863 elif android_package.startswith('chromeos-base/android-container-'):
864 return 'cheets'
865
866 raise NoAndroidTargetError(
867 'Android Target cannot be determined for the package: %s' %
868 android_package)
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600869
870
871def determine_platform_version():
872 """Returns the platform version from the source root."""
Michael Mortensen009cb662019-10-21 11:38:43 -0600873 # Platform version is something like '12575.0.0'.
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600874 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
875 return version.VersionString()
Michael Mortensen009cb662019-10-21 11:38:43 -0600876
877
878def determine_milestone_version():
879 """Returns the platform version from the source root."""
880 # Milestone version is something like '79'.
881 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
882 return version.chrome_branch
883
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700884
Michael Mortensen009cb662019-10-21 11:38:43 -0600885def determine_full_version():
886 """Returns the full version from the source root."""
887 # Full version is something like 'R79-12575.0.0'.
888 milestone_version = determine_milestone_version()
889 platform_version = determine_platform_version()
890 full_version = ('R%s-%s' % (milestone_version, platform_version))
891 return full_version
Michael Mortensen71ef5682020-05-07 14:29:24 -0600892
893
Michael Mortensende716a12020-05-15 11:27:00 -0600894def find_fingerprints(build_target):
895 """Returns a list of fingerprints for this build.
896
897 Args:
898 build_target (build_target_lib.BuildTarget): The build target.
899
900 Returns:
901 list[str] - List of fingerprint strings.
902 """
903 cros_build_lib.AssertInsideChroot()
904 fp_file = 'cheets-fingerprint.txt'
905 fp_path = os.path.join(
906 image_lib.GetLatestImageLink(build_target.name),
907 fp_file)
908 if not os.path.isfile(fp_path):
909 logging.info('Fingerprint file not found: %s', fp_path)
Michael Mortensend81d81e2020-06-09 14:20:59 -0600910 return []
Michael Mortensende716a12020-05-15 11:27:00 -0600911 logging.info('Reading fingerprint file: %s', fp_path)
912 fingerprints = osutils.ReadFile(fp_path).splitlines()
913 return fingerprints
914
915
Michael Mortensen59e30872020-05-18 14:12:49 -0600916def get_all_firmware_versions(build_target):
917 """Extract firmware version for all models present.
918
919 Args:
920 build_target (build_target_lib.BuildTarget): The build target.
921
922 Returns:
923 A dict of FirmwareVersions namedtuple instances by model.
924 Each element will be populated based on whether it was present in the
925 command output.
926 """
927 cros_build_lib.AssertInsideChroot()
928 result = {}
929 # Note that example output for _get_firmware_version_cmd_result is available
930 # in the packages_unittest.py for testing get_all_firmware_versions.
931 cmd_result = _get_firmware_version_cmd_result(build_target)
932
933 # There is a blank line between the version info for each model.
934 firmware_version_payloads = cmd_result.split('\n\n')
935 for firmware_version_payload in firmware_version_payloads:
936 if 'BIOS' in firmware_version_payload:
937 firmware_version = _find_firmware_versions(firmware_version_payload)
938 result[firmware_version.model] = firmware_version
939 return result
940
941
Michael Mortensen71ef5682020-05-07 14:29:24 -0600942FirmwareVersions = collections.namedtuple(
943 'FirmwareVersions', ['model', 'main', 'main_rw', 'ec', 'ec_rw'])
944
945
946def get_firmware_versions(build_target):
947 """Extract version information from the firmware updater, if one exists.
948
949 Args:
950 build_target (build_target_lib.BuildTarget): The build target.
951
952 Returns:
953 A FirmwareVersions namedtuple instance.
954 Each element will either be set to the string output by the firmware
955 updater shellball, or None if there is no firmware updater.
956 """
957 cros_build_lib.AssertInsideChroot()
958 cmd_result = _get_firmware_version_cmd_result(build_target)
959 if cmd_result:
960 return _find_firmware_versions(cmd_result)
961 else:
962 return FirmwareVersions(None, None, None, None, None)
963
964
965def _get_firmware_version_cmd_result(build_target):
966 """Gets the raw result output of the firmware updater version command.
967
968 Args:
969 build_target (build_target_lib.BuildTarget): The build target.
970
971 Returns:
972 Command execution result.
973 """
974 updater = os.path.join(build_target.root,
975 'usr/sbin/chromeos-firmwareupdate')
976 logging.info('Calling updater %s', updater)
977 # Call the updater using the chroot-based path.
978 return cros_build_lib.run([updater, '-V'],
979 capture_output=True, log_output=True,
980 encoding='utf-8').stdout
981
982
983def _find_firmware_versions(cmd_output):
984 """Finds firmware version output via regex matches against the cmd_output.
985
986 Args:
987 cmd_output: The raw output to search against.
988
989 Returns:
990 FirmwareVersions namedtuple with results.
991 Each element will either be set to the string output by the firmware
992 updater shellball, or None if there is no match.
993 """
994
995 # Sometimes a firmware bundle includes a special combination of RO+RW
996 # firmware. In this case, the RW firmware version is indicated with a "(RW)
997 # version" field. In other cases, the "(RW) version" field is not present.
998 # Therefore, search for the "(RW)" fields first and if they aren't present,
999 # fallback to the other format. e.g. just "BIOS version:".
1000 # TODO(mmortensen): Use JSON once the firmware updater supports it.
1001 main = None
1002 main_rw = None
1003 ec = None
1004 ec_rw = None
1005 model = None
1006
1007 match = re.search(r'BIOS version:\s*(?P<version>.*)', cmd_output)
1008 if match:
1009 main = match.group('version')
1010
1011 match = re.search(r'BIOS \(RW\) version:\s*(?P<version>.*)', cmd_output)
1012 if match:
1013 main_rw = match.group('version')
1014
1015 match = re.search(r'EC version:\s*(?P<version>.*)', cmd_output)
1016 if match:
1017 ec = match.group('version')
1018
1019 match = re.search(r'EC \(RW\) version:\s*(?P<version>.*)', cmd_output)
1020 if match:
1021 ec_rw = match.group('version')
1022
1023 match = re.search(r'Model:\s*(?P<model>.*)', cmd_output)
1024 if match:
1025 model = match.group('model')
1026
1027 return FirmwareVersions(model, main, main_rw, ec, ec_rw)
Michael Mortensena4af79e2020-05-06 16:18:48 -06001028
1029
1030MainEcFirmwareVersions = collections.namedtuple(
1031 'MainEcFirmwareVersions', ['main_fw_version', 'ec_fw_version'])
1032
1033def determine_firmware_versions(build_target):
1034 """Returns a namedtuple with main and ec firmware versions.
1035
1036 Args:
1037 build_target (build_target_lib.BuildTarget): The build target.
1038
1039 Returns:
1040 MainEcFirmwareVersions namedtuple with results.
1041 """
1042 fw_versions = get_firmware_versions(build_target)
1043 main_fw_version = fw_versions.main_rw or fw_versions.main
1044 ec_fw_version = fw_versions.ec_rw or fw_versions.ec
1045
1046 return MainEcFirmwareVersions(main_fw_version, ec_fw_version)
Michael Mortensenfbf2b2d2020-05-14 16:33:06 -06001047
1048def determine_kernel_version(build_target):
1049 """Returns a string containing the kernel version for this build target.
1050
1051 Args:
1052 build_target (build_target_lib.BuildTarget): The build target.
1053
1054 Returns:
1055 (str) The kernel versions, or None.
1056 """
1057 try:
1058 packages = portage_util.GetPackageDependencies(build_target.name,
1059 'virtual/linux-sources')
1060 except cros_build_lib.RunCommandError as e:
1061 logging.warning('Unable to get package list for metadata: %s', e)
1062 return None
1063 for package in packages:
1064 if package.startswith('sys-kernel/chromeos-kernel-'):
1065 kernel_version = portage_util.SplitCPV(package).version
1066 logging.info('Found active kernel version: %s', kernel_version)
1067 return kernel_version
1068 return None
Michael Mortensen125bb012020-05-21 14:02:10 -06001069
1070
1071def get_models(build_target, log_output=True):
1072 """Obtain a list of models supported by a unified board.
1073
1074 This ignored whitelabel models since GoldenEye has no specific support for
1075 these at present.
1076
1077 Args:
1078 build_target (build_target_lib.BuildTarget): The build target.
1079 log_output: Whether to log the output of the cros_config_host invocation.
1080
1081 Returns:
1082 A list of models supported by this board, if it is a unified build; None,
1083 if it is not a unified build.
1084 """
1085 return _run_cros_config_host(build_target, ['list-models'],
1086 log_output=log_output)
1087
1088
Michael Mortensen359c1f32020-05-28 19:35:42 -06001089def get_key_id(build_target, model):
1090 """Obtain the key_id for a model within the build_target.
1091
1092 Args:
1093 build_target (build_target_lib.BuildTarget): The build target.
1094 model (str): The model name
1095
1096 Returns:
1097 A key_id (str) or None.
1098 """
1099 model_arg = '--model=' + model
1100 key_id_list = _run_cros_config_host(
1101 build_target,
1102 [model_arg, 'get', '/firmware-signing', 'key-id'])
1103 key_id = None
1104 if len(key_id_list) == 1:
1105 key_id = key_id_list[0]
1106 return key_id
1107
1108
Michael Mortensen125bb012020-05-21 14:02:10 -06001109def _run_cros_config_host(build_target, args, log_output=True):
1110 """Run the cros_config_host tool.
1111
1112 Args:
1113 build_target (build_target_lib.BuildTarget): The build target.
1114 args: List of arguments to pass.
1115 log_output: Whether to log the output of the cros_config_host.
1116
1117 Returns:
1118 Output of the tool
1119 """
1120 cros_build_lib.AssertInsideChroot()
1121 tool = '/usr/bin/cros_config_host'
1122 if not os.path.isfile(tool):
1123 return None
1124
1125 config_fname = build_target.full_path(
1126 'usr/share/chromeos-config/yaml/config.yaml')
1127
1128 result = cros_build_lib.run(
1129 [tool, '-c', config_fname] + args,
1130 capture_output=True,
1131 encoding='utf-8',
1132 log_output=log_output,
1133 check=False)
1134 if result.returncode:
1135 # Show the output for debugging purposes.
1136 if 'No such file or directory' not in result.error:
1137 logging.error('cros_config_host failed: %s\n', result.error)
1138 return None
1139 return result.output.strip().splitlines()