blob: d2aac5f6fa49630ebfe5b300f9be0699ba2ab953 [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 Klein18a60af2020-06-11 12:08:47 -060033from chromite.lib.parser import package_info
Alex Kleineb77ffa2019-05-28 14:47:44 -060034
Alex Klein36b117f2019-09-30 15:13:46 -060035if cros_build_lib.IsInsideChroot():
36 from chromite.service import dependency
37
Mike Frysingerbafb3182020-02-21 03:15:43 -050038
Alex Klein87531182019-08-12 15:23:37 -060039# Registered handlers for uprevving versioned packages.
40_UPREV_FUNCS = {}
41
Alex Kleineb77ffa2019-05-28 14:47:44 -060042
43class Error(Exception):
44 """Module's base error class."""
45
46
Alex Klein4de25e82019-08-05 15:58:39 -060047class UnknownPackageError(Error):
48 """Uprev attempted for a package without a registered handler."""
49
50
Alex Kleineb77ffa2019-05-28 14:47:44 -060051class UprevError(Error):
52 """An error occurred while uprevving packages."""
53
54
Michael Mortensenb70e8a82019-10-10 18:43:41 -060055class NoAndroidVersionError(Error):
56 """An error occurred while trying to determine the android version."""
57
58
59class NoAndroidBranchError(Error):
60 """An error occurred while trying to determine the android branch."""
61
62
63class NoAndroidTargetError(Error):
64 """An error occurred while trying to determine the android target."""
65
66
Alex Klein4de25e82019-08-05 15:58:39 -060067class AndroidIsPinnedUprevError(UprevError):
68 """Raised when we try to uprev while Android is pinned."""
69
70 def __init__(self, new_android_atom):
71 """Initialize a AndroidIsPinnedUprevError.
72
73 Args:
74 new_android_atom: The Android atom that we failed to
75 uprev to, due to Android being pinned.
76 """
77 assert new_android_atom
78 msg = ('Failed up uprev to Android version %s as Android was pinned.' %
79 new_android_atom)
80 super(AndroidIsPinnedUprevError, self).__init__(msg)
81 self.new_android_atom = new_android_atom
Alex Klein87531182019-08-12 15:23:37 -060082
83
Yaakov Shaul1eafe832019-09-10 16:50:26 -060084class EbuildManifestError(Error):
85 """Error when running ebuild manifest."""
86
87
Andrew Lamb9563a152019-12-04 11:42:18 -070088class GeneratedCrosConfigFilesError(Error):
89 """Error when cros_config_schema does not produce expected files"""
90
91 def __init__(self, expected_files, found_files):
92 msg = ('Expected to find generated C files: %s. Actually found: %s' %
93 (expected_files, found_files))
94 super(GeneratedCrosConfigFilesError, self).__init__(msg)
95
Alex Klein7a3a7dd2020-01-08 16:44:38 -070096
Yaakov Shaul730814a2019-09-10 13:58:25 -060097UprevVersionedPackageModifications = collections.namedtuple(
98 'UprevVersionedPackageModifications', ('new_version', 'files'))
Alex Klein34afcbc2019-08-22 16:14:31 -060099
Yaakov Shaul730814a2019-09-10 13:58:25 -0600100
101class UprevVersionedPackageResult(object):
102 """Data object for uprev_versioned_package."""
103
104 def __init__(self):
105 self.modified = []
106
107 def add_result(self, new_version, modified_files):
108 """Adds version/ebuilds tuple to result.
109
110 Args:
111 new_version: New version number of package.
112 modified_files: List of files modified for the given version.
113 """
114 result = UprevVersionedPackageModifications(new_version, modified_files)
115 self.modified.append(result)
116 return self
Alex Klein34afcbc2019-08-22 16:14:31 -0600117
118 @property
119 def uprevved(self):
Yaakov Shaul730814a2019-09-10 13:58:25 -0600120 return bool(self.modified)
Alex Klein34afcbc2019-08-22 16:14:31 -0600121
122
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600123def patch_ebuild_vars(ebuild_path, variables):
124 """Updates variables in ebuild.
125
126 Use this function rather than portage_util.EBuild.UpdateEBuild when you
127 want to preserve the variable position and quotes within the ebuild.
128
129 Args:
130 ebuild_path: The path of the ebuild.
131 variables: Dictionary of variables to update in ebuild.
132 """
133 try:
134 for line in fileinput.input(ebuild_path, inplace=1):
135 varname, eq, _ = line.partition('=')
136 if eq == '=' and varname.strip() in variables:
137 value = variables[varname]
138 sys.stdout.write('%s="%s"\n' % (varname, value))
139 else:
140 sys.stdout.write(line)
141 finally:
142 fileinput.close()
143
144
Alex Klein87531182019-08-12 15:23:37 -0600145def uprevs_versioned_package(package):
146 """Decorator to register package uprev handlers."""
147 assert package
148
149 def register(func):
150 """Registers |func| as a handler for |package|."""
151 _UPREV_FUNCS[package] = func
152
153 @functools.wraps(func)
154 def pass_through(*args, **kwargs):
155 return func(*args, **kwargs)
156
157 return pass_through
158
159 return register
160
161
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700162def uprev_android(tracking_branch,
163 android_package,
164 android_build_branch,
165 chroot,
166 build_targets=None,
Shao-Chuan Lee9c39e0c2020-04-24 11:40:34 +0900167 android_version=None):
Alex Klein4de25e82019-08-05 15:58:39 -0600168 """Returns the portage atom for the revved Android ebuild - see man emerge."""
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700169 command = [
170 'cros_mark_android_as_stable',
171 '--tracking_branch=%s' % tracking_branch,
172 '--android_package=%s' % android_package,
173 '--android_build_branch=%s' % android_build_branch,
174 ]
Alex Klein4de25e82019-08-05 15:58:39 -0600175 if build_targets:
176 command.append('--boards=%s' % ':'.join(bt.name for bt in build_targets))
177 if android_version:
178 command.append('--force_version=%s' % android_version)
Alex Klein4de25e82019-08-05 15:58:39 -0600179
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700180 result = cros_build_lib.run(
181 command,
182 stdout=True,
183 enter_chroot=True,
Mike Frysinger88d96362020-02-14 19:05:45 -0500184 encoding='utf-8',
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700185 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600186
Mike Frysinger88d96362020-02-14 19:05:45 -0500187 portage_atom_string = result.stdout.strip()
188 android_atom = None
189 if portage_atom_string:
190 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
Alex Klein4de25e82019-08-05 15:58:39 -0600191 if not android_atom:
192 logging.info('Found nothing to rev.')
193 return None
194
195 for target in build_targets or []:
196 # Sanity check: We should always be able to merge the version of
197 # Android we just unmasked.
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700198 command = ['emerge-%s' % target.name, '-p', '--quiet', '=%s' % android_atom]
Alex Klein4de25e82019-08-05 15:58:39 -0600199 try:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700200 cros_build_lib.run(
201 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600202 except cros_build_lib.RunCommandError:
203 logging.error(
204 'Cannot emerge-%s =%s\nIs Android pinned to an older '
205 'version?', target, android_atom)
206 raise AndroidIsPinnedUprevError(android_atom)
207
208 return android_atom
209
210
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700211def uprev_build_targets(build_targets,
212 overlay_type,
213 chroot=None,
Alex Kleineb77ffa2019-05-28 14:47:44 -0600214 output_dir=None):
215 """Uprev the set provided build targets, or all if not specified.
216
217 Args:
Alex Klein2960c752020-03-09 13:43:38 -0600218 build_targets (list[build_target_lib.BuildTarget]|None): The build targets
Alex Kleineb77ffa2019-05-28 14:47:44 -0600219 whose overlays should be uprevved, empty or None for all.
220 overlay_type (str): One of the valid overlay types except None (see
221 constants.VALID_OVERLAYS).
222 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
223 output_dir (str|None): The path to optionally dump result files.
224 """
225 # Need a valid overlay, but exclude None.
226 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
227
228 if build_targets:
229 overlays = portage_util.FindOverlaysForBoards(
230 overlay_type, boards=[t.name for t in build_targets])
231 else:
232 overlays = portage_util.FindOverlays(overlay_type)
233
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700234 return uprev_overlays(
235 overlays,
236 build_targets=build_targets,
237 chroot=chroot,
238 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600239
240
241def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
242 """Uprev the given overlays.
243
244 Args:
245 overlays (list[str]): The list of overlay paths.
Alex Klein2960c752020-03-09 13:43:38 -0600246 build_targets (list[build_target_lib.BuildTarget]|None): The build targets
Alex Kleineb77ffa2019-05-28 14:47:44 -0600247 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
248 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
249 output_dir (str|None): The path to optionally dump result files.
250
251 Returns:
252 list[str] - The paths to all of the modified ebuild files. This includes the
253 new files that were added (i.e. the new versions) and all of the removed
254 files (i.e. the old versions).
255 """
256 assert overlays
257
258 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
259
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700260 uprev_manager = uprev_lib.UprevOverlayManager(
261 overlays,
262 manifest,
263 build_targets=build_targets,
264 chroot=chroot,
265 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600266 uprev_manager.uprev()
267
268 return uprev_manager.modified_ebuilds
269
270
Alex Klein87531182019-08-12 15:23:37 -0600271def uprev_versioned_package(package, build_targets, refs, chroot):
272 """Call registered uprev handler function for the package.
273
274 Args:
Alex Klein75df1792020-06-11 14:42:49 -0600275 package (package_info.CPV): The package being uprevved.
Alex Klein2960c752020-03-09 13:43:38 -0600276 build_targets (list[build_target_lib.BuildTarget]): The build targets to
Alex Klein87531182019-08-12 15:23:37 -0600277 clean on a successful uprev.
278 refs (list[uprev_lib.GitRef]):
279 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
280
281 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600282 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600283 """
284 assert package
285
286 if package.cp not in _UPREV_FUNCS:
287 raise UnknownPackageError(
288 'Package "%s" does not have a registered handler.' % package.cp)
289
Andrew Lambea9a8a22019-12-12 14:03:43 -0700290 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
Alex Klein87531182019-08-12 15:23:37 -0600291
292
Navil Perezf57ba872020-06-04 22:38:37 +0000293@uprevs_versioned_package('media-libs/virglrenderer')
294def uprev_virglrenderer(_build_targets, refs, _chroot):
295 """Updates virglrenderer ebuilds.
296
297 See: uprev_versioned_package.
298
299 Returns:
300 UprevVersionedPackageResult: The result of updating virglrenderer ebuilds.
301 """
Navil Perezf57ba872020-06-04 22:38:37 +0000302 overlay = os.path.join(constants.SOURCE_ROOT,
303 constants.CHROMIUMOS_OVERLAY_DIR)
George Engelbrechte73f2782020-06-10 14:10:46 -0600304 repo_path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
305 'virglrenderer')
306 manifest = git.ManifestCheckout.Cached(repo_path)
Navil Perezf57ba872020-06-04 22:38:37 +0000307
308 uprev_manager = uprev_lib.UprevOverlayManager([overlay], manifest)
309 # TODO(crbug.com/1066242): Ebuilds for virglrenderer are currently
310 # blacklisted. Do not force uprevs after builder is stable and ebuilds are no
311 # longer blacklisted.
312 uprev_manager.uprev(package_list=['media-libs/virglrenderer'], force=True)
313
George Engelbrechte73f2782020-06-10 14:10:46 -0600314 updated_files = uprev_manager.modified_ebuilds
Navil Perezf57ba872020-06-04 22:38:37 +0000315 result = UprevVersionedPackageResult()
316 result.add_result(refs[0].revision, updated_files)
317 return result
318
319
Yaakov Shaul395ae832019-09-09 14:45:32 -0600320@uprevs_versioned_package('afdo/kernel-profiles')
321def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600322 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600323
324 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600325
326 Raises:
327 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600328 """
329 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
330 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
331
David Burger92485342019-09-10 17:52:45 -0600332 with open(path, 'r') as f:
333 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600334
Yaakov Shaul730814a2019-09-10 13:58:25 -0600335 result = UprevVersionedPackageResult()
Yaakov Shaul395ae832019-09-09 14:45:32 -0600336 for version, version_info in versions.items():
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600337 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
338 'sys-kernel', version)
339 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
340 '%s-9999.ebuild' % version)
Yaakov Shaula187b152019-09-11 12:41:32 -0600341 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
342 '%s-9999.ebuild' % version)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600343 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600344 patch_ebuild_vars(ebuild_path,
345 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600346
347 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600348 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400349 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600350 except cros_build_lib.RunCommandError as e:
351 raise EbuildManifestError(
352 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600353 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600354
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600355 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600356
Yaakov Shaul730814a2019-09-10 13:58:25 -0600357 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
358
359 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600360
361
Trent Begineb624182020-07-14 10:09:45 -0600362@uprevs_versioned_package('chromeos-base/termina-dlc')
363def uprev_termina_dlc(_build_targets, _refs, chroot):
364 """Updates shared termina-dlc ebuild - chromeos-base/termina-dlc.
Trent Beginaf51f1b2020-03-09 17:35:31 -0600365
366 See: uprev_versioned_package.
367 """
Trent Begineb624182020-07-14 10:09:45 -0600368 package = 'termina-dlc'
Trent Beginaf51f1b2020-03-09 17:35:31 -0600369 package_path = os.path.join(constants.CHROMIUMOS_OVERLAY_DIR, 'chromeos-base',
370 package)
Patrick Meiring5897add2020-09-16 16:30:17 +1000371
372 version_pin_src_path = _get_version_pin_src_path(package_path)
373 version_no_rev = osutils.ReadFile(version_pin_src_path).strip()
374
375 return uprev_ebuild_from_pin(package_path, version_no_rev, chroot)
376
377
378@uprevs_versioned_package('app-emulation/parallels-desktop')
379def uprev_parallels_desktop(_build_targets, _refs, chroot):
380 """Updates Parallels Desktop ebuild - app-emulation/parallels-desktop.
381
382 See: uprev_versioned_package
383
384 Returns:
385 UprevVersionedPackageResult: The result.
386 """
387 package = 'parallels-desktop'
388 package_path = os.path.join(constants.CHROMEOS_PARTNER_OVERLAY_DIR,
389 'app-emulation', package)
390 version_pin_src_path = _get_version_pin_src_path(package_path)
391
392 # Expect a JSON blob like the following:
393 # {
394 # "version": "1.2.3",
395 # "test_image": { "url": "...", "size": 12345678,
396 # "sha256sum": "<32 bytes of hexadecimal>" }
397 # }
398 with open(version_pin_src_path, 'r') as f:
399 pinned = json.load(f)
400
401 if 'version' not in pinned or 'test_image' not in pinned:
402 raise UprevError('VERSION-PIN for %s missing version and/or '
403 'test_image field' % package)
404
405 version = pinned['version']
406 if not isinstance(version, str):
407 raise UprevError('version in VERSION-PIN for %s not a string' % package)
408
409 # Update the ebuild.
410 result = uprev_ebuild_from_pin(package_path, version, chroot)
411
412 # Update the VM image used for testing.
413 test_image_path = ('src/platform/tast-tests-pita/src/chromiumos/tast/local/'
414 'bundles/pita/pita/data/pluginvm_image.zip.external')
415 test_image_src_path = os.path.join(constants.SOURCE_ROOT, test_image_path)
416 with open(test_image_src_path, 'w') as f:
417 json.dump(pinned['test_image'], f, indent=2)
418 result.add_result(version, [test_image_src_path])
419
420 return result
Trent Beginaf51f1b2020-03-09 17:35:31 -0600421
422
Trent Begin315d9d92019-12-03 21:55:53 -0700423@uprevs_versioned_package('chromeos-base/chromeos-dtc-vm')
Trent Beginaf51f1b2020-03-09 17:35:31 -0600424def uprev_sludge(_build_targets, _refs, chroot):
Trent Begin315d9d92019-12-03 21:55:53 -0700425 """Updates sludge VM - chromeos-base/chromeos-dtc-vm.
426
427 See: uprev_versioned_package.
428 """
429 package = 'chromeos-dtc-vm'
Trent Begind943df92020-02-25 10:30:10 -0700430 package_path = os.path.join('src', 'private-overlays',
431 'project-wilco-private', 'chromeos-base', package)
Patrick Meiring5897add2020-09-16 16:30:17 +1000432 version_pin_src_path = _get_version_pin_src_path(package_path)
433 version_no_rev = osutils.ReadFile(version_pin_src_path).strip()
Trent Begin315d9d92019-12-03 21:55:53 -0700434
Patrick Meiring5897add2020-09-16 16:30:17 +1000435 return uprev_ebuild_from_pin(package_path, version_no_rev, chroot)
Trent Begin315d9d92019-12-03 21:55:53 -0700436
437
Patrick Meiring5897add2020-09-16 16:30:17 +1000438def _get_version_pin_src_path(package_path):
439 """Returns the path to the VERSION-PIN file for the given package."""
440 return os.path.join(constants.SOURCE_ROOT, package_path, 'VERSION-PIN')
441
442
443def uprev_ebuild_from_pin(package_path, version_no_rev, chroot):
Trent Begin315d9d92019-12-03 21:55:53 -0700444 """Changes the package ebuild's version to match the version pin file.
445
446 Args:
447 package_path: The path of the package relative to the src root. This path
Patrick Meiring5897add2020-09-16 16:30:17 +1000448 should contain a stable and an unstable ebuild with the same name
449 as the package.
450 version_no_rev: The version string to uprev to (excluding revision). The
451 ebuild's version will be directly set to this number.
Trent Begin6daa8702020-01-29 14:58:12 -0700452 chroot (chroot_lib.Chroot): specify a chroot to enter.
Trent Begin315d9d92019-12-03 21:55:53 -0700453
454 Returns:
455 UprevVersionedPackageResult: The result.
456 """
457 package = os.path.basename(package_path)
Trent Begind943df92020-02-25 10:30:10 -0700458
459 package_src_path = os.path.join(constants.SOURCE_ROOT, package_path)
460 ebuild_paths = list(portage_util.EBuild.List(package_src_path))
Fergus Dall2209d0b2020-08-06 11:51:43 +1000461 stable_ebuild = None
462 unstable_ebuild = None
463 for path in ebuild_paths:
464 ebuild = portage_util.EBuild(path)
465 if ebuild.is_stable:
466 stable_ebuild = ebuild
467 else:
468 unstable_ebuild = ebuild
469
470 if stable_ebuild is None:
471 raise UprevError('No stable ebuild found for %s' % package)
472 if unstable_ebuild is None:
473 raise UprevError('No unstable ebuild found for %s' % package)
474 if len(ebuild_paths) > 2:
475 raise UprevError('Found too many ebuilds for %s: '
476 'expected one stable and one unstable' % package)
Trent Begin315d9d92019-12-03 21:55:53 -0700477
Fergus Dall2209d0b2020-08-06 11:51:43 +1000478 # If the new version is the same as the old version, bump the revision number,
479 # otherwise reset it to 1
Patrick Meiring5897add2020-09-16 16:30:17 +1000480 if version_no_rev == stable_ebuild.version_no_rev:
481 version = '%s-r%d' % (version_no_rev, stable_ebuild.current_revision + 1)
Fergus Dall2209d0b2020-08-06 11:51:43 +1000482 else:
Patrick Meiring5897add2020-09-16 16:30:17 +1000483 version = version_no_rev + '-r1'
Fergus Dall2209d0b2020-08-06 11:51:43 +1000484
Fergus Dall6d5b7472020-08-13 21:24:28 +1000485 new_ebuild_path = os.path.join(package_path,
486 '%s-%s.ebuild' % (package, version))
487 new_ebuild_src_path = os.path.join(constants.SOURCE_ROOT,
488 new_ebuild_path)
Trent Begin4a11a632020-02-28 12:59:58 -0700489 manifest_src_path = os.path.join(package_src_path, 'Manifest')
Fergus Dall2209d0b2020-08-06 11:51:43 +1000490
491 portage_util.EBuild.MarkAsStable(unstable_ebuild.ebuild_path,
492 new_ebuild_src_path, {})
493 osutils.SafeUnlink(stable_ebuild.ebuild_path)
Trent Begin315d9d92019-12-03 21:55:53 -0700494
Trent Begin6daa8702020-01-29 14:58:12 -0700495 try:
Fergus Dall6d5b7472020-08-13 21:24:28 +1000496 # UpdateEbuildManifest runs inside the chroot and therefore needs a
497 # chroot-relative path.
498 new_ebuild_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
499 new_ebuild_path)
500 portage_util.UpdateEbuildManifest(new_ebuild_chroot_path, chroot=chroot)
Trent Begin6daa8702020-01-29 14:58:12 -0700501 except cros_build_lib.RunCommandError as e:
Trent Begind943df92020-02-25 10:30:10 -0700502 raise EbuildManifestError(
Trent Begin6daa8702020-01-29 14:58:12 -0700503 'Unable to update manifest for %s: %s' % (package, e.stderr))
504
Trent Begin315d9d92019-12-03 21:55:53 -0700505 result = UprevVersionedPackageResult()
Trent Begin4a11a632020-02-28 12:59:58 -0700506 result.add_result(version,
Fergus Dall2209d0b2020-08-06 11:51:43 +1000507 [new_ebuild_src_path,
508 stable_ebuild.ebuild_path,
509 manifest_src_path])
Trent Begin315d9d92019-12-03 21:55:53 -0700510 return result
511
512
Alex Klein87531182019-08-12 15:23:37 -0600513@uprevs_versioned_package(constants.CHROME_CP)
Andrew Lambea9a8a22019-12-12 14:03:43 -0700514def uprev_chrome(build_targets, refs, chroot):
Alex Klein87531182019-08-12 15:23:37 -0600515 """Uprev chrome and its related packages.
516
517 See: uprev_versioned_package.
518 """
519 # Determine the version from the refs (tags), i.e. the chrome versions are the
520 # tag names.
521 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
Chris McDonald25881af2020-05-12 03:17:53 -0600522 logging.debug('Chrome version determined from refs: %s', chrome_version)
Alex Klein87531182019-08-12 15:23:37 -0600523
524 uprev_manager = uprev_lib.UprevChromeManager(
525 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600526 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600527 # Start with chrome itself, as we can't do anything else unless chrome
528 # uprevs successfully.
Chris McDonald25881af2020-05-12 03:17:53 -0600529 # TODO(crbug.com/1080429): Handle all possible outcomes of a Chrome uprev
530 # attempt. The expected behavior is documented in the following table:
531 #
532 # Outcome of Chrome uprev attempt:
533 # NEWER_VERSION_EXISTS:
534 # Do nothing.
535 # SAME_VERSION_EXISTS or REVISION_BUMP:
536 # Uprev followers
537 # Assert not VERSION_BUMP (any other outcome is fine)
538 # VERSION_BUMP or NEW_EBUILD_CREATED:
539 # Uprev followers
540 # Assert that Chrome & followers are at same package version
Alex Klein87531182019-08-12 15:23:37 -0600541 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600542 return result
Alex Klein87531182019-08-12 15:23:37 -0600543
544 # With a successful chrome rev, also uprev related packages.
545 for package in constants.OTHER_CHROME_PACKAGES:
546 uprev_manager.uprev(package)
547
David Burger37f48672019-09-18 17:07:56 -0600548 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600549
550
Andrew Lamb9563a152019-12-04 11:42:18 -0700551def _generate_platform_c_files(replication_config, chroot):
552 """Generates platform C files from a platform JSON payload.
553
554 Args:
555 replication_config (replication_config_pb2.ReplicationConfig): A
556 ReplicationConfig that has already been run. If it produced a
557 build_config.json file, that file will be used to generate platform C
558 files. Otherwise, nothing will be generated.
559 chroot (chroot_lib.Chroot): The chroot to use to generate.
560
561 Returns:
562 A list of generated files.
563 """
564 # Generate the platform C files from the build config. Note that it would be
565 # more intuitive to generate the platform C files from the platform config;
566 # however, cros_config_schema does not allow this, because the platform config
567 # payload is not always valid input. For example, if a property is both
568 # 'required' and 'build-only', it will fail schema validation. Thus, use the
569 # build config, and use '-f' to filter.
570 build_config_path = [
571 rule.destination_path
572 for rule in replication_config.file_replication_rules
573 if rule.destination_path.endswith('build_config.json')
574 ]
575
576 if not build_config_path:
577 logging.info(
Alex Kleinad6b48a2020-01-08 16:57:41 -0700578 'No build_config.json found, will not generate platform C files. '
579 'Replication config: %s', replication_config)
Andrew Lamb9563a152019-12-04 11:42:18 -0700580 return []
581
582 if len(build_config_path) > 1:
Alex Kleinad6b48a2020-01-08 16:57:41 -0700583 raise ValueError('Expected at most one build_config.json destination path. '
584 'Replication config: %s' % replication_config)
Andrew Lamb9563a152019-12-04 11:42:18 -0700585
586 build_config_path = build_config_path[0]
587
588 # Paths to the build_config.json and dir to output C files to, in the
589 # chroot.
590 build_config_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
591 build_config_path)
592 generated_output_chroot_dir = os.path.join(constants.CHROOT_SOURCE_ROOT,
593 os.path.dirname(build_config_path))
594
595 command = [
596 'cros_config_schema', '-m', build_config_chroot_path, '-g',
597 generated_output_chroot_dir, '-f', '"TRUE"'
598 ]
599
600 cros_build_lib.run(
601 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
602
603 # A relative (to the source root) path to the generated C files.
604 generated_output_dir = os.path.dirname(build_config_path)
605 generated_files = []
606 expected_c_files = ['config.c', 'ec_config.c', 'ec_config.h']
607 for f in expected_c_files:
608 if os.path.exists(
609 os.path.join(constants.SOURCE_ROOT, generated_output_dir, f)):
610 generated_files.append(os.path.join(generated_output_dir, f))
611
612 if len(expected_c_files) != len(generated_files):
613 raise GeneratedCrosConfigFilesError(expected_c_files, generated_files)
614
615 return generated_files
616
617
Andrew Lambe836f222019-12-09 12:27:38 -0700618def _get_private_overlay_package_root(ref, package):
619 """Returns the absolute path to the root of a given private overlay.
620
621 Args:
622 ref (uprev_lib.GitRef): GitRef for the private overlay.
623 package (str): Path to the package in the overlay.
624 """
625 # There might be a cleaner way to map from package -> path within the source
626 # tree. For now, just use string patterns.
Andrew Lamb4aa09912020-01-08 13:55:56 -0700627 private_overlay_ref_pattern = r'/chromeos\/overlays\/overlay-([\w-]+)-private'
Andrew Lambe836f222019-12-09 12:27:38 -0700628 match = re.match(private_overlay_ref_pattern, ref.path)
629 if not match:
630 raise ValueError('ref.path must match the pattern: %s. Actual ref: %s' %
631 (private_overlay_ref_pattern, ref))
632
633 overlay = match.group(1)
634
635 return os.path.join(constants.SOURCE_ROOT,
636 'src/private-overlays/overlay-%s-private' % overlay,
637 package)
638
639
Andrew Lambea9a8a22019-12-12 14:03:43 -0700640@uprevs_versioned_package('chromeos-base/chromeos-config-bsp')
641def replicate_private_config(_build_targets, refs, chroot):
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700642 """Replicate a private cros_config change to the corresponding public config.
643
Alex Kleinad6b48a2020-01-08 16:57:41 -0700644 See uprev_versioned_package for args
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700645 """
Andrew Lambea9a8a22019-12-12 14:03:43 -0700646 package = 'chromeos-base/chromeos-config-bsp'
647
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700648 if len(refs) != 1:
649 raise ValueError('Expected exactly one ref, actual %s' % refs)
650
651 # Expect a replication_config.jsonpb in the package root.
Andrew Lambe836f222019-12-09 12:27:38 -0700652 package_root = _get_private_overlay_package_root(refs[0], package)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700653 replication_config_path = os.path.join(package_root,
654 'replication_config.jsonpb')
655
656 try:
657 replication_config = json_format.Parse(
658 osutils.ReadFile(replication_config_path),
659 replication_config_pb2.ReplicationConfig())
660 except IOError:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700661 raise ValueError(
662 'Expected ReplicationConfig missing at %s' % replication_config_path)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700663
664 replication_lib.Replicate(replication_config)
665
666 modified_files = [
667 rule.destination_path
668 for rule in replication_config.file_replication_rules
669 ]
670
Andrew Lamb9563a152019-12-04 11:42:18 -0700671 # The generated platform C files are not easily filtered by replication rules,
672 # i.e. JSON / proto filtering can be described by a FieldMask, arbitrary C
673 # files cannot. Therefore, replicate and filter the JSON payloads, and then
674 # generate filtered C files from the JSON payload.
675 modified_files.extend(_generate_platform_c_files(replication_config, chroot))
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700676
677 # Use the private repo's commit hash as the new version.
678 new_private_version = refs[0].revision
679
Andrew Lamb988f4da2019-12-10 10:16:43 -0700680 # modified_files should contain only relative paths at this point, but the
681 # returned UprevVersionedPackageResult must contain only absolute paths.
682 for i, modified_file in enumerate(modified_files):
683 assert not os.path.isabs(modified_file)
684 modified_files[i] = os.path.join(constants.SOURCE_ROOT, modified_file)
685
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700686 return UprevVersionedPackageResult().add_result(new_private_version,
687 modified_files)
688
689
Alex Kleinbbef2b32019-08-27 10:38:50 -0600690def get_best_visible(atom, build_target=None):
691 """Returns the best visible CPV for the given atom.
692
693 Args:
694 atom (str): The atom to look up.
Alex Klein2960c752020-03-09 13:43:38 -0600695 build_target (build_target_lib.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600696 sysroot should be searched, or the SDK if not provided.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700697
698 Returns:
Alex Klein75df1792020-06-11 14:42:49 -0600699 package_info.CPV|None: The best visible package.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600700 """
David Burger1e0fe232019-07-01 14:52:07 -0600701 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600702
703 board = build_target.name if build_target else None
704 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600705
706
Alex Klein149fd3b2019-12-16 16:01:05 -0700707def has_prebuilt(atom, build_target=None, useflags=None):
Alex Kleinda39c6d2019-09-16 14:36:36 -0600708 """Check if a prebuilt exists.
709
710 Args:
711 atom (str): The package whose prebuilt is being queried.
Alex Klein2960c752020-03-09 13:43:38 -0600712 build_target (build_target_lib.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600713 sysroot should be searched, or the SDK if not provided.
Alex Klein149fd3b2019-12-16 16:01:05 -0700714 useflags: Any additional USE flags that should be set. May be a string
715 of properly formatted USE flags, or an iterable of individual flags.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700716
717 Returns:
718 bool: True iff there is an available prebuilt, False otherwise.
Alex Kleinda39c6d2019-09-16 14:36:36 -0600719 """
720 assert atom
721
722 board = build_target.name if build_target else None
Alex Klein149fd3b2019-12-16 16:01:05 -0700723 extra_env = None
724 if useflags:
725 new_flags = useflags
726 if not isinstance(useflags, six.string_types):
727 new_flags = ' '.join(useflags)
728
729 existing = os.environ.get('USE', '')
730 final_flags = '%s %s' % (existing, new_flags)
731 extra_env = {'USE': final_flags.strip()}
732 return portage_util.HasPrebuilt(atom, board=board, extra_env=extra_env)
Alex Klein36b117f2019-09-30 15:13:46 -0600733
734
David Burger0f9dd4e2019-10-08 12:33:42 -0600735def builds(atom, build_target, packages=None):
Alex Klein36b117f2019-09-30 15:13:46 -0600736 """Check if |build_target| builds |atom| (has it in its depgraph)."""
737 cros_build_lib.AssertInsideChroot()
738
Alex Kleind8cd4c62020-09-14 13:37:47 -0600739 pkgs = tuple(packages) if packages else None
LaMont Jones4cbecba2020-05-12 11:54:27 -0600740 # TODO(crbug/1081828): Receive and use sysroot.
741 graph, _sdk_graph = dependency.GetBuildDependency(
Alex Kleind8cd4c62020-09-14 13:37:47 -0600742 build_target.root, build_target.name, pkgs)
Alex Klein36b117f2019-09-30 15:13:46 -0600743 return any(atom in package for package in graph['package_deps'])
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600744
745
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600746def determine_chrome_version(build_target):
Michael Mortensenc2615b72019-10-15 08:12:24 -0600747 """Returns the current Chrome version for the board (or in buildroot).
748
749 Args:
Alex Klein2960c752020-03-09 13:43:38 -0600750 build_target (build_target_lib.BuildTarget): The board build target.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700751
752 Returns:
753 str|None: The chrome version if available.
Michael Mortensenc2615b72019-10-15 08:12:24 -0600754 """
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600755 # TODO(crbug/1019770): Long term we should not need the try/catch here once
756 # the builds function above only returns True for chrome when
757 # determine_chrome_version will succeed.
758 try:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700759 cpv = portage_util.PortageqBestVisible(
760 constants.CHROME_CP, build_target.name, cwd=constants.SOURCE_ROOT)
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600761 except cros_build_lib.RunCommandError as e:
762 # Return None because portage failed when trying to determine the chrome
763 # version.
764 logging.warning('Caught exception in determine_chrome_package: %s', e)
765 return None
Michael Mortensenc2615b72019-10-15 08:12:24 -0600766 # Something like 78.0.3877.4_rc -> 78.0.3877.4
767 return cpv.version_no_rev.partition('_')[0]
768
769
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600770def determine_android_package(board):
771 """Returns the active Android container package in use by the board.
772
773 Args:
774 board: The board name this is specific to.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700775
776 Returns:
777 str|None: The android package string if there is one.
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600778 """
Michael Mortensene0f4b542019-10-24 15:30:23 -0600779 try:
780 packages = portage_util.GetPackageDependencies(board, 'virtual/target-os')
Michael Mortensene0f4b542019-10-24 15:30:23 -0600781 except cros_build_lib.RunCommandError as e:
782 # Return None because a command (likely portage) failed when trying to
783 # determine the package.
784 logging.warning('Caught exception in determine_android_package: %s', e)
785 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600786
Alex Kleinad6b48a2020-01-08 16:57:41 -0700787 # We assume there is only one Android package in the depgraph.
788 for package in packages:
789 if package.startswith('chromeos-base/android-container-') or \
790 package.startswith('chromeos-base/android-vm-'):
791 return package
792 return None
793
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600794
795def determine_android_version(boards=None):
796 """Determine the current Android version in buildroot now and return it.
797
798 This uses the typical portage logic to determine which version of Android
799 is active right now in the buildroot.
800
801 Args:
802 boards: List of boards to check version of.
803
804 Returns:
805 The Android build ID of the container for the boards.
806
807 Raises:
808 NoAndroidVersionError: if no unique Android version can be determined.
809 """
810 if not boards:
811 return None
812 # Verify that all boards have the same version.
813 version = None
814 for board in boards:
815 package = determine_android_package(board)
816 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600817 return None
Alex Klein18a60af2020-06-11 12:08:47 -0600818 cpv = package_info.SplitCPV(package)
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600819 if not cpv:
820 raise NoAndroidVersionError(
821 'Android version could not be determined for %s' % board)
822 if not version:
823 version = cpv.version_no_rev
824 elif version != cpv.version_no_rev:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700825 raise NoAndroidVersionError('Different Android versions (%s vs %s) for %s'
826 % (version, cpv.version_no_rev, boards))
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600827 return version
828
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700829
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600830def determine_android_branch(board):
831 """Returns the Android branch in use by the active container ebuild."""
832 try:
833 android_package = determine_android_package(board)
834 except cros_build_lib.RunCommandError:
835 raise NoAndroidBranchError(
836 'Android branch could not be determined for %s' % board)
837 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600838 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600839 ebuild_path = portage_util.FindEbuildForBoardPackage(android_package, board)
840 # We assume all targets pull from the same branch and that we always
Federico 'Morg' Pareschicd9165a2020-05-29 09:45:55 +0900841 # have at least one of the following targets.
Shao-Chuan Lee73bba612020-06-17 11:47:04 +0900842 targets = constants.ANDROID_ALL_BUILD_TARGETS
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600843 ebuild_content = osutils.SourceEnvironment(ebuild_path, targets)
844 for target in targets:
845 if target in ebuild_content:
846 branch = re.search(r'(.*?)-linux-', ebuild_content[target])
847 if branch is not None:
848 return branch.group(1)
849 raise NoAndroidBranchError(
850 'Android branch could not be determined for %s (ebuild empty?)' % board)
851
852
853def determine_android_target(board):
Michael Mortensen14960d02019-10-18 07:53:59 -0600854 """Returns the Android target in use by the active container ebuild."""
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600855 try:
856 android_package = determine_android_package(board)
857 except cros_build_lib.RunCommandError:
858 raise NoAndroidTargetError(
859 'Android Target could not be determined for %s' % board)
860 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600861 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600862 if android_package.startswith('chromeos-base/android-vm-'):
863 return 'bertha'
864 elif android_package.startswith('chromeos-base/android-container-'):
865 return 'cheets'
866
867 raise NoAndroidTargetError(
868 'Android Target cannot be determined for the package: %s' %
869 android_package)
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600870
871
872def determine_platform_version():
873 """Returns the platform version from the source root."""
Michael Mortensen009cb662019-10-21 11:38:43 -0600874 # Platform version is something like '12575.0.0'.
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600875 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
876 return version.VersionString()
Michael Mortensen009cb662019-10-21 11:38:43 -0600877
878
879def determine_milestone_version():
880 """Returns the platform version from the source root."""
881 # Milestone version is something like '79'.
882 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
883 return version.chrome_branch
884
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700885
Michael Mortensen009cb662019-10-21 11:38:43 -0600886def determine_full_version():
887 """Returns the full version from the source root."""
888 # Full version is something like 'R79-12575.0.0'.
889 milestone_version = determine_milestone_version()
890 platform_version = determine_platform_version()
891 full_version = ('R%s-%s' % (milestone_version, platform_version))
892 return full_version
Michael Mortensen71ef5682020-05-07 14:29:24 -0600893
894
Michael Mortensende716a12020-05-15 11:27:00 -0600895def find_fingerprints(build_target):
896 """Returns a list of fingerprints for this build.
897
898 Args:
899 build_target (build_target_lib.BuildTarget): The build target.
900
901 Returns:
902 list[str] - List of fingerprint strings.
903 """
904 cros_build_lib.AssertInsideChroot()
905 fp_file = 'cheets-fingerprint.txt'
906 fp_path = os.path.join(
907 image_lib.GetLatestImageLink(build_target.name),
908 fp_file)
909 if not os.path.isfile(fp_path):
910 logging.info('Fingerprint file not found: %s', fp_path)
Michael Mortensend81d81e2020-06-09 14:20:59 -0600911 return []
Michael Mortensende716a12020-05-15 11:27:00 -0600912 logging.info('Reading fingerprint file: %s', fp_path)
913 fingerprints = osutils.ReadFile(fp_path).splitlines()
914 return fingerprints
915
916
Michael Mortensen59e30872020-05-18 14:12:49 -0600917def get_all_firmware_versions(build_target):
918 """Extract firmware version for all models present.
919
920 Args:
921 build_target (build_target_lib.BuildTarget): The build target.
922
923 Returns:
924 A dict of FirmwareVersions namedtuple instances by model.
925 Each element will be populated based on whether it was present in the
926 command output.
927 """
928 cros_build_lib.AssertInsideChroot()
929 result = {}
930 # Note that example output for _get_firmware_version_cmd_result is available
931 # in the packages_unittest.py for testing get_all_firmware_versions.
932 cmd_result = _get_firmware_version_cmd_result(build_target)
933
934 # There is a blank line between the version info for each model.
935 firmware_version_payloads = cmd_result.split('\n\n')
936 for firmware_version_payload in firmware_version_payloads:
937 if 'BIOS' in firmware_version_payload:
938 firmware_version = _find_firmware_versions(firmware_version_payload)
939 result[firmware_version.model] = firmware_version
940 return result
941
942
Michael Mortensen71ef5682020-05-07 14:29:24 -0600943FirmwareVersions = collections.namedtuple(
944 'FirmwareVersions', ['model', 'main', 'main_rw', 'ec', 'ec_rw'])
945
946
947def get_firmware_versions(build_target):
948 """Extract version information from the firmware updater, if one exists.
949
950 Args:
951 build_target (build_target_lib.BuildTarget): The build target.
952
953 Returns:
954 A FirmwareVersions namedtuple instance.
955 Each element will either be set to the string output by the firmware
956 updater shellball, or None if there is no firmware updater.
957 """
958 cros_build_lib.AssertInsideChroot()
959 cmd_result = _get_firmware_version_cmd_result(build_target)
960 if cmd_result:
961 return _find_firmware_versions(cmd_result)
962 else:
963 return FirmwareVersions(None, None, None, None, None)
964
965
966def _get_firmware_version_cmd_result(build_target):
967 """Gets the raw result output of the firmware updater version command.
968
969 Args:
970 build_target (build_target_lib.BuildTarget): The build target.
971
972 Returns:
973 Command execution result.
974 """
975 updater = os.path.join(build_target.root,
976 'usr/sbin/chromeos-firmwareupdate')
977 logging.info('Calling updater %s', updater)
978 # Call the updater using the chroot-based path.
979 return cros_build_lib.run([updater, '-V'],
980 capture_output=True, log_output=True,
981 encoding='utf-8').stdout
982
983
984def _find_firmware_versions(cmd_output):
985 """Finds firmware version output via regex matches against the cmd_output.
986
987 Args:
988 cmd_output: The raw output to search against.
989
990 Returns:
991 FirmwareVersions namedtuple with results.
992 Each element will either be set to the string output by the firmware
993 updater shellball, or None if there is no match.
994 """
995
996 # Sometimes a firmware bundle includes a special combination of RO+RW
997 # firmware. In this case, the RW firmware version is indicated with a "(RW)
998 # version" field. In other cases, the "(RW) version" field is not present.
999 # Therefore, search for the "(RW)" fields first and if they aren't present,
1000 # fallback to the other format. e.g. just "BIOS version:".
1001 # TODO(mmortensen): Use JSON once the firmware updater supports it.
1002 main = None
1003 main_rw = None
1004 ec = None
1005 ec_rw = None
1006 model = None
1007
1008 match = re.search(r'BIOS version:\s*(?P<version>.*)', cmd_output)
1009 if match:
1010 main = match.group('version')
1011
1012 match = re.search(r'BIOS \(RW\) version:\s*(?P<version>.*)', cmd_output)
1013 if match:
1014 main_rw = match.group('version')
1015
1016 match = re.search(r'EC version:\s*(?P<version>.*)', cmd_output)
1017 if match:
1018 ec = match.group('version')
1019
1020 match = re.search(r'EC \(RW\) version:\s*(?P<version>.*)', cmd_output)
1021 if match:
1022 ec_rw = match.group('version')
1023
1024 match = re.search(r'Model:\s*(?P<model>.*)', cmd_output)
1025 if match:
1026 model = match.group('model')
1027
1028 return FirmwareVersions(model, main, main_rw, ec, ec_rw)
Michael Mortensena4af79e2020-05-06 16:18:48 -06001029
1030
1031MainEcFirmwareVersions = collections.namedtuple(
1032 'MainEcFirmwareVersions', ['main_fw_version', 'ec_fw_version'])
1033
1034def determine_firmware_versions(build_target):
1035 """Returns a namedtuple with main and ec firmware versions.
1036
1037 Args:
1038 build_target (build_target_lib.BuildTarget): The build target.
1039
1040 Returns:
1041 MainEcFirmwareVersions namedtuple with results.
1042 """
1043 fw_versions = get_firmware_versions(build_target)
1044 main_fw_version = fw_versions.main_rw or fw_versions.main
1045 ec_fw_version = fw_versions.ec_rw or fw_versions.ec
1046
1047 return MainEcFirmwareVersions(main_fw_version, ec_fw_version)
Michael Mortensenfbf2b2d2020-05-14 16:33:06 -06001048
1049def determine_kernel_version(build_target):
1050 """Returns a string containing the kernel version for this build target.
1051
1052 Args:
1053 build_target (build_target_lib.BuildTarget): The build target.
1054
1055 Returns:
1056 (str) The kernel versions, or None.
1057 """
1058 try:
1059 packages = portage_util.GetPackageDependencies(build_target.name,
1060 'virtual/linux-sources')
1061 except cros_build_lib.RunCommandError as e:
1062 logging.warning('Unable to get package list for metadata: %s', e)
1063 return None
1064 for package in packages:
1065 if package.startswith('sys-kernel/chromeos-kernel-'):
Alex Klein18a60af2020-06-11 12:08:47 -06001066 kernel_version = package_info.SplitCPV(package).version
Michael Mortensenfbf2b2d2020-05-14 16:33:06 -06001067 logging.info('Found active kernel version: %s', kernel_version)
1068 return kernel_version
1069 return None
Michael Mortensen125bb012020-05-21 14:02:10 -06001070
1071
1072def get_models(build_target, log_output=True):
1073 """Obtain a list of models supported by a unified board.
1074
1075 This ignored whitelabel models since GoldenEye has no specific support for
1076 these at present.
1077
1078 Args:
1079 build_target (build_target_lib.BuildTarget): The build target.
1080 log_output: Whether to log the output of the cros_config_host invocation.
1081
1082 Returns:
1083 A list of models supported by this board, if it is a unified build; None,
1084 if it is not a unified build.
1085 """
1086 return _run_cros_config_host(build_target, ['list-models'],
1087 log_output=log_output)
1088
1089
Michael Mortensen359c1f32020-05-28 19:35:42 -06001090def get_key_id(build_target, model):
1091 """Obtain the key_id for a model within the build_target.
1092
1093 Args:
1094 build_target (build_target_lib.BuildTarget): The build target.
1095 model (str): The model name
1096
1097 Returns:
1098 A key_id (str) or None.
1099 """
1100 model_arg = '--model=' + model
1101 key_id_list = _run_cros_config_host(
1102 build_target,
1103 [model_arg, 'get', '/firmware-signing', 'key-id'])
1104 key_id = None
1105 if len(key_id_list) == 1:
1106 key_id = key_id_list[0]
1107 return key_id
1108
1109
Michael Mortensen125bb012020-05-21 14:02:10 -06001110def _run_cros_config_host(build_target, args, log_output=True):
1111 """Run the cros_config_host tool.
1112
1113 Args:
1114 build_target (build_target_lib.BuildTarget): The build target.
1115 args: List of arguments to pass.
1116 log_output: Whether to log the output of the cros_config_host.
1117
1118 Returns:
1119 Output of the tool
1120 """
1121 cros_build_lib.AssertInsideChroot()
1122 tool = '/usr/bin/cros_config_host'
1123 if not os.path.isfile(tool):
1124 return None
1125
1126 config_fname = build_target.full_path(
1127 'usr/share/chromeos-config/yaml/config.yaml')
1128
1129 result = cros_build_lib.run(
1130 [tool, '-c', config_fname] + args,
1131 capture_output=True,
1132 encoding='utf-8',
1133 log_output=log_output,
1134 check=False)
1135 if result.returncode:
1136 # Show the output for debugging purposes.
1137 if 'No such file or directory' not in result.error:
1138 logging.error('cros_config_host failed: %s\n', result.error)
1139 return None
1140 return result.output.strip().splitlines()