blob: 564a36302bde215363935e2cc938b250468f481a [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 Mortensenb70e8a82019-10-10 18:43:41 -060028from chromite.lib import osutils
Alex Kleineb77ffa2019-05-28 14:47:44 -060029from chromite.lib import portage_util
Andrew Lamb2bde9e42019-11-04 13:24:09 -070030from chromite.lib import replication_lib
Alex Kleind6195b62019-08-06 16:01:16 -060031from chromite.lib import uprev_lib
Alex Kleineb77ffa2019-05-28 14:47:44 -060032
Alex Klein36b117f2019-09-30 15:13:46 -060033if cros_build_lib.IsInsideChroot():
34 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
Yaakov Shaul1eafe832019-09-10 16:50:26 -060081class EbuildManifestError(Error):
82 """Error when running ebuild manifest."""
83
84
Andrew Lamb9563a152019-12-04 11:42:18 -070085class GeneratedCrosConfigFilesError(Error):
86 """Error when cros_config_schema does not produce expected files"""
87
88 def __init__(self, expected_files, found_files):
89 msg = ('Expected to find generated C files: %s. Actually found: %s' %
90 (expected_files, found_files))
91 super(GeneratedCrosConfigFilesError, self).__init__(msg)
92
Yaakov Shaul730814a2019-09-10 13:58:25 -060093UprevVersionedPackageModifications = collections.namedtuple(
94 'UprevVersionedPackageModifications', ('new_version', 'files'))
Alex Klein34afcbc2019-08-22 16:14:31 -060095
Yaakov Shaul730814a2019-09-10 13:58:25 -060096
97class UprevVersionedPackageResult(object):
98 """Data object for uprev_versioned_package."""
99
100 def __init__(self):
101 self.modified = []
102
103 def add_result(self, new_version, modified_files):
104 """Adds version/ebuilds tuple to result.
105
106 Args:
107 new_version: New version number of package.
108 modified_files: List of files modified for the given version.
109 """
110 result = UprevVersionedPackageModifications(new_version, modified_files)
111 self.modified.append(result)
112 return self
Alex Klein34afcbc2019-08-22 16:14:31 -0600113
114 @property
115 def uprevved(self):
Yaakov Shaul730814a2019-09-10 13:58:25 -0600116 return bool(self.modified)
Alex Klein34afcbc2019-08-22 16:14:31 -0600117
118
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600119def patch_ebuild_vars(ebuild_path, variables):
120 """Updates variables in ebuild.
121
122 Use this function rather than portage_util.EBuild.UpdateEBuild when you
123 want to preserve the variable position and quotes within the ebuild.
124
125 Args:
126 ebuild_path: The path of the ebuild.
127 variables: Dictionary of variables to update in ebuild.
128 """
129 try:
130 for line in fileinput.input(ebuild_path, inplace=1):
131 varname, eq, _ = line.partition('=')
132 if eq == '=' and varname.strip() in variables:
133 value = variables[varname]
134 sys.stdout.write('%s="%s"\n' % (varname, value))
135 else:
136 sys.stdout.write(line)
137 finally:
138 fileinput.close()
139
140
Alex Klein87531182019-08-12 15:23:37 -0600141def uprevs_versioned_package(package):
142 """Decorator to register package uprev handlers."""
143 assert package
144
145 def register(func):
146 """Registers |func| as a handler for |package|."""
147 _UPREV_FUNCS[package] = func
148
149 @functools.wraps(func)
150 def pass_through(*args, **kwargs):
151 return func(*args, **kwargs)
152
153 return pass_through
154
155 return register
156
157
Alex Klein4de25e82019-08-05 15:58:39 -0600158def uprev_android(tracking_branch, android_package, android_build_branch,
159 chroot, build_targets=None, android_version=None,
160 android_gts_build_branch=None):
161 """Returns the portage atom for the revved Android ebuild - see man emerge."""
162 command = ['cros_mark_android_as_stable',
163 '--tracking_branch=%s' % tracking_branch,
164 '--android_package=%s' % android_package,
165 '--android_build_branch=%s' % android_build_branch]
166 if build_targets:
167 command.append('--boards=%s' % ':'.join(bt.name for bt in build_targets))
168 if android_version:
169 command.append('--force_version=%s' % android_version)
170 if android_gts_build_branch:
171 command.append('--android_gts_build_branch=%s' % android_gts_build_branch)
172
Mike Frysinger45602c72019-09-22 02:15:11 -0400173 result = cros_build_lib.run(command, redirect_stdout=True,
174 enter_chroot=True,
175 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600176
177 android_atom = _parse_android_atom(result)
178 if not android_atom:
179 logging.info('Found nothing to rev.')
180 return None
181
182 for target in build_targets or []:
183 # Sanity check: We should always be able to merge the version of
184 # Android we just unmasked.
185 command = ['emerge-%s' % target.name, '-p', '--quiet',
186 '=%s' % android_atom]
187 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400188 cros_build_lib.run(command, enter_chroot=True,
189 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600190 except cros_build_lib.RunCommandError:
191 logging.error(
192 'Cannot emerge-%s =%s\nIs Android pinned to an older '
193 'version?', target, android_atom)
194 raise AndroidIsPinnedUprevError(android_atom)
195
196 return android_atom
197
198
199def _parse_android_atom(result):
200 """Helper to parse the atom from the cros_mark_android_as_stable output.
201
202 This function is largely just intended to make testing easier.
203
204 Args:
205 result (cros_build_lib.CommandResult): The cros_mark_android_as_stable
206 command result.
207 """
208 portage_atom_string = result.output.strip()
209
210 android_atom = None
211 if portage_atom_string:
212 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
213
214 return android_atom
215
216
Alex Kleineb77ffa2019-05-28 14:47:44 -0600217def uprev_build_targets(build_targets, overlay_type, chroot=None,
218 output_dir=None):
219 """Uprev the set provided build targets, or all if not specified.
220
221 Args:
222 build_targets (list[build_target_util.BuildTarget]|None): The build targets
223 whose overlays should be uprevved, empty or None for all.
224 overlay_type (str): One of the valid overlay types except None (see
225 constants.VALID_OVERLAYS).
226 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
227 output_dir (str|None): The path to optionally dump result files.
228 """
229 # Need a valid overlay, but exclude None.
230 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
231
232 if build_targets:
233 overlays = portage_util.FindOverlaysForBoards(
234 overlay_type, boards=[t.name for t in build_targets])
235 else:
236 overlays = portage_util.FindOverlays(overlay_type)
237
238 return uprev_overlays(overlays, build_targets=build_targets, chroot=chroot,
239 output_dir=output_dir)
240
241
242def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
243 """Uprev the given overlays.
244
245 Args:
246 overlays (list[str]): The list of overlay paths.
247 build_targets (list[build_target_util.BuildTarget]|None): The build targets
248 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
249 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
250 output_dir (str|None): The path to optionally dump result files.
251
252 Returns:
253 list[str] - The paths to all of the modified ebuild files. This includes the
254 new files that were added (i.e. the new versions) and all of the removed
255 files (i.e. the old versions).
256 """
257 assert overlays
258
259 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
260
Alex Kleind6195b62019-08-06 16:01:16 -0600261 uprev_manager = uprev_lib.UprevOverlayManager(overlays, manifest,
262 build_targets=build_targets,
263 chroot=chroot,
264 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600265 uprev_manager.uprev()
266
267 return uprev_manager.modified_ebuilds
268
269
Alex Klein87531182019-08-12 15:23:37 -0600270def uprev_versioned_package(package, build_targets, refs, chroot):
271 """Call registered uprev handler function for the package.
272
273 Args:
274 package (portage_util.CPV): The package being uprevved.
275 build_targets (list[build_target_util.BuildTarget]): The build targets to
276 clean on a successful uprev.
277 refs (list[uprev_lib.GitRef]):
278 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
279
280 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600281 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600282 """
283 assert package
284
285 if package.cp not in _UPREV_FUNCS:
286 raise UnknownPackageError(
287 'Package "%s" does not have a registered handler.' % package.cp)
288
Andrew Lambea9a8a22019-12-12 14:03:43 -0700289 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
Alex Klein87531182019-08-12 15:23:37 -0600290
291
Evan Hernandezb51f1522019-08-15 11:29:40 -0600292# TODO(evanhernandez): Remove this. Only a quick hack for testing.
293@uprevs_versioned_package('sample/sample')
294def uprev_sample(*_args, **_kwargs):
295 """Mimics an uprev by changing files in sandbox repos.
296
297 See: uprev_versioned_package.
298 """
299 paths = [
300 os.path.join(constants.SOURCE_ROOT, 'infra/dummies', repo, 'sample.txt')
301 for repo in ('general-sandbox', 'merge-sandbox')
302 ]
303
Yaakov Shaul730814a2019-09-10 13:58:25 -0600304 return UprevVersionedPackageResult().add_result('1.2.3', paths)
Evan Hernandezb51f1522019-08-15 11:29:40 -0600305
306
Yaakov Shaul395ae832019-09-09 14:45:32 -0600307@uprevs_versioned_package('afdo/kernel-profiles')
308def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600309 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600310
311 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600312
313 Raises:
314 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600315 """
316 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
317 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
318
David Burger92485342019-09-10 17:52:45 -0600319 with open(path, 'r') as f:
320 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600321
Yaakov Shaul730814a2019-09-10 13:58:25 -0600322 result = UprevVersionedPackageResult()
Yaakov Shaul395ae832019-09-09 14:45:32 -0600323 for version, version_info in versions.items():
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600324 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
325 'sys-kernel', version)
326 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
327 '%s-9999.ebuild' % version)
Yaakov Shaula187b152019-09-11 12:41:32 -0600328 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
329 '%s-9999.ebuild' % version)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600330 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600331 patch_ebuild_vars(ebuild_path,
332 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600333
334 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600335 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400336 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600337 except cros_build_lib.RunCommandError as e:
338 raise EbuildManifestError(
339 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600340 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600341
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600342 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600343
Yaakov Shaul730814a2019-09-10 13:58:25 -0600344 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
345
346 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600347
348
Alex Klein87531182019-08-12 15:23:37 -0600349@uprevs_versioned_package(constants.CHROME_CP)
Andrew Lambea9a8a22019-12-12 14:03:43 -0700350def uprev_chrome(build_targets, refs, chroot):
Alex Klein87531182019-08-12 15:23:37 -0600351 """Uprev chrome and its related packages.
352
353 See: uprev_versioned_package.
354 """
355 # Determine the version from the refs (tags), i.e. the chrome versions are the
356 # tag names.
357 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
358
359 uprev_manager = uprev_lib.UprevChromeManager(
360 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600361 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600362 # Start with chrome itself, as we can't do anything else unless chrome
363 # uprevs successfully.
364 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600365 return result
Alex Klein87531182019-08-12 15:23:37 -0600366
367 # With a successful chrome rev, also uprev related packages.
368 for package in constants.OTHER_CHROME_PACKAGES:
369 uprev_manager.uprev(package)
370
David Burger37f48672019-09-18 17:07:56 -0600371 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600372
373
Andrew Lamb9563a152019-12-04 11:42:18 -0700374def _generate_platform_c_files(replication_config, chroot):
375 """Generates platform C files from a platform JSON payload.
376
377 Args:
378 replication_config (replication_config_pb2.ReplicationConfig): A
379 ReplicationConfig that has already been run. If it produced a
380 build_config.json file, that file will be used to generate platform C
381 files. Otherwise, nothing will be generated.
382 chroot (chroot_lib.Chroot): The chroot to use to generate.
383
384 Returns:
385 A list of generated files.
386 """
387 # Generate the platform C files from the build config. Note that it would be
388 # more intuitive to generate the platform C files from the platform config;
389 # however, cros_config_schema does not allow this, because the platform config
390 # payload is not always valid input. For example, if a property is both
391 # 'required' and 'build-only', it will fail schema validation. Thus, use the
392 # build config, and use '-f' to filter.
393 build_config_path = [
394 rule.destination_path
395 for rule in replication_config.file_replication_rules
396 if rule.destination_path.endswith('build_config.json')
397 ]
398
399 if not build_config_path:
400 logging.info(
401 'No build_config.json found, will not generate platform C files.'
402 ' Replication config: %s', replication_config)
403 return []
404
405 if len(build_config_path) > 1:
406 raise ValueError('Expected at most one build_config.json destination path.'
407 ' Replication config: %s' % replication_config)
408
409 build_config_path = build_config_path[0]
410
411 # Paths to the build_config.json and dir to output C files to, in the
412 # chroot.
413 build_config_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
414 build_config_path)
415 generated_output_chroot_dir = os.path.join(constants.CHROOT_SOURCE_ROOT,
416 os.path.dirname(build_config_path))
417
418 command = [
419 'cros_config_schema', '-m', build_config_chroot_path, '-g',
420 generated_output_chroot_dir, '-f', '"TRUE"'
421 ]
422
423 cros_build_lib.run(
424 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
425
426 # A relative (to the source root) path to the generated C files.
427 generated_output_dir = os.path.dirname(build_config_path)
428 generated_files = []
429 expected_c_files = ['config.c', 'ec_config.c', 'ec_config.h']
430 for f in expected_c_files:
431 if os.path.exists(
432 os.path.join(constants.SOURCE_ROOT, generated_output_dir, f)):
433 generated_files.append(os.path.join(generated_output_dir, f))
434
435 if len(expected_c_files) != len(generated_files):
436 raise GeneratedCrosConfigFilesError(expected_c_files, generated_files)
437
438 return generated_files
439
440
Andrew Lambe836f222019-12-09 12:27:38 -0700441def _get_private_overlay_package_root(ref, package):
442 """Returns the absolute path to the root of a given private overlay.
443
444 Args:
445 ref (uprev_lib.GitRef): GitRef for the private overlay.
446 package (str): Path to the package in the overlay.
447 """
448 # There might be a cleaner way to map from package -> path within the source
449 # tree. For now, just use string patterns.
450 private_overlay_ref_pattern = r'chromeos\/overlays\/overlay-([\w-]+)-private'
451 match = re.match(private_overlay_ref_pattern, ref.path)
452 if not match:
453 raise ValueError('ref.path must match the pattern: %s. Actual ref: %s' %
454 (private_overlay_ref_pattern, ref))
455
456 overlay = match.group(1)
457
458 return os.path.join(constants.SOURCE_ROOT,
459 'src/private-overlays/overlay-%s-private' % overlay,
460 package)
461
462
Andrew Lambea9a8a22019-12-12 14:03:43 -0700463@uprevs_versioned_package('chromeos-base/chromeos-config-bsp')
464def replicate_private_config(_build_targets, refs, chroot):
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700465 """Replicate a private cros_config change to the corresponding public config.
466
467 See uprev_versioned_package for args
468 """
Andrew Lambea9a8a22019-12-12 14:03:43 -0700469 package = 'chromeos-base/chromeos-config-bsp'
470
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700471 if len(refs) != 1:
472 raise ValueError('Expected exactly one ref, actual %s' % refs)
473
474 # Expect a replication_config.jsonpb in the package root.
Andrew Lambe836f222019-12-09 12:27:38 -0700475 package_root = _get_private_overlay_package_root(refs[0], package)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700476 replication_config_path = os.path.join(package_root,
477 'replication_config.jsonpb')
478
479 try:
480 replication_config = json_format.Parse(
481 osutils.ReadFile(replication_config_path),
482 replication_config_pb2.ReplicationConfig())
483 except IOError:
484 raise ValueError('Expected ReplicationConfig missing at %s' %
485 replication_config_path)
486
487 replication_lib.Replicate(replication_config)
488
489 modified_files = [
490 rule.destination_path
491 for rule in replication_config.file_replication_rules
492 ]
493
Andrew Lamb9563a152019-12-04 11:42:18 -0700494 # The generated platform C files are not easily filtered by replication rules,
495 # i.e. JSON / proto filtering can be described by a FieldMask, arbitrary C
496 # files cannot. Therefore, replicate and filter the JSON payloads, and then
497 # generate filtered C files from the JSON payload.
498 modified_files.extend(_generate_platform_c_files(replication_config, chroot))
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700499
500 # Use the private repo's commit hash as the new version.
501 new_private_version = refs[0].revision
502
Andrew Lamb988f4da2019-12-10 10:16:43 -0700503 # modified_files should contain only relative paths at this point, but the
504 # returned UprevVersionedPackageResult must contain only absolute paths.
505 for i, modified_file in enumerate(modified_files):
506 assert not os.path.isabs(modified_file)
507 modified_files[i] = os.path.join(constants.SOURCE_ROOT, modified_file)
508
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700509 return UprevVersionedPackageResult().add_result(new_private_version,
510 modified_files)
511
512
Alex Kleinbbef2b32019-08-27 10:38:50 -0600513def get_best_visible(atom, build_target=None):
514 """Returns the best visible CPV for the given atom.
515
516 Args:
517 atom (str): The atom to look up.
518 build_target (build_target_util.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600519 sysroot should be searched, or the SDK if not provided.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600520 """
David Burger1e0fe232019-07-01 14:52:07 -0600521 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600522
523 board = build_target.name if build_target else None
524 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600525
526
Alex Klein149fd3b2019-12-16 16:01:05 -0700527def has_prebuilt(atom, build_target=None, useflags=None):
Alex Kleinda39c6d2019-09-16 14:36:36 -0600528 """Check if a prebuilt exists.
529
530 Args:
531 atom (str): The package whose prebuilt is being queried.
532 build_target (build_target_util.BuildTarget): The build target whose
533 sysroot should be searched, or the SDK if not provided.
Alex Klein149fd3b2019-12-16 16:01:05 -0700534 useflags: Any additional USE flags that should be set. May be a string
535 of properly formatted USE flags, or an iterable of individual flags.
Alex Kleinda39c6d2019-09-16 14:36:36 -0600536 """
537 assert atom
538
539 board = build_target.name if build_target else None
Alex Klein149fd3b2019-12-16 16:01:05 -0700540 extra_env = None
541 if useflags:
542 new_flags = useflags
543 if not isinstance(useflags, six.string_types):
544 new_flags = ' '.join(useflags)
545
546 existing = os.environ.get('USE', '')
547 final_flags = '%s %s' % (existing, new_flags)
548 extra_env = {'USE': final_flags.strip()}
549 return portage_util.HasPrebuilt(atom, board=board, extra_env=extra_env)
Alex Klein36b117f2019-09-30 15:13:46 -0600550
551
David Burger0f9dd4e2019-10-08 12:33:42 -0600552def builds(atom, build_target, packages=None):
Alex Klein36b117f2019-09-30 15:13:46 -0600553 """Check if |build_target| builds |atom| (has it in its depgraph)."""
554 cros_build_lib.AssertInsideChroot()
555
Chris McDonalda22b74f2019-11-22 13:55:06 -0700556 graph, _sdk_graph = dependency.GetBuildDependency(build_target.name, packages)
Alex Klein36b117f2019-09-30 15:13:46 -0600557 return any(atom in package for package in graph['package_deps'])
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600558
559
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600560def determine_chrome_version(build_target):
Michael Mortensenc2615b72019-10-15 08:12:24 -0600561 """Returns the current Chrome version for the board (or in buildroot).
562
563 Args:
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600564 build_target (build_target_util.BuildTarget): The board build target.
Michael Mortensenc2615b72019-10-15 08:12:24 -0600565 """
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600566 # TODO(crbug/1019770): Long term we should not need the try/catch here once
567 # the builds function above only returns True for chrome when
568 # determine_chrome_version will succeed.
569 try:
570 cpv = portage_util.PortageqBestVisible(constants.CHROME_CP,
571 build_target.name,
572 cwd=constants.SOURCE_ROOT)
573 except cros_build_lib.RunCommandError as e:
574 # Return None because portage failed when trying to determine the chrome
575 # version.
576 logging.warning('Caught exception in determine_chrome_package: %s', e)
577 return None
Michael Mortensenc2615b72019-10-15 08:12:24 -0600578 # Something like 78.0.3877.4_rc -> 78.0.3877.4
579 return cpv.version_no_rev.partition('_')[0]
580
581
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600582def determine_android_package(board):
583 """Returns the active Android container package in use by the board.
584
585 Args:
586 board: The board name this is specific to.
587 """
Michael Mortensene0f4b542019-10-24 15:30:23 -0600588 try:
589 packages = portage_util.GetPackageDependencies(board, 'virtual/target-os')
590 # We assume there is only one Android package in the depgraph.
591 for package in packages:
592 if package.startswith('chromeos-base/android-container-') or \
593 package.startswith('chromeos-base/android-vm-'):
594 return package
595 return None
596 except cros_build_lib.RunCommandError as e:
597 # Return None because a command (likely portage) failed when trying to
598 # determine the package.
599 logging.warning('Caught exception in determine_android_package: %s', e)
600 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600601
602
603def determine_android_version(boards=None):
604 """Determine the current Android version in buildroot now and return it.
605
606 This uses the typical portage logic to determine which version of Android
607 is active right now in the buildroot.
608
609 Args:
610 boards: List of boards to check version of.
611
612 Returns:
613 The Android build ID of the container for the boards.
614
615 Raises:
616 NoAndroidVersionError: if no unique Android version can be determined.
617 """
618 if not boards:
619 return None
620 # Verify that all boards have the same version.
621 version = None
622 for board in boards:
623 package = determine_android_package(board)
624 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600625 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600626 cpv = portage_util.SplitCPV(package)
627 if not cpv:
628 raise NoAndroidVersionError(
629 'Android version could not be determined for %s' % board)
630 if not version:
631 version = cpv.version_no_rev
632 elif version != cpv.version_no_rev:
633 raise NoAndroidVersionError(
634 'Different Android versions (%s vs %s) for %s' %
635 (version, cpv.version_no_rev, boards))
636 return version
637
638def determine_android_branch(board):
639 """Returns the Android branch in use by the active container ebuild."""
640 try:
641 android_package = determine_android_package(board)
642 except cros_build_lib.RunCommandError:
643 raise NoAndroidBranchError(
644 'Android branch could not be determined for %s' % board)
645 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600646 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600647 ebuild_path = portage_util.FindEbuildForBoardPackage(android_package, board)
648 # We assume all targets pull from the same branch and that we always
649 # have an ARM_TARGET, ARM_USERDEBUG_TARGET, or an X86_USERDEBUG_TARGET.
650 targets = ['ARM_TARGET', 'ARM_USERDEBUG_TARGET', 'X86_USERDEBUG_TARGET']
651 ebuild_content = osutils.SourceEnvironment(ebuild_path, targets)
652 for target in targets:
653 if target in ebuild_content:
654 branch = re.search(r'(.*?)-linux-', ebuild_content[target])
655 if branch is not None:
656 return branch.group(1)
657 raise NoAndroidBranchError(
658 'Android branch could not be determined for %s (ebuild empty?)' % board)
659
660
661def determine_android_target(board):
Michael Mortensen14960d02019-10-18 07:53:59 -0600662 """Returns the Android target in use by the active container ebuild."""
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600663 try:
664 android_package = determine_android_package(board)
665 except cros_build_lib.RunCommandError:
666 raise NoAndroidTargetError(
667 'Android Target could not be determined for %s' % board)
668 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600669 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600670 if android_package.startswith('chromeos-base/android-vm-'):
671 return 'bertha'
672 elif android_package.startswith('chromeos-base/android-container-'):
673 return 'cheets'
674
675 raise NoAndroidTargetError(
676 'Android Target cannot be determined for the package: %s' %
677 android_package)
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600678
679
680def determine_platform_version():
681 """Returns the platform version from the source root."""
Michael Mortensen009cb662019-10-21 11:38:43 -0600682 # Platform version is something like '12575.0.0'.
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600683 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
684 return version.VersionString()
Michael Mortensen009cb662019-10-21 11:38:43 -0600685
686
687def determine_milestone_version():
688 """Returns the platform version from the source root."""
689 # Milestone version is something like '79'.
690 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
691 return version.chrome_branch
692
693def determine_full_version():
694 """Returns the full version from the source root."""
695 # Full version is something like 'R79-12575.0.0'.
696 milestone_version = determine_milestone_version()
697 platform_version = determine_platform_version()
698 full_version = ('R%s-%s' % (milestone_version, platform_version))
699 return full_version