blob: 83699c8772cfda1afbf638beebcd1395f0ed4dbb [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
Andrew Lamb2bde9e42019-11-04 13:24:09 -070018from google.protobuf import json_format
Yaakov Shaul730814a2019-09-10 13:58:25 -060019
Andrew Lamb2bde9e42019-11-04 13:24:09 -070020from chromite.api.gen.config import replication_config_pb2
Michael Mortensen9fdb14b2019-10-17 11:17:30 -060021from chromite.cbuildbot import manifest_version
Alex Kleineb77ffa2019-05-28 14:47:44 -060022from chromite.lib import constants
Evan Hernandezb51f1522019-08-15 11:29:40 -060023from chromite.lib import cros_build_lib
Alex Klein4de25e82019-08-05 15:58:39 -060024from chromite.lib import cros_logging as logging
Alex Kleineb77ffa2019-05-28 14:47:44 -060025from chromite.lib import git
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 Kleineb77ffa2019-05-28 14:47:44 -060030
Alex Klein36b117f2019-09-30 15:13:46 -060031if cros_build_lib.IsInsideChroot():
32 from chromite.service import dependency
33
Alex Klein87531182019-08-12 15:23:37 -060034# Registered handlers for uprevving versioned packages.
35_UPREV_FUNCS = {}
36
Alex Kleineb77ffa2019-05-28 14:47:44 -060037
38class Error(Exception):
39 """Module's base error class."""
40
41
Alex Klein4de25e82019-08-05 15:58:39 -060042class UnknownPackageError(Error):
43 """Uprev attempted for a package without a registered handler."""
44
45
Alex Kleineb77ffa2019-05-28 14:47:44 -060046class UprevError(Error):
47 """An error occurred while uprevving packages."""
48
49
Michael Mortensenb70e8a82019-10-10 18:43:41 -060050class NoAndroidVersionError(Error):
51 """An error occurred while trying to determine the android version."""
52
53
54class NoAndroidBranchError(Error):
55 """An error occurred while trying to determine the android branch."""
56
57
58class NoAndroidTargetError(Error):
59 """An error occurred while trying to determine the android target."""
60
61
Alex Klein4de25e82019-08-05 15:58:39 -060062class AndroidIsPinnedUprevError(UprevError):
63 """Raised when we try to uprev while Android is pinned."""
64
65 def __init__(self, new_android_atom):
66 """Initialize a AndroidIsPinnedUprevError.
67
68 Args:
69 new_android_atom: The Android atom that we failed to
70 uprev to, due to Android being pinned.
71 """
72 assert new_android_atom
73 msg = ('Failed up uprev to Android version %s as Android was pinned.' %
74 new_android_atom)
75 super(AndroidIsPinnedUprevError, self).__init__(msg)
76 self.new_android_atom = new_android_atom
Alex Klein87531182019-08-12 15:23:37 -060077
78
Yaakov Shaul1eafe832019-09-10 16:50:26 -060079class EbuildManifestError(Error):
80 """Error when running ebuild manifest."""
81
82
Andrew Lamb9563a152019-12-04 11:42:18 -070083class GeneratedCrosConfigFilesError(Error):
84 """Error when cros_config_schema does not produce expected files"""
85
86 def __init__(self, expected_files, found_files):
87 msg = ('Expected to find generated C files: %s. Actually found: %s' %
88 (expected_files, found_files))
89 super(GeneratedCrosConfigFilesError, self).__init__(msg)
90
Yaakov Shaul730814a2019-09-10 13:58:25 -060091UprevVersionedPackageModifications = collections.namedtuple(
92 'UprevVersionedPackageModifications', ('new_version', 'files'))
Alex Klein34afcbc2019-08-22 16:14:31 -060093
Yaakov Shaul730814a2019-09-10 13:58:25 -060094
95class UprevVersionedPackageResult(object):
96 """Data object for uprev_versioned_package."""
97
98 def __init__(self):
99 self.modified = []
100
101 def add_result(self, new_version, modified_files):
102 """Adds version/ebuilds tuple to result.
103
104 Args:
105 new_version: New version number of package.
106 modified_files: List of files modified for the given version.
107 """
108 result = UprevVersionedPackageModifications(new_version, modified_files)
109 self.modified.append(result)
110 return self
Alex Klein34afcbc2019-08-22 16:14:31 -0600111
112 @property
113 def uprevved(self):
Yaakov Shaul730814a2019-09-10 13:58:25 -0600114 return bool(self.modified)
Alex Klein34afcbc2019-08-22 16:14:31 -0600115
116
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600117def patch_ebuild_vars(ebuild_path, variables):
118 """Updates variables in ebuild.
119
120 Use this function rather than portage_util.EBuild.UpdateEBuild when you
121 want to preserve the variable position and quotes within the ebuild.
122
123 Args:
124 ebuild_path: The path of the ebuild.
125 variables: Dictionary of variables to update in ebuild.
126 """
127 try:
128 for line in fileinput.input(ebuild_path, inplace=1):
129 varname, eq, _ = line.partition('=')
130 if eq == '=' and varname.strip() in variables:
131 value = variables[varname]
132 sys.stdout.write('%s="%s"\n' % (varname, value))
133 else:
134 sys.stdout.write(line)
135 finally:
136 fileinput.close()
137
138
Alex Klein87531182019-08-12 15:23:37 -0600139def uprevs_versioned_package(package):
140 """Decorator to register package uprev handlers."""
141 assert package
142
143 def register(func):
144 """Registers |func| as a handler for |package|."""
145 _UPREV_FUNCS[package] = func
146
147 @functools.wraps(func)
148 def pass_through(*args, **kwargs):
149 return func(*args, **kwargs)
150
151 return pass_through
152
153 return register
154
155
Alex Klein4de25e82019-08-05 15:58:39 -0600156def uprev_android(tracking_branch, android_package, android_build_branch,
157 chroot, build_targets=None, android_version=None,
158 android_gts_build_branch=None):
159 """Returns the portage atom for the revved Android ebuild - see man emerge."""
160 command = ['cros_mark_android_as_stable',
161 '--tracking_branch=%s' % tracking_branch,
162 '--android_package=%s' % android_package,
163 '--android_build_branch=%s' % android_build_branch]
164 if build_targets:
165 command.append('--boards=%s' % ':'.join(bt.name for bt in build_targets))
166 if android_version:
167 command.append('--force_version=%s' % android_version)
168 if android_gts_build_branch:
169 command.append('--android_gts_build_branch=%s' % android_gts_build_branch)
170
Mike Frysinger45602c72019-09-22 02:15:11 -0400171 result = cros_build_lib.run(command, redirect_stdout=True,
172 enter_chroot=True,
173 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600174
175 android_atom = _parse_android_atom(result)
176 if not android_atom:
177 logging.info('Found nothing to rev.')
178 return None
179
180 for target in build_targets or []:
181 # Sanity check: We should always be able to merge the version of
182 # Android we just unmasked.
183 command = ['emerge-%s' % target.name, '-p', '--quiet',
184 '=%s' % android_atom]
185 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400186 cros_build_lib.run(command, enter_chroot=True,
187 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600188 except cros_build_lib.RunCommandError:
189 logging.error(
190 'Cannot emerge-%s =%s\nIs Android pinned to an older '
191 'version?', target, android_atom)
192 raise AndroidIsPinnedUprevError(android_atom)
193
194 return android_atom
195
196
197def _parse_android_atom(result):
198 """Helper to parse the atom from the cros_mark_android_as_stable output.
199
200 This function is largely just intended to make testing easier.
201
202 Args:
203 result (cros_build_lib.CommandResult): The cros_mark_android_as_stable
204 command result.
205 """
206 portage_atom_string = result.output.strip()
207
208 android_atom = None
209 if portage_atom_string:
210 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
211
212 return android_atom
213
214
Alex Kleineb77ffa2019-05-28 14:47:44 -0600215def uprev_build_targets(build_targets, overlay_type, chroot=None,
216 output_dir=None):
217 """Uprev the set provided build targets, or all if not specified.
218
219 Args:
220 build_targets (list[build_target_util.BuildTarget]|None): The build targets
221 whose overlays should be uprevved, empty or None for all.
222 overlay_type (str): One of the valid overlay types except None (see
223 constants.VALID_OVERLAYS).
224 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
225 output_dir (str|None): The path to optionally dump result files.
226 """
227 # Need a valid overlay, but exclude None.
228 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
229
230 if build_targets:
231 overlays = portage_util.FindOverlaysForBoards(
232 overlay_type, boards=[t.name for t in build_targets])
233 else:
234 overlays = portage_util.FindOverlays(overlay_type)
235
236 return uprev_overlays(overlays, build_targets=build_targets, chroot=chroot,
237 output_dir=output_dir)
238
239
240def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
241 """Uprev the given overlays.
242
243 Args:
244 overlays (list[str]): The list of overlay paths.
245 build_targets (list[build_target_util.BuildTarget]|None): The build targets
246 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
247 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
248 output_dir (str|None): The path to optionally dump result files.
249
250 Returns:
251 list[str] - The paths to all of the modified ebuild files. This includes the
252 new files that were added (i.e. the new versions) and all of the removed
253 files (i.e. the old versions).
254 """
255 assert overlays
256
257 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
258
Alex Kleind6195b62019-08-06 16:01:16 -0600259 uprev_manager = uprev_lib.UprevOverlayManager(overlays, manifest,
260 build_targets=build_targets,
261 chroot=chroot,
262 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600263 uprev_manager.uprev()
264
265 return uprev_manager.modified_ebuilds
266
267
Alex Klein87531182019-08-12 15:23:37 -0600268def uprev_versioned_package(package, build_targets, refs, chroot):
269 """Call registered uprev handler function for the package.
270
271 Args:
272 package (portage_util.CPV): The package being uprevved.
273 build_targets (list[build_target_util.BuildTarget]): The build targets to
274 clean on a successful uprev.
275 refs (list[uprev_lib.GitRef]):
276 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
277
278 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600279 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600280 """
281 assert package
282
283 if package.cp not in _UPREV_FUNCS:
284 raise UnknownPackageError(
285 'Package "%s" does not have a registered handler.' % package.cp)
286
Andrew Lambea9a8a22019-12-12 14:03:43 -0700287 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
Alex Klein87531182019-08-12 15:23:37 -0600288
289
Evan Hernandezb51f1522019-08-15 11:29:40 -0600290# TODO(evanhernandez): Remove this. Only a quick hack for testing.
291@uprevs_versioned_package('sample/sample')
292def uprev_sample(*_args, **_kwargs):
293 """Mimics an uprev by changing files in sandbox repos.
294
295 See: uprev_versioned_package.
296 """
297 paths = [
298 os.path.join(constants.SOURCE_ROOT, 'infra/dummies', repo, 'sample.txt')
299 for repo in ('general-sandbox', 'merge-sandbox')
300 ]
301
Yaakov Shaul730814a2019-09-10 13:58:25 -0600302 return UprevVersionedPackageResult().add_result('1.2.3', paths)
Evan Hernandezb51f1522019-08-15 11:29:40 -0600303
304
Yaakov Shaul395ae832019-09-09 14:45:32 -0600305@uprevs_versioned_package('afdo/kernel-profiles')
306def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600307 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600308
309 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600310
311 Raises:
312 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600313 """
314 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
315 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
316
David Burger92485342019-09-10 17:52:45 -0600317 with open(path, 'r') as f:
318 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600319
Yaakov Shaul730814a2019-09-10 13:58:25 -0600320 result = UprevVersionedPackageResult()
Yaakov Shaul395ae832019-09-09 14:45:32 -0600321 for version, version_info in versions.items():
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600322 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
323 'sys-kernel', version)
324 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
325 '%s-9999.ebuild' % version)
Yaakov Shaula187b152019-09-11 12:41:32 -0600326 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
327 '%s-9999.ebuild' % version)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600328 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600329 patch_ebuild_vars(ebuild_path,
330 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600331
332 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600333 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400334 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600335 except cros_build_lib.RunCommandError as e:
336 raise EbuildManifestError(
337 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600338 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600339
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600340 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600341
Yaakov Shaul730814a2019-09-10 13:58:25 -0600342 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
343
344 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600345
346
Alex Klein87531182019-08-12 15:23:37 -0600347@uprevs_versioned_package(constants.CHROME_CP)
Andrew Lambea9a8a22019-12-12 14:03:43 -0700348def uprev_chrome(build_targets, refs, chroot):
Alex Klein87531182019-08-12 15:23:37 -0600349 """Uprev chrome and its related packages.
350
351 See: uprev_versioned_package.
352 """
353 # Determine the version from the refs (tags), i.e. the chrome versions are the
354 # tag names.
355 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
356
357 uprev_manager = uprev_lib.UprevChromeManager(
358 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600359 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600360 # Start with chrome itself, as we can't do anything else unless chrome
361 # uprevs successfully.
362 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600363 return result
Alex Klein87531182019-08-12 15:23:37 -0600364
365 # With a successful chrome rev, also uprev related packages.
366 for package in constants.OTHER_CHROME_PACKAGES:
367 uprev_manager.uprev(package)
368
David Burger37f48672019-09-18 17:07:56 -0600369 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600370
371
Andrew Lamb9563a152019-12-04 11:42:18 -0700372def _generate_platform_c_files(replication_config, chroot):
373 """Generates platform C files from a platform JSON payload.
374
375 Args:
376 replication_config (replication_config_pb2.ReplicationConfig): A
377 ReplicationConfig that has already been run. If it produced a
378 build_config.json file, that file will be used to generate platform C
379 files. Otherwise, nothing will be generated.
380 chroot (chroot_lib.Chroot): The chroot to use to generate.
381
382 Returns:
383 A list of generated files.
384 """
385 # Generate the platform C files from the build config. Note that it would be
386 # more intuitive to generate the platform C files from the platform config;
387 # however, cros_config_schema does not allow this, because the platform config
388 # payload is not always valid input. For example, if a property is both
389 # 'required' and 'build-only', it will fail schema validation. Thus, use the
390 # build config, and use '-f' to filter.
391 build_config_path = [
392 rule.destination_path
393 for rule in replication_config.file_replication_rules
394 if rule.destination_path.endswith('build_config.json')
395 ]
396
397 if not build_config_path:
398 logging.info(
399 'No build_config.json found, will not generate platform C files.'
400 ' Replication config: %s', replication_config)
401 return []
402
403 if len(build_config_path) > 1:
404 raise ValueError('Expected at most one build_config.json destination path.'
405 ' Replication config: %s' % replication_config)
406
407 build_config_path = build_config_path[0]
408
409 # Paths to the build_config.json and dir to output C files to, in the
410 # chroot.
411 build_config_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
412 build_config_path)
413 generated_output_chroot_dir = os.path.join(constants.CHROOT_SOURCE_ROOT,
414 os.path.dirname(build_config_path))
415
416 command = [
417 'cros_config_schema', '-m', build_config_chroot_path, '-g',
418 generated_output_chroot_dir, '-f', '"TRUE"'
419 ]
420
421 cros_build_lib.run(
422 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
423
424 # A relative (to the source root) path to the generated C files.
425 generated_output_dir = os.path.dirname(build_config_path)
426 generated_files = []
427 expected_c_files = ['config.c', 'ec_config.c', 'ec_config.h']
428 for f in expected_c_files:
429 if os.path.exists(
430 os.path.join(constants.SOURCE_ROOT, generated_output_dir, f)):
431 generated_files.append(os.path.join(generated_output_dir, f))
432
433 if len(expected_c_files) != len(generated_files):
434 raise GeneratedCrosConfigFilesError(expected_c_files, generated_files)
435
436 return generated_files
437
438
Andrew Lambe836f222019-12-09 12:27:38 -0700439def _get_private_overlay_package_root(ref, package):
440 """Returns the absolute path to the root of a given private overlay.
441
442 Args:
443 ref (uprev_lib.GitRef): GitRef for the private overlay.
444 package (str): Path to the package in the overlay.
445 """
446 # There might be a cleaner way to map from package -> path within the source
447 # tree. For now, just use string patterns.
448 private_overlay_ref_pattern = r'chromeos\/overlays\/overlay-([\w-]+)-private'
449 match = re.match(private_overlay_ref_pattern, ref.path)
450 if not match:
451 raise ValueError('ref.path must match the pattern: %s. Actual ref: %s' %
452 (private_overlay_ref_pattern, ref))
453
454 overlay = match.group(1)
455
456 return os.path.join(constants.SOURCE_ROOT,
457 'src/private-overlays/overlay-%s-private' % overlay,
458 package)
459
460
Andrew Lambea9a8a22019-12-12 14:03:43 -0700461@uprevs_versioned_package('chromeos-base/chromeos-config-bsp')
462def replicate_private_config(_build_targets, refs, chroot):
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700463 """Replicate a private cros_config change to the corresponding public config.
464
465 See uprev_versioned_package for args
466 """
Andrew Lambea9a8a22019-12-12 14:03:43 -0700467 package = 'chromeos-base/chromeos-config-bsp'
468
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700469 if len(refs) != 1:
470 raise ValueError('Expected exactly one ref, actual %s' % refs)
471
472 # Expect a replication_config.jsonpb in the package root.
Andrew Lambe836f222019-12-09 12:27:38 -0700473 package_root = _get_private_overlay_package_root(refs[0], package)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700474 replication_config_path = os.path.join(package_root,
475 'replication_config.jsonpb')
476
477 try:
478 replication_config = json_format.Parse(
479 osutils.ReadFile(replication_config_path),
480 replication_config_pb2.ReplicationConfig())
481 except IOError:
482 raise ValueError('Expected ReplicationConfig missing at %s' %
483 replication_config_path)
484
485 replication_lib.Replicate(replication_config)
486
487 modified_files = [
488 rule.destination_path
489 for rule in replication_config.file_replication_rules
490 ]
491
Andrew Lamb9563a152019-12-04 11:42:18 -0700492 # The generated platform C files are not easily filtered by replication rules,
493 # i.e. JSON / proto filtering can be described by a FieldMask, arbitrary C
494 # files cannot. Therefore, replicate and filter the JSON payloads, and then
495 # generate filtered C files from the JSON payload.
496 modified_files.extend(_generate_platform_c_files(replication_config, chroot))
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700497
498 # Use the private repo's commit hash as the new version.
499 new_private_version = refs[0].revision
500
Andrew Lamb988f4da2019-12-10 10:16:43 -0700501 # modified_files should contain only relative paths at this point, but the
502 # returned UprevVersionedPackageResult must contain only absolute paths.
503 for i, modified_file in enumerate(modified_files):
504 assert not os.path.isabs(modified_file)
505 modified_files[i] = os.path.join(constants.SOURCE_ROOT, modified_file)
506
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700507 return UprevVersionedPackageResult().add_result(new_private_version,
508 modified_files)
509
510
Alex Kleinbbef2b32019-08-27 10:38:50 -0600511def get_best_visible(atom, build_target=None):
512 """Returns the best visible CPV for the given atom.
513
514 Args:
515 atom (str): The atom to look up.
516 build_target (build_target_util.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600517 sysroot should be searched, or the SDK if not provided.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600518 """
David Burger1e0fe232019-07-01 14:52:07 -0600519 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600520
521 board = build_target.name if build_target else None
522 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600523
524
525def has_prebuilt(atom, build_target=None):
526 """Check if a prebuilt exists.
527
528 Args:
529 atom (str): The package whose prebuilt is being queried.
530 build_target (build_target_util.BuildTarget): The build target whose
531 sysroot should be searched, or the SDK if not provided.
532 """
533 assert atom
534
535 board = build_target.name if build_target else None
536 return portage_util.HasPrebuilt(atom, board=board)
Alex Klein36b117f2019-09-30 15:13:46 -0600537
538
David Burger0f9dd4e2019-10-08 12:33:42 -0600539def builds(atom, build_target, packages=None):
Alex Klein36b117f2019-09-30 15:13:46 -0600540 """Check if |build_target| builds |atom| (has it in its depgraph)."""
541 cros_build_lib.AssertInsideChroot()
542
Chris McDonalda22b74f2019-11-22 13:55:06 -0700543 graph, _sdk_graph = dependency.GetBuildDependency(build_target.name, packages)
Alex Klein36b117f2019-09-30 15:13:46 -0600544 return any(atom in package for package in graph['package_deps'])
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600545
546
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600547def determine_chrome_version(build_target):
Michael Mortensenc2615b72019-10-15 08:12:24 -0600548 """Returns the current Chrome version for the board (or in buildroot).
549
550 Args:
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600551 build_target (build_target_util.BuildTarget): The board build target.
Michael Mortensenc2615b72019-10-15 08:12:24 -0600552 """
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600553 # TODO(crbug/1019770): Long term we should not need the try/catch here once
554 # the builds function above only returns True for chrome when
555 # determine_chrome_version will succeed.
556 try:
557 cpv = portage_util.PortageqBestVisible(constants.CHROME_CP,
558 build_target.name,
559 cwd=constants.SOURCE_ROOT)
560 except cros_build_lib.RunCommandError as e:
561 # Return None because portage failed when trying to determine the chrome
562 # version.
563 logging.warning('Caught exception in determine_chrome_package: %s', e)
564 return None
Michael Mortensenc2615b72019-10-15 08:12:24 -0600565 # Something like 78.0.3877.4_rc -> 78.0.3877.4
566 return cpv.version_no_rev.partition('_')[0]
567
568
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600569def determine_android_package(board):
570 """Returns the active Android container package in use by the board.
571
572 Args:
573 board: The board name this is specific to.
574 """
Michael Mortensene0f4b542019-10-24 15:30:23 -0600575 try:
576 packages = portage_util.GetPackageDependencies(board, 'virtual/target-os')
577 # We assume there is only one Android package in the depgraph.
578 for package in packages:
579 if package.startswith('chromeos-base/android-container-') or \
580 package.startswith('chromeos-base/android-vm-'):
581 return package
582 return None
583 except cros_build_lib.RunCommandError as e:
584 # Return None because a command (likely portage) failed when trying to
585 # determine the package.
586 logging.warning('Caught exception in determine_android_package: %s', e)
587 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600588
589
590def determine_android_version(boards=None):
591 """Determine the current Android version in buildroot now and return it.
592
593 This uses the typical portage logic to determine which version of Android
594 is active right now in the buildroot.
595
596 Args:
597 boards: List of boards to check version of.
598
599 Returns:
600 The Android build ID of the container for the boards.
601
602 Raises:
603 NoAndroidVersionError: if no unique Android version can be determined.
604 """
605 if not boards:
606 return None
607 # Verify that all boards have the same version.
608 version = None
609 for board in boards:
610 package = determine_android_package(board)
611 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600612 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600613 cpv = portage_util.SplitCPV(package)
614 if not cpv:
615 raise NoAndroidVersionError(
616 'Android version could not be determined for %s' % board)
617 if not version:
618 version = cpv.version_no_rev
619 elif version != cpv.version_no_rev:
620 raise NoAndroidVersionError(
621 'Different Android versions (%s vs %s) for %s' %
622 (version, cpv.version_no_rev, boards))
623 return version
624
625def determine_android_branch(board):
626 """Returns the Android branch in use by the active container ebuild."""
627 try:
628 android_package = determine_android_package(board)
629 except cros_build_lib.RunCommandError:
630 raise NoAndroidBranchError(
631 'Android branch could not be determined for %s' % board)
632 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600633 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600634 ebuild_path = portage_util.FindEbuildForBoardPackage(android_package, board)
635 # We assume all targets pull from the same branch and that we always
636 # have an ARM_TARGET, ARM_USERDEBUG_TARGET, or an X86_USERDEBUG_TARGET.
637 targets = ['ARM_TARGET', 'ARM_USERDEBUG_TARGET', 'X86_USERDEBUG_TARGET']
638 ebuild_content = osutils.SourceEnvironment(ebuild_path, targets)
639 for target in targets:
640 if target in ebuild_content:
641 branch = re.search(r'(.*?)-linux-', ebuild_content[target])
642 if branch is not None:
643 return branch.group(1)
644 raise NoAndroidBranchError(
645 'Android branch could not be determined for %s (ebuild empty?)' % board)
646
647
648def determine_android_target(board):
Michael Mortensen14960d02019-10-18 07:53:59 -0600649 """Returns the Android target in use by the active container ebuild."""
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600650 try:
651 android_package = determine_android_package(board)
652 except cros_build_lib.RunCommandError:
653 raise NoAndroidTargetError(
654 'Android Target could not be determined for %s' % board)
655 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600656 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600657 if android_package.startswith('chromeos-base/android-vm-'):
658 return 'bertha'
659 elif android_package.startswith('chromeos-base/android-container-'):
660 return 'cheets'
661
662 raise NoAndroidTargetError(
663 'Android Target cannot be determined for the package: %s' %
664 android_package)
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600665
666
667def determine_platform_version():
668 """Returns the platform version from the source root."""
Michael Mortensen009cb662019-10-21 11:38:43 -0600669 # Platform version is something like '12575.0.0'.
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600670 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
671 return version.VersionString()
Michael Mortensen009cb662019-10-21 11:38:43 -0600672
673
674def determine_milestone_version():
675 """Returns the platform version from the source root."""
676 # Milestone version is something like '79'.
677 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
678 return version.chrome_branch
679
680def determine_full_version():
681 """Returns the full version from the source root."""
682 # Full version is something like 'R79-12575.0.0'.
683 milestone_version = determine_milestone_version()
684 platform_version = determine_platform_version()
685 full_version = ('R%s-%s' % (milestone_version, platform_version))
686 return full_version