blob: 6be086ae182296fb5e4ba26cb25afb547279cb84 [file] [log] [blame]
Alex Kleineb77ffa2019-05-28 14:47:44 -06001# Copyright 2019 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Package utility functionality."""
6
Yaakov Shaul730814a2019-09-10 13:58:25 -06007import collections
Ben Reiche779cf42020-12-15 03:21:31 +00008from distutils.version import LooseVersion
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -06009import fileinput
Alex Klein87531182019-08-12 15:23:37 -060010import functools
Yaakov Shaul395ae832019-09-09 14:45:32 -060011import json
Evan Hernandezb51f1522019-08-15 11:29:40 -060012import os
Michael Mortensenb70e8a82019-10-10 18:43:41 -060013import re
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -060014import sys
Alex Klein9ce3f682021-06-23 15:06:44 -060015from typing import List, Optional, Union
Alex Klein87531182019-08-12 15:23:37 -060016
Mike Frysinger2c024062021-05-22 15:43:22 -040017from chromite.third_party.google.protobuf import json_format
Yaakov Shaul730814a2019-09-10 13:58:25 -060018
Andrew Lamb2bde9e42019-11-04 13:24:09 -070019from chromite.api.gen.config import replication_config_pb2
Michael Mortensen9fdb14b2019-10-17 11:17:30 -060020from chromite.cbuildbot import manifest_version
Alex Kleineb77ffa2019-05-28 14:47:44 -060021from chromite.lib import constants
Evan Hernandezb51f1522019-08-15 11:29:40 -060022from chromite.lib import cros_build_lib
Alex Klein4de25e82019-08-05 15:58:39 -060023from chromite.lib import cros_logging as logging
Alex Kleineb77ffa2019-05-28 14:47:44 -060024from chromite.lib import git
Michael Mortensende716a12020-05-15 11:27:00 -060025from chromite.lib import image_lib
Michael Mortensenb70e8a82019-10-10 18:43:41 -060026from chromite.lib import osutils
Alex Kleineb77ffa2019-05-28 14:47:44 -060027from chromite.lib import portage_util
Andrew Lamb2bde9e42019-11-04 13:24:09 -070028from chromite.lib import replication_lib
Alex Kleind6195b62019-08-06 16:01:16 -060029from chromite.lib import uprev_lib
Alex Klein18a60af2020-06-11 12:08:47 -060030from chromite.lib.parser import package_info
Alex Kleineb77ffa2019-05-28 14:47:44 -060031
Alex Klein36b117f2019-09-30 15:13:46 -060032if cros_build_lib.IsInsideChroot():
Alex Klein6becabc2020-09-11 14:03:05 -060033 from chromite.lib import depgraph
Alex Klein36b117f2019-09-30 15:13:46 -060034 from chromite.service import dependency
35
Alex Klein87531182019-08-12 15:23:37 -060036# Registered handlers for uprevving versioned packages.
37_UPREV_FUNCS = {}
38
Alex Kleineb77ffa2019-05-28 14:47:44 -060039
40class Error(Exception):
41 """Module's base error class."""
42
43
Alex Klein4de25e82019-08-05 15:58:39 -060044class UnknownPackageError(Error):
45 """Uprev attempted for a package without a registered handler."""
46
47
Alex Kleineb77ffa2019-05-28 14:47:44 -060048class UprevError(Error):
49 """An error occurred while uprevving packages."""
50
51
Michael Mortensenb70e8a82019-10-10 18:43:41 -060052class NoAndroidVersionError(Error):
53 """An error occurred while trying to determine the android version."""
54
55
56class NoAndroidBranchError(Error):
57 """An error occurred while trying to determine the android branch."""
58
59
60class NoAndroidTargetError(Error):
61 """An error occurred while trying to determine the android target."""
62
63
Alex Klein4de25e82019-08-05 15:58:39 -060064class AndroidIsPinnedUprevError(UprevError):
65 """Raised when we try to uprev while Android is pinned."""
66
67 def __init__(self, new_android_atom):
68 """Initialize a AndroidIsPinnedUprevError.
69
70 Args:
71 new_android_atom: The Android atom that we failed to
72 uprev to, due to Android being pinned.
73 """
74 assert new_android_atom
75 msg = ('Failed up uprev to Android version %s as Android was pinned.' %
76 new_android_atom)
77 super(AndroidIsPinnedUprevError, self).__init__(msg)
78 self.new_android_atom = new_android_atom
Alex Klein87531182019-08-12 15:23:37 -060079
80
Andrew Lamb9563a152019-12-04 11:42:18 -070081class GeneratedCrosConfigFilesError(Error):
82 """Error when cros_config_schema does not produce expected files"""
83
84 def __init__(self, expected_files, found_files):
85 msg = ('Expected to find generated C files: %s. Actually found: %s' %
86 (expected_files, found_files))
87 super(GeneratedCrosConfigFilesError, self).__init__(msg)
88
Alex Klein7a3a7dd2020-01-08 16:44:38 -070089
Alex Klein6becabc2020-09-11 14:03:05 -060090NeedsChromeSourceResult = collections.namedtuple('NeedsChromeSourceResult', (
91 'needs_chrome_source',
92 'builds_chrome',
93 'packages',
94 'missing_chrome_prebuilt',
95 'missing_follower_prebuilt',
Alex Klein9ce3f682021-06-23 15:06:44 -060096 'local_uprev',
Alex Klein6becabc2020-09-11 14:03:05 -060097))
98
99
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600100def patch_ebuild_vars(ebuild_path, variables):
101 """Updates variables in ebuild.
102
103 Use this function rather than portage_util.EBuild.UpdateEBuild when you
104 want to preserve the variable position and quotes within the ebuild.
105
106 Args:
107 ebuild_path: The path of the ebuild.
108 variables: Dictionary of variables to update in ebuild.
109 """
110 try:
111 for line in fileinput.input(ebuild_path, inplace=1):
112 varname, eq, _ = line.partition('=')
113 if eq == '=' and varname.strip() in variables:
114 value = variables[varname]
115 sys.stdout.write('%s="%s"\n' % (varname, value))
116 else:
117 sys.stdout.write(line)
118 finally:
119 fileinput.close()
120
121
Alex Klein87531182019-08-12 15:23:37 -0600122def uprevs_versioned_package(package):
123 """Decorator to register package uprev handlers."""
124 assert package
125
126 def register(func):
127 """Registers |func| as a handler for |package|."""
128 _UPREV_FUNCS[package] = func
129
130 @functools.wraps(func)
131 def pass_through(*args, **kwargs):
132 return func(*args, **kwargs)
133
134 return pass_through
135
136 return register
137
138
Shao-Chuan Leeb6a2cb62021-02-19 14:18:59 +0900139def uprev_android(android_package,
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700140 chroot,
141 build_targets=None,
Shao-Chuan Leea4b4f302021-05-12 14:40:20 +0900142 android_build_branch=None,
Shao-Chuan Lee85ba7ce2021-02-09 13:50:11 +0900143 android_version=None,
144 skip_commit=False):
Alex Klein4de25e82019-08-05 15:58:39 -0600145 """Returns the portage atom for the revved Android ebuild - see man emerge."""
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700146 command = [
147 'cros_mark_android_as_stable',
Shao-Chuan Lee85ba7ce2021-02-09 13:50:11 +0900148 f'--android_package={android_package}',
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700149 ]
Alex Klein4de25e82019-08-05 15:58:39 -0600150 if build_targets:
Shao-Chuan Lee85ba7ce2021-02-09 13:50:11 +0900151 command.append(f'--boards={":".join(bt.name for bt in build_targets)}')
Shao-Chuan Leea4b4f302021-05-12 14:40:20 +0900152 if android_build_branch:
153 command.append(f'--android_build_branch={android_build_branch}')
Alex Klein4de25e82019-08-05 15:58:39 -0600154 if android_version:
Shao-Chuan Lee85ba7ce2021-02-09 13:50:11 +0900155 command.append(f'--force_version={android_version}')
156 if skip_commit:
157 command.append('--skip_commit')
Alex Klein4de25e82019-08-05 15:58:39 -0600158
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700159 result = cros_build_lib.run(
160 command,
161 stdout=True,
162 enter_chroot=True,
Mike Frysinger88d96362020-02-14 19:05:45 -0500163 encoding='utf-8',
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700164 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600165
Mike Frysinger88d96362020-02-14 19:05:45 -0500166 portage_atom_string = result.stdout.strip()
167 android_atom = None
168 if portage_atom_string:
169 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
Alex Klein4de25e82019-08-05 15:58:39 -0600170 if not android_atom:
171 logging.info('Found nothing to rev.')
172 return None
173
174 for target in build_targets or []:
175 # Sanity check: We should always be able to merge the version of
176 # Android we just unmasked.
Shao-Chuan Lee85ba7ce2021-02-09 13:50:11 +0900177 command = [f'emerge-{target.name}', '-p', '--quiet', f'={android_atom}']
Alex Klein4de25e82019-08-05 15:58:39 -0600178 try:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700179 cros_build_lib.run(
180 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600181 except cros_build_lib.RunCommandError:
182 logging.error(
183 'Cannot emerge-%s =%s\nIs Android pinned to an older '
184 'version?', target, android_atom)
185 raise AndroidIsPinnedUprevError(android_atom)
186
187 return android_atom
188
189
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700190def uprev_build_targets(build_targets,
191 overlay_type,
192 chroot=None,
Alex Kleineb77ffa2019-05-28 14:47:44 -0600193 output_dir=None):
194 """Uprev the set provided build targets, or all if not specified.
195
196 Args:
Alex Klein2960c752020-03-09 13:43:38 -0600197 build_targets (list[build_target_lib.BuildTarget]|None): The build targets
Alex Kleineb77ffa2019-05-28 14:47:44 -0600198 whose overlays should be uprevved, empty or None for all.
199 overlay_type (str): One of the valid overlay types except None (see
200 constants.VALID_OVERLAYS).
201 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
202 output_dir (str|None): The path to optionally dump result files.
203 """
204 # Need a valid overlay, but exclude None.
205 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
206
207 if build_targets:
208 overlays = portage_util.FindOverlaysForBoards(
209 overlay_type, boards=[t.name for t in build_targets])
210 else:
211 overlays = portage_util.FindOverlays(overlay_type)
212
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700213 return uprev_overlays(
214 overlays,
215 build_targets=build_targets,
216 chroot=chroot,
217 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600218
219
220def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
221 """Uprev the given overlays.
222
223 Args:
224 overlays (list[str]): The list of overlay paths.
Alex Klein2960c752020-03-09 13:43:38 -0600225 build_targets (list[build_target_lib.BuildTarget]|None): The build targets
Alex Kleineb77ffa2019-05-28 14:47:44 -0600226 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
227 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
228 output_dir (str|None): The path to optionally dump result files.
229
230 Returns:
231 list[str] - The paths to all of the modified ebuild files. This includes the
232 new files that were added (i.e. the new versions) and all of the removed
233 files (i.e. the old versions).
234 """
235 assert overlays
236
237 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
238
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700239 uprev_manager = uprev_lib.UprevOverlayManager(
240 overlays,
241 manifest,
242 build_targets=build_targets,
243 chroot=chroot,
244 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600245 uprev_manager.uprev()
246
247 return uprev_manager.modified_ebuilds
248
249
Alex Klein87531182019-08-12 15:23:37 -0600250def uprev_versioned_package(package, build_targets, refs, chroot):
251 """Call registered uprev handler function for the package.
252
253 Args:
Alex Klein75df1792020-06-11 14:42:49 -0600254 package (package_info.CPV): The package being uprevved.
Alex Klein2960c752020-03-09 13:43:38 -0600255 build_targets (list[build_target_lib.BuildTarget]): The build targets to
Alex Klein87531182019-08-12 15:23:37 -0600256 clean on a successful uprev.
257 refs (list[uprev_lib.GitRef]):
258 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
259
260 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600261 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600262 """
263 assert package
264
265 if package.cp not in _UPREV_FUNCS:
266 raise UnknownPackageError(
267 'Package "%s" does not have a registered handler.' % package.cp)
268
Andrew Lambea9a8a22019-12-12 14:03:43 -0700269 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
Alex Klein87531182019-08-12 15:23:37 -0600270
271
Navil Perezf57ba872020-06-04 22:38:37 +0000272@uprevs_versioned_package('media-libs/virglrenderer')
273def uprev_virglrenderer(_build_targets, refs, _chroot):
274 """Updates virglrenderer ebuilds.
275
276 See: uprev_versioned_package.
277
278 Returns:
279 UprevVersionedPackageResult: The result of updating virglrenderer ebuilds.
280 """
Navil Perezf57ba872020-06-04 22:38:37 +0000281 overlay = os.path.join(constants.SOURCE_ROOT,
282 constants.CHROMIUMOS_OVERLAY_DIR)
George Engelbrechte73f2782020-06-10 14:10:46 -0600283 repo_path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
284 'virglrenderer')
285 manifest = git.ManifestCheckout.Cached(repo_path)
Navil Perezf57ba872020-06-04 22:38:37 +0000286
287 uprev_manager = uprev_lib.UprevOverlayManager([overlay], manifest)
288 # TODO(crbug.com/1066242): Ebuilds for virglrenderer are currently
Jose Magana03b5a842020-08-19 12:52:59 +1000289 # denylisted. Do not force uprevs after builder is stable and ebuilds are no
290 # longer denylisted.
Navil Perezf57ba872020-06-04 22:38:37 +0000291 uprev_manager.uprev(package_list=['media-libs/virglrenderer'], force=True)
292
George Engelbrechte73f2782020-06-10 14:10:46 -0600293 updated_files = uprev_manager.modified_ebuilds
Chris McDonald38409112020-09-24 11:24:51 -0600294 result = uprev_lib.UprevVersionedPackageResult()
Navil Perezf57ba872020-06-04 22:38:37 +0000295 result.add_result(refs[0].revision, updated_files)
296 return result
297
Jose Magana03b5a842020-08-19 12:52:59 +1000298@uprevs_versioned_package('chromeos-base/drivefs')
299def uprev_drivefs(_build_targets, refs, chroot):
300 """Updates drivefs ebuilds.
301
Harvey Yang3eee06c2021-03-18 15:47:56 +0800302 DriveFS versions follow the tag format of refs/tags/drivefs_1.2.3.
Jose Magana03b5a842020-08-19 12:52:59 +1000303 See: uprev_versioned_package.
304
305 Returns:
306 UprevVersionedPackageResult: The result of updating drivefs ebuilds.
307 """
308
Ben Reiche779cf42020-12-15 03:21:31 +0000309 DRIVEFS_PATH_PREFIX = 'src/private-overlays/chromeos-overlay/chromeos-base'
Ben Reich4f3fa1b2020-12-19 08:21:26 +0000310 result = uprev_lib.UprevVersionedPackageResult()
311 all_changed_files = []
Jose Magana03b5a842020-08-19 12:52:59 +1000312
Harvey Yang3eee06c2021-03-18 15:47:56 +0800313 DRIVEFS_REFS_PREFIX = 'refs/tags/drivefs_'
314 drivefs_version = _get_latest_version_from_refs(DRIVEFS_REFS_PREFIX, refs)
Ben Reiche779cf42020-12-15 03:21:31 +0000315 if not drivefs_version:
316 # No valid DriveFS version is identified.
Ben Reich4f3fa1b2020-12-19 08:21:26 +0000317 return result
Jose Magana03b5a842020-08-19 12:52:59 +1000318
Ben Reiche779cf42020-12-15 03:21:31 +0000319 logging.debug('DriveFS version determined from refs: %s', drivefs_version)
Jose Magana03b5a842020-08-19 12:52:59 +1000320
Ben Reich4f3fa1b2020-12-19 08:21:26 +0000321 # Attempt to uprev drivefs package.
Ben Reiche779cf42020-12-15 03:21:31 +0000322 pkg_path = os.path.join(DRIVEFS_PATH_PREFIX, 'drivefs')
Jose Magana03b5a842020-08-19 12:52:59 +1000323 uprev_result = uprev_lib.uprev_workon_ebuild_to_version(pkg_path,
Ben Reiche779cf42020-12-15 03:21:31 +0000324 drivefs_version,
Harvey Yang3eee06c2021-03-18 15:47:56 +0800325 chroot,
326 allow_downrev=False)
Jose Magana03b5a842020-08-19 12:52:59 +1000327
328 if not uprev_result:
Ben Reich4f3fa1b2020-12-19 08:21:26 +0000329 return result
Jose Magana03b5a842020-08-19 12:52:59 +1000330 all_changed_files.extend(uprev_result.changed_files)
331
Ben Reich4f3fa1b2020-12-19 08:21:26 +0000332 # Attempt to uprev drivefs-ipc package.
Ben Reiche779cf42020-12-15 03:21:31 +0000333 pkg_path = os.path.join(DRIVEFS_PATH_PREFIX, 'drivefs-ipc')
Jose Magana03b5a842020-08-19 12:52:59 +1000334 uprev_result = uprev_lib.uprev_workon_ebuild_to_version(pkg_path,
Ben Reiche779cf42020-12-15 03:21:31 +0000335 drivefs_version,
Harvey Yang3eee06c2021-03-18 15:47:56 +0800336 chroot,
337 allow_downrev=False)
Jose Magana03b5a842020-08-19 12:52:59 +1000338
339 if not uprev_result:
Ben Reich4f3fa1b2020-12-19 08:21:26 +0000340 logging.warning(
341 'drivefs package has changed files %s but drivefs-ipc does not',
342 all_changed_files)
343 return result
Jose Magana03b5a842020-08-19 12:52:59 +1000344
345 all_changed_files.extend(uprev_result.changed_files)
Ben Reiche779cf42020-12-15 03:21:31 +0000346 result.add_result(drivefs_version, all_changed_files)
Jose Magana03b5a842020-08-19 12:52:59 +1000347
348 return result
349
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800350@uprevs_versioned_package('chromeos-base/perfetto')
351def uprev_perfetto(_build_targets, refs, chroot):
352 """Updates Perfetto ebuilds.
353
Harvey Yang3eee06c2021-03-18 15:47:56 +0800354 Perfetto versions follow the tag format of refs/tags/v1.2.
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800355 See: uprev_versioned_package.
356
357 Returns:
358 UprevVersionedPackageResult: The result of updating Perfetto ebuilds.
359 """
360 result = uprev_lib.UprevVersionedPackageResult()
361
Harvey Yang3eee06c2021-03-18 15:47:56 +0800362 PERFETTO_REFS_PREFIX = 'refs/tags/v'
363 perfetto_version = _get_latest_version_from_refs(PERFETTO_REFS_PREFIX, refs)
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800364 if not perfetto_version:
365 # No valid Perfetto version is identified.
366 return result
367
368 logging.debug('Perfetto version determined from refs: %s', perfetto_version)
369
370 # Attempt to uprev perfetto package.
371 PERFETTO_PATH = 'src/third_party/chromiumos-overlay/chromeos-base/perfetto'
372
Harvey Yang3eee06c2021-03-18 15:47:56 +0800373 uprev_result = uprev_lib.uprev_workon_ebuild_to_version(
374 PERFETTO_PATH,
375 perfetto_version,
376 chroot,
377 allow_downrev=False,
378 ref=PERFETTO_REFS_PREFIX + perfetto_version)
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800379
380 if not uprev_result:
381 return result
382
383 result.add_result(perfetto_version, uprev_result.changed_files)
384
385 return result
Navil Perezf57ba872020-06-04 22:38:37 +0000386
Yaakov Shaul395ae832019-09-09 14:45:32 -0600387@uprevs_versioned_package('afdo/kernel-profiles')
388def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600389 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600390
391 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600392
393 Raises:
394 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600395 """
396 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
397 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
398
David Burger92485342019-09-10 17:52:45 -0600399 with open(path, 'r') as f:
400 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600401
Chris McDonald38409112020-09-24 11:24:51 -0600402 result = uprev_lib.UprevVersionedPackageResult()
Alex Klein6edd4e62021-07-08 12:43:25 -0600403 for kernel_pkg, version_info in versions.items():
404 path = os.path.join(constants.CHROMIUMOS_OVERLAY_DIR,
405 'sys-kernel', kernel_pkg)
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600406 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
Alex Klein6edd4e62021-07-08 12:43:25 -0600407 '%s-9999.ebuild' % kernel_pkg)
Yaakov Shaula187b152019-09-11 12:41:32 -0600408 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
Alex Klein6edd4e62021-07-08 12:43:25 -0600409 '%s-9999.ebuild' % kernel_pkg)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600410 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600411 patch_ebuild_vars(ebuild_path,
412 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600413
414 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600415 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400416 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600417 except cros_build_lib.RunCommandError as e:
Chris McDonald38409112020-09-24 11:24:51 -0600418 raise uprev_lib.EbuildManifestError(
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600419 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600420 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600421
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600422 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600423
Yaakov Shaul730814a2019-09-10 13:58:25 -0600424 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
425
426 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600427
428
Trent Begineb624182020-07-14 10:09:45 -0600429@uprevs_versioned_package('chromeos-base/termina-dlc')
430def uprev_termina_dlc(_build_targets, _refs, chroot):
431 """Updates shared termina-dlc ebuild - chromeos-base/termina-dlc.
Trent Beginaf51f1b2020-03-09 17:35:31 -0600432
433 See: uprev_versioned_package.
434 """
Trent Begineb624182020-07-14 10:09:45 -0600435 package = 'termina-dlc'
Trent Beginaf51f1b2020-03-09 17:35:31 -0600436 package_path = os.path.join(constants.CHROMIUMOS_OVERLAY_DIR, 'chromeos-base',
437 package)
Patrick Meiring5897add2020-09-16 16:30:17 +1000438
439 version_pin_src_path = _get_version_pin_src_path(package_path)
440 version_no_rev = osutils.ReadFile(version_pin_src_path).strip()
441
Chris McDonald38409112020-09-24 11:24:51 -0600442 return uprev_lib.uprev_ebuild_from_pin(package_path, version_no_rev, chroot)
Patrick Meiring5897add2020-09-16 16:30:17 +1000443
444
Julio Hurtadof1befec2021-05-05 21:34:26 +0000445@uprevs_versioned_package('chromeos-base/chromeos-lacros')
446def uprev_lacros(_build_targets, refs, chroot):
447 """Updates lacros ebuilds.
448
449 Information used is gathered from the QA qualified version tracking file
450 stored in chromium/src.
451
452 See: uprev_versioned_package.
453 """
454 path = os.path.join(
455 constants.CHROMIUMOS_OVERLAY_DIR, 'chromeos-base','chromeos-lacros')
456 return uprev_lib.uprev_ebuild_from_pin(path, refs[0].revision, chroot)
457
458
Patrick Meiring5897add2020-09-16 16:30:17 +1000459@uprevs_versioned_package('app-emulation/parallels-desktop')
460def uprev_parallels_desktop(_build_targets, _refs, chroot):
461 """Updates Parallels Desktop ebuild - app-emulation/parallels-desktop.
462
463 See: uprev_versioned_package
464
465 Returns:
466 UprevVersionedPackageResult: The result.
467 """
468 package = 'parallels-desktop'
469 package_path = os.path.join(constants.CHROMEOS_PARTNER_OVERLAY_DIR,
470 'app-emulation', package)
471 version_pin_src_path = _get_version_pin_src_path(package_path)
472
473 # Expect a JSON blob like the following:
474 # {
475 # "version": "1.2.3",
476 # "test_image": { "url": "...", "size": 12345678,
477 # "sha256sum": "<32 bytes of hexadecimal>" }
478 # }
479 with open(version_pin_src_path, 'r') as f:
480 pinned = json.load(f)
481
482 if 'version' not in pinned or 'test_image' not in pinned:
483 raise UprevError('VERSION-PIN for %s missing version and/or '
484 'test_image field' % package)
485
486 version = pinned['version']
487 if not isinstance(version, str):
488 raise UprevError('version in VERSION-PIN for %s not a string' % package)
489
490 # Update the ebuild.
Chris McDonald38409112020-09-24 11:24:51 -0600491 result = uprev_lib.uprev_ebuild_from_pin(package_path, version, chroot)
Patrick Meiring5897add2020-09-16 16:30:17 +1000492
493 # Update the VM image used for testing.
Patrick Meiring1342def2020-10-30 17:09:13 +1100494 test_image_path = ('src/platform/tast-tests-private/src/chromiumos/tast/'
495 'local/bundles/crosint/pita/data/'
496 'pluginvm_image.zip.external')
Patrick Meiring5897add2020-09-16 16:30:17 +1000497 test_image_src_path = os.path.join(constants.SOURCE_ROOT, test_image_path)
498 with open(test_image_src_path, 'w') as f:
499 json.dump(pinned['test_image'], f, indent=2)
500 result.add_result(version, [test_image_src_path])
501
502 return result
Trent Beginaf51f1b2020-03-09 17:35:31 -0600503
504
Trent Begin315d9d92019-12-03 21:55:53 -0700505@uprevs_versioned_package('chromeos-base/chromeos-dtc-vm')
Trent Beginaf51f1b2020-03-09 17:35:31 -0600506def uprev_sludge(_build_targets, _refs, chroot):
Trent Begin315d9d92019-12-03 21:55:53 -0700507 """Updates sludge VM - chromeos-base/chromeos-dtc-vm.
508
509 See: uprev_versioned_package.
510 """
511 package = 'chromeos-dtc-vm'
Trent Begind943df92020-02-25 10:30:10 -0700512 package_path = os.path.join('src', 'private-overlays',
513 'project-wilco-private', 'chromeos-base', package)
Patrick Meiring5897add2020-09-16 16:30:17 +1000514 version_pin_src_path = _get_version_pin_src_path(package_path)
515 version_no_rev = osutils.ReadFile(version_pin_src_path).strip()
Trent Begin315d9d92019-12-03 21:55:53 -0700516
Chris McDonald38409112020-09-24 11:24:51 -0600517 return uprev_lib.uprev_ebuild_from_pin(package_path, version_no_rev, chroot)
Trent Begin315d9d92019-12-03 21:55:53 -0700518
519
Patrick Meiring5897add2020-09-16 16:30:17 +1000520def _get_version_pin_src_path(package_path):
521 """Returns the path to the VERSION-PIN file for the given package."""
522 return os.path.join(constants.SOURCE_ROOT, package_path, 'VERSION-PIN')
523
524
Alex Klein87531182019-08-12 15:23:37 -0600525@uprevs_versioned_package(constants.CHROME_CP)
Alex Kleinf69bd802021-06-22 15:43:49 -0600526def uprev_chrome_from_ref(build_targets, refs, chroot):
Alex Klein87531182019-08-12 15:23:37 -0600527 """Uprev chrome and its related packages.
528
529 See: uprev_versioned_package.
530 """
531 # Determine the version from the refs (tags), i.e. the chrome versions are the
532 # tag names.
533 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
Chris McDonald25881af2020-05-12 03:17:53 -0600534 logging.debug('Chrome version determined from refs: %s', chrome_version)
Alex Klein87531182019-08-12 15:23:37 -0600535
Alex Kleinf69bd802021-06-22 15:43:49 -0600536 return uprev_chrome(build_targets, chrome_version, chroot)
537
538
Alex Klein9ce3f682021-06-23 15:06:44 -0600539def revbump_chrome(
540 build_targets: List['build_target_lib.BuildTarget'],
541 chroot: Optional['chroot_lib.Chroot'] = None
542) -> uprev_lib.UprevVersionedPackageResult:
Alex Kleinf69bd802021-06-22 15:43:49 -0600543 """Attempt to revbump chrome.
544
545 Revbumps are done by executing an uprev using the current stable version.
546 E.g. if chrome is on 1.2.3.4 and has a 1.2.3.4_rc-r2.ebuild, performing an
547 uprev on version 1.2.3.4 when there are applicable changes (e.g. to the 9999
548 ebuild) will result in a revbump to 1.2.3.4_rc-r3.ebuild.
549 """
550 chrome_version = uprev_lib.get_stable_chrome_version()
551 return uprev_chrome(build_targets, chrome_version, chroot)
552
553
Alex Klein9ce3f682021-06-23 15:06:44 -0600554def uprev_chrome(
555 build_targets: List['build_target_lib.BuildTarget'], chrome_version: str,
556 chroot: Union['chroot_lib.Chroot', None]
557) -> uprev_lib.UprevVersionedPackageResult:
558 """Attempt to uprev chrome and its related packages to the given version."""
Alex Klein87531182019-08-12 15:23:37 -0600559 uprev_manager = uprev_lib.UprevChromeManager(
560 chrome_version, build_targets=build_targets, chroot=chroot)
Chris McDonald38409112020-09-24 11:24:51 -0600561 result = uprev_lib.UprevVersionedPackageResult()
Chris McDonald25881af2020-05-12 03:17:53 -0600562 # TODO(crbug.com/1080429): Handle all possible outcomes of a Chrome uprev
563 # attempt. The expected behavior is documented in the following table:
564 #
565 # Outcome of Chrome uprev attempt:
566 # NEWER_VERSION_EXISTS:
567 # Do nothing.
568 # SAME_VERSION_EXISTS or REVISION_BUMP:
569 # Uprev followers
570 # Assert not VERSION_BUMP (any other outcome is fine)
571 # VERSION_BUMP or NEW_EBUILD_CREATED:
572 # Uprev followers
573 # Assert that Chrome & followers are at same package version
Alex Klein0b2ec2d2021-06-23 15:56:45 -0600574
575 # Start with chrome itself so we can proceed accordingly.
576 chrome_result = uprev_manager.uprev(constants.CHROME_CP)
577 if chrome_result.newer_version_exists:
578 # Cannot use the given version (newer version already exists).
David Burger37f48672019-09-18 17:07:56 -0600579 return result
Alex Klein87531182019-08-12 15:23:37 -0600580
Alex Klein0b2ec2d2021-06-23 15:56:45 -0600581 # Also uprev related packages.
Alex Klein87531182019-08-12 15:23:37 -0600582 for package in constants.OTHER_CHROME_PACKAGES:
Alex Klein0b2ec2d2021-06-23 15:56:45 -0600583 follower_result = uprev_manager.uprev(package)
584 if chrome_result.stable_version and follower_result.version_bump:
585 logging.warning('%s had a version bump, but no more than a revision bump '
586 'should have been possible.', package)
Alex Klein87531182019-08-12 15:23:37 -0600587
Alex Klein0b2ec2d2021-06-23 15:56:45 -0600588 if uprev_manager.modified_ebuilds:
589 # Record changes when we have them.
590 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
591
592 return result
Alex Klein87531182019-08-12 15:23:37 -0600593
594
Harvey Yang3eee06c2021-03-18 15:47:56 +0800595def _get_latest_version_from_refs(refs_prefix: str,
596 refs: List[uprev_lib.GitRef]) -> str:
597 """Get the latest version from refs
Ben Reiche779cf42020-12-15 03:21:31 +0000598
Ben Reiche779cf42020-12-15 03:21:31 +0000599 Versions are compared using |distutils.version.LooseVersion| and
600 the latest version is returned.
601
602 Args:
Harvey Yang3eee06c2021-03-18 15:47:56 +0800603 refs_prefix: The refs prefix of the tag format.
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800604 refs: The tags to parse for the latest Perfetto version.
605
606 Returns:
607 The latest Perfetto version to use.
608 """
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800609 valid_refs = []
610 for gitiles in refs:
Harvey Yang3eee06c2021-03-18 15:47:56 +0800611 if gitiles.ref.startswith(refs_prefix):
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800612 valid_refs.append(gitiles.ref)
613
614 if not valid_refs:
615 return None
616
617 # Sort by version and take the latest version.
618 target_version_ref = sorted(valid_refs,
619 key=LooseVersion,
620 reverse=True)[0]
Harvey Yang3eee06c2021-03-18 15:47:56 +0800621 return target_version_ref.replace(refs_prefix, '')
Harvey Yang9c61e9c2021-03-02 16:32:43 +0800622
623
Andrew Lamb9563a152019-12-04 11:42:18 -0700624def _generate_platform_c_files(replication_config, chroot):
625 """Generates platform C files from a platform JSON payload.
626
627 Args:
628 replication_config (replication_config_pb2.ReplicationConfig): A
629 ReplicationConfig that has already been run. If it produced a
630 build_config.json file, that file will be used to generate platform C
631 files. Otherwise, nothing will be generated.
632 chroot (chroot_lib.Chroot): The chroot to use to generate.
633
634 Returns:
635 A list of generated files.
636 """
637 # Generate the platform C files from the build config. Note that it would be
638 # more intuitive to generate the platform C files from the platform config;
639 # however, cros_config_schema does not allow this, because the platform config
640 # payload is not always valid input. For example, if a property is both
641 # 'required' and 'build-only', it will fail schema validation. Thus, use the
642 # build config, and use '-f' to filter.
643 build_config_path = [
644 rule.destination_path
645 for rule in replication_config.file_replication_rules
646 if rule.destination_path.endswith('build_config.json')
647 ]
648
649 if not build_config_path:
650 logging.info(
Alex Kleinad6b48a2020-01-08 16:57:41 -0700651 'No build_config.json found, will not generate platform C files. '
652 'Replication config: %s', replication_config)
Andrew Lamb9563a152019-12-04 11:42:18 -0700653 return []
654
655 if len(build_config_path) > 1:
Alex Kleinad6b48a2020-01-08 16:57:41 -0700656 raise ValueError('Expected at most one build_config.json destination path. '
657 'Replication config: %s' % replication_config)
Andrew Lamb9563a152019-12-04 11:42:18 -0700658
659 build_config_path = build_config_path[0]
660
661 # Paths to the build_config.json and dir to output C files to, in the
662 # chroot.
663 build_config_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
664 build_config_path)
665 generated_output_chroot_dir = os.path.join(constants.CHROOT_SOURCE_ROOT,
666 os.path.dirname(build_config_path))
667
668 command = [
669 'cros_config_schema', '-m', build_config_chroot_path, '-g',
670 generated_output_chroot_dir, '-f', '"TRUE"'
671 ]
672
673 cros_build_lib.run(
674 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
675
676 # A relative (to the source root) path to the generated C files.
677 generated_output_dir = os.path.dirname(build_config_path)
678 generated_files = []
679 expected_c_files = ['config.c', 'ec_config.c', 'ec_config.h']
680 for f in expected_c_files:
681 if os.path.exists(
682 os.path.join(constants.SOURCE_ROOT, generated_output_dir, f)):
683 generated_files.append(os.path.join(generated_output_dir, f))
684
685 if len(expected_c_files) != len(generated_files):
686 raise GeneratedCrosConfigFilesError(expected_c_files, generated_files)
687
688 return generated_files
689
690
Andrew Lambe836f222019-12-09 12:27:38 -0700691def _get_private_overlay_package_root(ref, package):
692 """Returns the absolute path to the root of a given private overlay.
693
694 Args:
695 ref (uprev_lib.GitRef): GitRef for the private overlay.
696 package (str): Path to the package in the overlay.
697 """
698 # There might be a cleaner way to map from package -> path within the source
699 # tree. For now, just use string patterns.
Andrew Lamb4aa09912020-01-08 13:55:56 -0700700 private_overlay_ref_pattern = r'/chromeos\/overlays\/overlay-([\w-]+)-private'
Andrew Lambe836f222019-12-09 12:27:38 -0700701 match = re.match(private_overlay_ref_pattern, ref.path)
702 if not match:
703 raise ValueError('ref.path must match the pattern: %s. Actual ref: %s' %
704 (private_overlay_ref_pattern, ref))
705
706 overlay = match.group(1)
707
708 return os.path.join(constants.SOURCE_ROOT,
709 'src/private-overlays/overlay-%s-private' % overlay,
710 package)
711
712
Andrew Lambea9a8a22019-12-12 14:03:43 -0700713@uprevs_versioned_package('chromeos-base/chromeos-config-bsp')
714def replicate_private_config(_build_targets, refs, chroot):
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700715 """Replicate a private cros_config change to the corresponding public config.
716
Alex Kleinad6b48a2020-01-08 16:57:41 -0700717 See uprev_versioned_package for args
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700718 """
Andrew Lambea9a8a22019-12-12 14:03:43 -0700719 package = 'chromeos-base/chromeos-config-bsp'
720
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700721 if len(refs) != 1:
722 raise ValueError('Expected exactly one ref, actual %s' % refs)
723
724 # Expect a replication_config.jsonpb in the package root.
Andrew Lambe836f222019-12-09 12:27:38 -0700725 package_root = _get_private_overlay_package_root(refs[0], package)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700726 replication_config_path = os.path.join(package_root,
727 'replication_config.jsonpb')
728
729 try:
730 replication_config = json_format.Parse(
731 osutils.ReadFile(replication_config_path),
732 replication_config_pb2.ReplicationConfig())
733 except IOError:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700734 raise ValueError(
735 'Expected ReplicationConfig missing at %s' % replication_config_path)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700736
737 replication_lib.Replicate(replication_config)
738
739 modified_files = [
740 rule.destination_path
741 for rule in replication_config.file_replication_rules
742 ]
743
Andrew Lamb9563a152019-12-04 11:42:18 -0700744 # The generated platform C files are not easily filtered by replication rules,
745 # i.e. JSON / proto filtering can be described by a FieldMask, arbitrary C
746 # files cannot. Therefore, replicate and filter the JSON payloads, and then
747 # generate filtered C files from the JSON payload.
748 modified_files.extend(_generate_platform_c_files(replication_config, chroot))
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700749
750 # Use the private repo's commit hash as the new version.
751 new_private_version = refs[0].revision
752
Andrew Lamb988f4da2019-12-10 10:16:43 -0700753 # modified_files should contain only relative paths at this point, but the
754 # returned UprevVersionedPackageResult must contain only absolute paths.
755 for i, modified_file in enumerate(modified_files):
756 assert not os.path.isabs(modified_file)
757 modified_files[i] = os.path.join(constants.SOURCE_ROOT, modified_file)
758
Chris McDonald38409112020-09-24 11:24:51 -0600759 return uprev_lib.UprevVersionedPackageResult().add_result(
760 new_private_version, modified_files)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700761
762
Alex Kleinbbef2b32019-08-27 10:38:50 -0600763def get_best_visible(atom, build_target=None):
764 """Returns the best visible CPV for the given atom.
765
766 Args:
767 atom (str): The atom to look up.
Alex Klein2960c752020-03-09 13:43:38 -0600768 build_target (build_target_lib.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600769 sysroot should be searched, or the SDK if not provided.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700770
771 Returns:
Alex Klein75df1792020-06-11 14:42:49 -0600772 package_info.CPV|None: The best visible package.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600773 """
David Burger1e0fe232019-07-01 14:52:07 -0600774 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600775
776 board = build_target.name if build_target else None
777 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600778
779
Alex Klein149fd3b2019-12-16 16:01:05 -0700780def has_prebuilt(atom, build_target=None, useflags=None):
Alex Kleinda39c6d2019-09-16 14:36:36 -0600781 """Check if a prebuilt exists.
782
783 Args:
784 atom (str): The package whose prebuilt is being queried.
Alex Klein2960c752020-03-09 13:43:38 -0600785 build_target (build_target_lib.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600786 sysroot should be searched, or the SDK if not provided.
Alex Klein149fd3b2019-12-16 16:01:05 -0700787 useflags: Any additional USE flags that should be set. May be a string
788 of properly formatted USE flags, or an iterable of individual flags.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700789
790 Returns:
791 bool: True iff there is an available prebuilt, False otherwise.
Alex Kleinda39c6d2019-09-16 14:36:36 -0600792 """
793 assert atom
794
795 board = build_target.name if build_target else None
Alex Klein149fd3b2019-12-16 16:01:05 -0700796 extra_env = None
797 if useflags:
798 new_flags = useflags
Mike Frysingercfecd6b2020-12-14 23:54:05 -0500799 if not isinstance(useflags, str):
Alex Klein149fd3b2019-12-16 16:01:05 -0700800 new_flags = ' '.join(useflags)
801
802 existing = os.environ.get('USE', '')
803 final_flags = '%s %s' % (existing, new_flags)
804 extra_env = {'USE': final_flags.strip()}
805 return portage_util.HasPrebuilt(atom, board=board, extra_env=extra_env)
Alex Klein36b117f2019-09-30 15:13:46 -0600806
807
David Burger0f9dd4e2019-10-08 12:33:42 -0600808def builds(atom, build_target, packages=None):
Alex Klein36b117f2019-09-30 15:13:46 -0600809 """Check if |build_target| builds |atom| (has it in its depgraph)."""
810 cros_build_lib.AssertInsideChroot()
811
Alex Kleind8cd4c62020-09-14 13:37:47 -0600812 pkgs = tuple(packages) if packages else None
LaMont Jones4cbecba2020-05-12 11:54:27 -0600813 # TODO(crbug/1081828): Receive and use sysroot.
814 graph, _sdk_graph = dependency.GetBuildDependency(
Alex Kleind8cd4c62020-09-14 13:37:47 -0600815 build_target.root, build_target.name, pkgs)
Alex Klein36b117f2019-09-30 15:13:46 -0600816 return any(atom in package for package in graph['package_deps'])
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600817
818
Alex Klein6becabc2020-09-11 14:03:05 -0600819def needs_chrome_source(
820 build_target: 'build_target_lib.BuildTarget',
821 compile_source=False,
822 packages: Optional[List[package_info.PackageInfo]] = None,
823 useflags=None):
824 """Check if the chrome source is needed.
825
826 The chrome source is needed if the build target builds chrome or any of its
827 follower packages, and can't use a prebuilt for them either because it's not
828 available, or because we can't use prebuilts because it must build from
829 source.
830 """
831 cros_build_lib.AssertInsideChroot()
832
833 # Check if it builds chrome and/or a follower package.
Alex Kleina8052162021-02-03 14:28:22 -0700834 graph = depgraph.get_sysroot_dependency_graph(build_target.root, packages)
Alex Klein6becabc2020-09-11 14:03:05 -0600835 builds_chrome = constants.CHROME_CP in graph
836 builds_follower = {
837 pkg: pkg in graph for pkg in constants.OTHER_CHROME_PACKAGES
838 }
839
Alex Klein9ce3f682021-06-23 15:06:44 -0600840 local_uprev = builds_chrome and revbump_chrome([build_target])
841
Alex Klein6becabc2020-09-11 14:03:05 -0600842 # When we are compiling source set False since we do not use prebuilts.
843 # When not compiling from source, start with True, i.e. we have every prebuilt
844 # we've checked for up to this point.
845 has_chrome_prebuilt = not compile_source
846 has_follower_prebuilts = not compile_source
847 # Save packages that need prebuilts for reporting.
848 pkgs_needing_prebuilts = []
849 if compile_source:
850 # Need everything.
851 pkgs_needing_prebuilts.append(constants.CHROME_CP)
852 pkgs_needing_prebuilts.extend(
853 [pkg for pkg, builds_pkg in builds_follower.items() if builds_pkg])
854 else:
855 # Check chrome itself.
856 if builds_chrome:
857 has_chrome_prebuilt = has_prebuilt(
858 constants.CHROME_CP, build_target=build_target, useflags=useflags)
859 if not has_chrome_prebuilt:
860 pkgs_needing_prebuilts.append(constants.CHROME_CP)
861 # Check follower packages.
862 for pkg, builds_pkg in builds_follower.items():
863 if not builds_pkg:
864 continue
865 prebuilt = has_prebuilt(pkg, build_target=build_target, useflags=useflags)
866 has_follower_prebuilts &= prebuilt
867 if not prebuilt:
868 pkgs_needing_prebuilts.append(pkg)
869 # Postcondition: has_chrome_prebuilt and has_follower_prebuilts now correctly
870 # reflect whether we actually have the corresponding prebuilts for the build.
871
872 needs_chrome = builds_chrome and not has_chrome_prebuilt
873 needs_follower = any(builds_follower.values()) and not has_follower_prebuilts
874
875 return NeedsChromeSourceResult(
876 needs_chrome_source=needs_chrome or needs_follower,
877 builds_chrome=builds_chrome,
878 packages=[package_info.parse(p) for p in pkgs_needing_prebuilts],
879 missing_chrome_prebuilt=not has_chrome_prebuilt,
Alex Klein9ce3f682021-06-23 15:06:44 -0600880 missing_follower_prebuilt=not has_follower_prebuilts,
881 local_uprev=local_uprev,
882 )
Alex Klein6becabc2020-09-11 14:03:05 -0600883
884
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600885def determine_chrome_version(build_target):
Michael Mortensenc2615b72019-10-15 08:12:24 -0600886 """Returns the current Chrome version for the board (or in buildroot).
887
888 Args:
Alex Klein2960c752020-03-09 13:43:38 -0600889 build_target (build_target_lib.BuildTarget): The board build target.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700890
891 Returns:
892 str|None: The chrome version if available.
Michael Mortensenc2615b72019-10-15 08:12:24 -0600893 """
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600894 # TODO(crbug/1019770): Long term we should not need the try/catch here once
895 # the builds function above only returns True for chrome when
896 # determine_chrome_version will succeed.
897 try:
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700898 cpv = portage_util.PortageqBestVisible(
899 constants.CHROME_CP, build_target.name, cwd=constants.SOURCE_ROOT)
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600900 except cros_build_lib.RunCommandError as e:
901 # Return None because portage failed when trying to determine the chrome
902 # version.
903 logging.warning('Caught exception in determine_chrome_package: %s', e)
904 return None
Michael Mortensenc2615b72019-10-15 08:12:24 -0600905 # Something like 78.0.3877.4_rc -> 78.0.3877.4
906 return cpv.version_no_rev.partition('_')[0]
907
908
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600909def determine_android_package(board):
910 """Returns the active Android container package in use by the board.
911
912 Args:
913 board: The board name this is specific to.
Alex Kleinad6b48a2020-01-08 16:57:41 -0700914
915 Returns:
916 str|None: The android package string if there is one.
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600917 """
Michael Mortensene0f4b542019-10-24 15:30:23 -0600918 try:
919 packages = portage_util.GetPackageDependencies(board, 'virtual/target-os')
Michael Mortensene0f4b542019-10-24 15:30:23 -0600920 except cros_build_lib.RunCommandError as e:
921 # Return None because a command (likely portage) failed when trying to
922 # determine the package.
923 logging.warning('Caught exception in determine_android_package: %s', e)
924 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600925
Alex Kleinad6b48a2020-01-08 16:57:41 -0700926 # We assume there is only one Android package in the depgraph.
927 for package in packages:
Mike Frysingerfcca49e2021-03-17 01:09:20 -0400928 if (package.startswith('chromeos-base/android-container-') or
929 package.startswith('chromeos-base/android-vm-')):
Alex Kleinad6b48a2020-01-08 16:57:41 -0700930 return package
931 return None
932
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600933
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500934def determine_android_version(board, package=None):
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600935 """Determine the current Android version in buildroot now and return it.
936
937 This uses the typical portage logic to determine which version of Android
938 is active right now in the buildroot.
939
940 Args:
Mike Frysingerb53f1822021-03-05 00:44:50 -0500941 board: The board name this is specific to.
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500942 package: The Android package, if already computed.
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600943
944 Returns:
Mike Frysingerb53f1822021-03-05 00:44:50 -0500945 The Android build ID of the container for the board.
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600946
947 Raises:
948 NoAndroidVersionError: if no unique Android version can be determined.
949 """
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500950 if not package:
951 package = determine_android_package(board)
Mike Frysingerb53f1822021-03-05 00:44:50 -0500952 if not package:
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600953 return None
Mike Frysingerb53f1822021-03-05 00:44:50 -0500954 cpv = package_info.SplitCPV(package)
955 if not cpv:
956 raise NoAndroidVersionError(
957 'Android version could not be determined for %s' % board)
958 return cpv.version_no_rev
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600959
Alex Klein7a3a7dd2020-01-08 16:44:38 -0700960
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500961def determine_android_branch(board, package=None):
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600962 """Returns the Android branch in use by the active container ebuild."""
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500963 if not package:
964 package = determine_android_package(board)
965 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600966 return None
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500967 ebuild_path = portage_util.FindEbuildForBoardPackage(package, board)
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600968 # We assume all targets pull from the same branch and that we always
Federico 'Morg' Pareschicd9165a2020-05-29 09:45:55 +0900969 # have at least one of the following targets.
Shao-Chuan Lee73bba612020-06-17 11:47:04 +0900970 targets = constants.ANDROID_ALL_BUILD_TARGETS
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600971 ebuild_content = osutils.SourceEnvironment(ebuild_path, targets)
972 for target in targets:
973 if target in ebuild_content:
974 branch = re.search(r'(.*?)-linux-', ebuild_content[target])
975 if branch is not None:
976 return branch.group(1)
977 raise NoAndroidBranchError(
978 'Android branch could not be determined for %s (ebuild empty?)' % board)
979
980
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500981def determine_android_target(board, package=None):
Michael Mortensen14960d02019-10-18 07:53:59 -0600982 """Returns the Android target in use by the active container ebuild."""
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500983 if not package:
984 package = determine_android_package(board)
985 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600986 return None
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500987 if package.startswith('chromeos-base/android-vm-'):
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600988 return 'bertha'
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500989 elif package.startswith('chromeos-base/android-container-'):
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600990 return 'cheets'
991
992 raise NoAndroidTargetError(
993 'Android Target cannot be determined for the package: %s' %
Mike Frysinger8e1c99a2021-03-05 00:58:11 -0500994 package)
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600995
996
997def determine_platform_version():
998 """Returns the platform version from the source root."""
Michael Mortensen009cb662019-10-21 11:38:43 -0600999 # Platform version is something like '12575.0.0'.
Michael Mortensen9fdb14b2019-10-17 11:17:30 -06001000 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
1001 return version.VersionString()
Michael Mortensen009cb662019-10-21 11:38:43 -06001002
1003
1004def determine_milestone_version():
1005 """Returns the platform version from the source root."""
1006 # Milestone version is something like '79'.
1007 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
1008 return version.chrome_branch
1009
Alex Klein7a3a7dd2020-01-08 16:44:38 -07001010
Michael Mortensen009cb662019-10-21 11:38:43 -06001011def determine_full_version():
1012 """Returns the full version from the source root."""
1013 # Full version is something like 'R79-12575.0.0'.
1014 milestone_version = determine_milestone_version()
1015 platform_version = determine_platform_version()
1016 full_version = ('R%s-%s' % (milestone_version, platform_version))
1017 return full_version
Michael Mortensen71ef5682020-05-07 14:29:24 -06001018
1019
Michael Mortensende716a12020-05-15 11:27:00 -06001020def find_fingerprints(build_target):
1021 """Returns a list of fingerprints for this build.
1022
1023 Args:
1024 build_target (build_target_lib.BuildTarget): The build target.
1025
1026 Returns:
1027 list[str] - List of fingerprint strings.
1028 """
1029 cros_build_lib.AssertInsideChroot()
1030 fp_file = 'cheets-fingerprint.txt'
1031 fp_path = os.path.join(
1032 image_lib.GetLatestImageLink(build_target.name),
1033 fp_file)
1034 if not os.path.isfile(fp_path):
1035 logging.info('Fingerprint file not found: %s', fp_path)
Michael Mortensend81d81e2020-06-09 14:20:59 -06001036 return []
Michael Mortensende716a12020-05-15 11:27:00 -06001037 logging.info('Reading fingerprint file: %s', fp_path)
1038 fingerprints = osutils.ReadFile(fp_path).splitlines()
1039 return fingerprints
1040
1041
Michael Mortensen59e30872020-05-18 14:12:49 -06001042def get_all_firmware_versions(build_target):
1043 """Extract firmware version for all models present.
1044
1045 Args:
1046 build_target (build_target_lib.BuildTarget): The build target.
1047
1048 Returns:
1049 A dict of FirmwareVersions namedtuple instances by model.
1050 Each element will be populated based on whether it was present in the
1051 command output.
1052 """
1053 cros_build_lib.AssertInsideChroot()
1054 result = {}
1055 # Note that example output for _get_firmware_version_cmd_result is available
1056 # in the packages_unittest.py for testing get_all_firmware_versions.
1057 cmd_result = _get_firmware_version_cmd_result(build_target)
1058
1059 # There is a blank line between the version info for each model.
1060 firmware_version_payloads = cmd_result.split('\n\n')
1061 for firmware_version_payload in firmware_version_payloads:
1062 if 'BIOS' in firmware_version_payload:
1063 firmware_version = _find_firmware_versions(firmware_version_payload)
1064 result[firmware_version.model] = firmware_version
1065 return result
1066
1067
Michael Mortensen71ef5682020-05-07 14:29:24 -06001068FirmwareVersions = collections.namedtuple(
1069 'FirmwareVersions', ['model', 'main', 'main_rw', 'ec', 'ec_rw'])
1070
1071
1072def get_firmware_versions(build_target):
1073 """Extract version information from the firmware updater, if one exists.
1074
1075 Args:
1076 build_target (build_target_lib.BuildTarget): The build target.
1077
1078 Returns:
1079 A FirmwareVersions namedtuple instance.
1080 Each element will either be set to the string output by the firmware
1081 updater shellball, or None if there is no firmware updater.
1082 """
1083 cros_build_lib.AssertInsideChroot()
1084 cmd_result = _get_firmware_version_cmd_result(build_target)
1085 if cmd_result:
1086 return _find_firmware_versions(cmd_result)
1087 else:
1088 return FirmwareVersions(None, None, None, None, None)
1089
1090
1091def _get_firmware_version_cmd_result(build_target):
1092 """Gets the raw result output of the firmware updater version command.
1093
1094 Args:
1095 build_target (build_target_lib.BuildTarget): The build target.
1096
1097 Returns:
1098 Command execution result.
1099 """
1100 updater = os.path.join(build_target.root,
1101 'usr/sbin/chromeos-firmwareupdate')
1102 logging.info('Calling updater %s', updater)
1103 # Call the updater using the chroot-based path.
1104 return cros_build_lib.run([updater, '-V'],
1105 capture_output=True, log_output=True,
1106 encoding='utf-8').stdout
1107
1108
1109def _find_firmware_versions(cmd_output):
1110 """Finds firmware version output via regex matches against the cmd_output.
1111
1112 Args:
1113 cmd_output: The raw output to search against.
1114
1115 Returns:
1116 FirmwareVersions namedtuple with results.
1117 Each element will either be set to the string output by the firmware
1118 updater shellball, or None if there is no match.
1119 """
1120
1121 # Sometimes a firmware bundle includes a special combination of RO+RW
1122 # firmware. In this case, the RW firmware version is indicated with a "(RW)
1123 # version" field. In other cases, the "(RW) version" field is not present.
1124 # Therefore, search for the "(RW)" fields first and if they aren't present,
1125 # fallback to the other format. e.g. just "BIOS version:".
1126 # TODO(mmortensen): Use JSON once the firmware updater supports it.
1127 main = None
1128 main_rw = None
1129 ec = None
1130 ec_rw = None
1131 model = None
1132
1133 match = re.search(r'BIOS version:\s*(?P<version>.*)', cmd_output)
1134 if match:
1135 main = match.group('version')
1136
1137 match = re.search(r'BIOS \(RW\) version:\s*(?P<version>.*)', cmd_output)
1138 if match:
1139 main_rw = match.group('version')
1140
1141 match = re.search(r'EC version:\s*(?P<version>.*)', cmd_output)
1142 if match:
1143 ec = match.group('version')
1144
1145 match = re.search(r'EC \(RW\) version:\s*(?P<version>.*)', cmd_output)
1146 if match:
1147 ec_rw = match.group('version')
1148
1149 match = re.search(r'Model:\s*(?P<model>.*)', cmd_output)
1150 if match:
1151 model = match.group('model')
1152
1153 return FirmwareVersions(model, main, main_rw, ec, ec_rw)
Michael Mortensena4af79e2020-05-06 16:18:48 -06001154
1155
1156MainEcFirmwareVersions = collections.namedtuple(
1157 'MainEcFirmwareVersions', ['main_fw_version', 'ec_fw_version'])
1158
1159def determine_firmware_versions(build_target):
1160 """Returns a namedtuple with main and ec firmware versions.
1161
1162 Args:
1163 build_target (build_target_lib.BuildTarget): The build target.
1164
1165 Returns:
1166 MainEcFirmwareVersions namedtuple with results.
1167 """
1168 fw_versions = get_firmware_versions(build_target)
1169 main_fw_version = fw_versions.main_rw or fw_versions.main
1170 ec_fw_version = fw_versions.ec_rw or fw_versions.ec
1171
1172 return MainEcFirmwareVersions(main_fw_version, ec_fw_version)
Michael Mortensenfbf2b2d2020-05-14 16:33:06 -06001173
1174def determine_kernel_version(build_target):
1175 """Returns a string containing the kernel version for this build target.
1176
1177 Args:
1178 build_target (build_target_lib.BuildTarget): The build target.
1179
1180 Returns:
1181 (str) The kernel versions, or None.
1182 """
1183 try:
1184 packages = portage_util.GetPackageDependencies(build_target.name,
1185 'virtual/linux-sources')
1186 except cros_build_lib.RunCommandError as e:
1187 logging.warning('Unable to get package list for metadata: %s', e)
1188 return None
1189 for package in packages:
1190 if package.startswith('sys-kernel/chromeos-kernel-'):
Alex Klein18a60af2020-06-11 12:08:47 -06001191 kernel_version = package_info.SplitCPV(package).version
Michael Mortensenfbf2b2d2020-05-14 16:33:06 -06001192 logging.info('Found active kernel version: %s', kernel_version)
1193 return kernel_version
1194 return None
Michael Mortensen125bb012020-05-21 14:02:10 -06001195
1196
1197def get_models(build_target, log_output=True):
1198 """Obtain a list of models supported by a unified board.
1199
1200 This ignored whitelabel models since GoldenEye has no specific support for
1201 these at present.
1202
1203 Args:
1204 build_target (build_target_lib.BuildTarget): The build target.
1205 log_output: Whether to log the output of the cros_config_host invocation.
1206
1207 Returns:
1208 A list of models supported by this board, if it is a unified build; None,
1209 if it is not a unified build.
1210 """
1211 return _run_cros_config_host(build_target, ['list-models'],
1212 log_output=log_output)
1213
1214
Michael Mortensen359c1f32020-05-28 19:35:42 -06001215def get_key_id(build_target, model):
1216 """Obtain the key_id for a model within the build_target.
1217
1218 Args:
1219 build_target (build_target_lib.BuildTarget): The build target.
1220 model (str): The model name
1221
1222 Returns:
1223 A key_id (str) or None.
1224 """
1225 model_arg = '--model=' + model
1226 key_id_list = _run_cros_config_host(
1227 build_target,
1228 [model_arg, 'get', '/firmware-signing', 'key-id'])
1229 key_id = None
1230 if len(key_id_list) == 1:
1231 key_id = key_id_list[0]
1232 return key_id
1233
1234
Michael Mortensen125bb012020-05-21 14:02:10 -06001235def _run_cros_config_host(build_target, args, log_output=True):
1236 """Run the cros_config_host tool.
1237
1238 Args:
1239 build_target (build_target_lib.BuildTarget): The build target.
1240 args: List of arguments to pass.
1241 log_output: Whether to log the output of the cros_config_host.
1242
1243 Returns:
1244 Output of the tool
1245 """
1246 cros_build_lib.AssertInsideChroot()
1247 tool = '/usr/bin/cros_config_host'
1248 if not os.path.isfile(tool):
1249 return None
1250
1251 config_fname = build_target.full_path(
1252 'usr/share/chromeos-config/yaml/config.yaml')
1253
1254 result = cros_build_lib.run(
1255 [tool, '-c', config_fname] + args,
1256 capture_output=True,
1257 encoding='utf-8',
1258 log_output=log_output,
1259 check=False)
1260 if result.returncode:
1261 # Show the output for debugging purposes.
1262 if 'No such file or directory' not in result.error:
1263 logging.error('cros_config_host failed: %s\n', result.error)
1264 return None
1265 return result.output.strip().splitlines()