blob: 070eb880a2a019b4a2a0f08005c18a44658c2814 [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 Frysinger0282d222019-12-17 17:15:48 -0500173 result = cros_build_lib.run(command, stdout=True,
Mike Frysinger45602c72019-09-22 02:15:11 -0400174 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
Trent Begin315d9d92019-12-03 21:55:53 -0700349@uprevs_versioned_package('chromeos-base/chromeos-dtc-vm')
350def uprev_sludge(*_args, **_kwargs):
351 """Updates sludge VM - chromeos-base/chromeos-dtc-vm.
352
353 See: uprev_versioned_package.
354 """
355 package = 'chromeos-dtc-vm'
356 path = os.path.join('src', 'private-overlays', 'project-wilco-private',
357 'chromeos-base', package)
358 package_path = os.path.join(constants.SOURCE_ROOT, path)
359 version_pin_path = os.path.join(constants.SOURCE_ROOT, path, 'VERSION-PIN')
360
361 return uprev_ebuild_from_pin(package_path, version_pin_path)
362
363
364def uprev_ebuild_from_pin(package_path, version_pin_path):
365 """Changes the package ebuild's version to match the version pin file.
366
367 Args:
368 package_path: The path of the package relative to the src root. This path
369 should contain a single ebuild with the same name as the package.
370 version_pin_path: The path of the version_pin file that contains only only
371 a version string. The ebuild's version will be directly set to this
372 number.
373
374 Returns:
375 UprevVersionedPackageResult: The result.
376 """
377 package = os.path.basename(package_path)
378 ebuild_paths = list(portage_util.EBuild.List(package_path))
379 if not ebuild_paths:
380 raise UprevError('No ebuilds found for %s' % package)
381 elif len(ebuild_paths) > 1:
382 raise UprevError('Multiple ebuilds found for %s' % package)
383 else:
384 ebuild_path = ebuild_paths[0]
385
386 version = osutils.ReadFile(version_pin_path).strip()
387 new_ebuild_path = os.path.join(package_path,
388 '%s-%s-r1.ebuild' % (package, version))
389 os.rename(ebuild_path, new_ebuild_path)
390
391 result = UprevVersionedPackageResult()
392 result.add_result(version, [new_ebuild_path])
393 return result
394
395
Alex Klein87531182019-08-12 15:23:37 -0600396@uprevs_versioned_package(constants.CHROME_CP)
Andrew Lambea9a8a22019-12-12 14:03:43 -0700397def uprev_chrome(build_targets, refs, chroot):
Alex Klein87531182019-08-12 15:23:37 -0600398 """Uprev chrome and its related packages.
399
400 See: uprev_versioned_package.
401 """
402 # Determine the version from the refs (tags), i.e. the chrome versions are the
403 # tag names.
404 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
405
406 uprev_manager = uprev_lib.UprevChromeManager(
407 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600408 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600409 # Start with chrome itself, as we can't do anything else unless chrome
410 # uprevs successfully.
411 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600412 return result
Alex Klein87531182019-08-12 15:23:37 -0600413
414 # With a successful chrome rev, also uprev related packages.
415 for package in constants.OTHER_CHROME_PACKAGES:
416 uprev_manager.uprev(package)
417
David Burger37f48672019-09-18 17:07:56 -0600418 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600419
420
Andrew Lamb9563a152019-12-04 11:42:18 -0700421def _generate_platform_c_files(replication_config, chroot):
422 """Generates platform C files from a platform JSON payload.
423
424 Args:
425 replication_config (replication_config_pb2.ReplicationConfig): A
426 ReplicationConfig that has already been run. If it produced a
427 build_config.json file, that file will be used to generate platform C
428 files. Otherwise, nothing will be generated.
429 chroot (chroot_lib.Chroot): The chroot to use to generate.
430
431 Returns:
432 A list of generated files.
433 """
434 # Generate the platform C files from the build config. Note that it would be
435 # more intuitive to generate the platform C files from the platform config;
436 # however, cros_config_schema does not allow this, because the platform config
437 # payload is not always valid input. For example, if a property is both
438 # 'required' and 'build-only', it will fail schema validation. Thus, use the
439 # build config, and use '-f' to filter.
440 build_config_path = [
441 rule.destination_path
442 for rule in replication_config.file_replication_rules
443 if rule.destination_path.endswith('build_config.json')
444 ]
445
446 if not build_config_path:
447 logging.info(
448 'No build_config.json found, will not generate platform C files.'
449 ' Replication config: %s', replication_config)
450 return []
451
452 if len(build_config_path) > 1:
453 raise ValueError('Expected at most one build_config.json destination path.'
454 ' Replication config: %s' % replication_config)
455
456 build_config_path = build_config_path[0]
457
458 # Paths to the build_config.json and dir to output C files to, in the
459 # chroot.
460 build_config_chroot_path = os.path.join(constants.CHROOT_SOURCE_ROOT,
461 build_config_path)
462 generated_output_chroot_dir = os.path.join(constants.CHROOT_SOURCE_ROOT,
463 os.path.dirname(build_config_path))
464
465 command = [
466 'cros_config_schema', '-m', build_config_chroot_path, '-g',
467 generated_output_chroot_dir, '-f', '"TRUE"'
468 ]
469
470 cros_build_lib.run(
471 command, enter_chroot=True, chroot_args=chroot.get_enter_args())
472
473 # A relative (to the source root) path to the generated C files.
474 generated_output_dir = os.path.dirname(build_config_path)
475 generated_files = []
476 expected_c_files = ['config.c', 'ec_config.c', 'ec_config.h']
477 for f in expected_c_files:
478 if os.path.exists(
479 os.path.join(constants.SOURCE_ROOT, generated_output_dir, f)):
480 generated_files.append(os.path.join(generated_output_dir, f))
481
482 if len(expected_c_files) != len(generated_files):
483 raise GeneratedCrosConfigFilesError(expected_c_files, generated_files)
484
485 return generated_files
486
487
Andrew Lambe836f222019-12-09 12:27:38 -0700488def _get_private_overlay_package_root(ref, package):
489 """Returns the absolute path to the root of a given private overlay.
490
491 Args:
492 ref (uprev_lib.GitRef): GitRef for the private overlay.
493 package (str): Path to the package in the overlay.
494 """
495 # There might be a cleaner way to map from package -> path within the source
496 # tree. For now, just use string patterns.
497 private_overlay_ref_pattern = r'chromeos\/overlays\/overlay-([\w-]+)-private'
498 match = re.match(private_overlay_ref_pattern, ref.path)
499 if not match:
500 raise ValueError('ref.path must match the pattern: %s. Actual ref: %s' %
501 (private_overlay_ref_pattern, ref))
502
503 overlay = match.group(1)
504
505 return os.path.join(constants.SOURCE_ROOT,
506 'src/private-overlays/overlay-%s-private' % overlay,
507 package)
508
509
Andrew Lambea9a8a22019-12-12 14:03:43 -0700510@uprevs_versioned_package('chromeos-base/chromeos-config-bsp')
511def replicate_private_config(_build_targets, refs, chroot):
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700512 """Replicate a private cros_config change to the corresponding public config.
513
514 See uprev_versioned_package for args
515 """
Andrew Lambea9a8a22019-12-12 14:03:43 -0700516 package = 'chromeos-base/chromeos-config-bsp'
517
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700518 if len(refs) != 1:
519 raise ValueError('Expected exactly one ref, actual %s' % refs)
520
521 # Expect a replication_config.jsonpb in the package root.
Andrew Lambe836f222019-12-09 12:27:38 -0700522 package_root = _get_private_overlay_package_root(refs[0], package)
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700523 replication_config_path = os.path.join(package_root,
524 'replication_config.jsonpb')
525
526 try:
527 replication_config = json_format.Parse(
528 osutils.ReadFile(replication_config_path),
529 replication_config_pb2.ReplicationConfig())
530 except IOError:
531 raise ValueError('Expected ReplicationConfig missing at %s' %
532 replication_config_path)
533
534 replication_lib.Replicate(replication_config)
535
536 modified_files = [
537 rule.destination_path
538 for rule in replication_config.file_replication_rules
539 ]
540
Andrew Lamb9563a152019-12-04 11:42:18 -0700541 # The generated platform C files are not easily filtered by replication rules,
542 # i.e. JSON / proto filtering can be described by a FieldMask, arbitrary C
543 # files cannot. Therefore, replicate and filter the JSON payloads, and then
544 # generate filtered C files from the JSON payload.
545 modified_files.extend(_generate_platform_c_files(replication_config, chroot))
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700546
547 # Use the private repo's commit hash as the new version.
548 new_private_version = refs[0].revision
549
Andrew Lamb988f4da2019-12-10 10:16:43 -0700550 # modified_files should contain only relative paths at this point, but the
551 # returned UprevVersionedPackageResult must contain only absolute paths.
552 for i, modified_file in enumerate(modified_files):
553 assert not os.path.isabs(modified_file)
554 modified_files[i] = os.path.join(constants.SOURCE_ROOT, modified_file)
555
Andrew Lamb2bde9e42019-11-04 13:24:09 -0700556 return UprevVersionedPackageResult().add_result(new_private_version,
557 modified_files)
558
559
Alex Kleinbbef2b32019-08-27 10:38:50 -0600560def get_best_visible(atom, build_target=None):
561 """Returns the best visible CPV for the given atom.
562
563 Args:
564 atom (str): The atom to look up.
565 build_target (build_target_util.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600566 sysroot should be searched, or the SDK if not provided.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600567 """
David Burger1e0fe232019-07-01 14:52:07 -0600568 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600569
570 board = build_target.name if build_target else None
571 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600572
573
Alex Klein149fd3b2019-12-16 16:01:05 -0700574def has_prebuilt(atom, build_target=None, useflags=None):
Alex Kleinda39c6d2019-09-16 14:36:36 -0600575 """Check if a prebuilt exists.
576
577 Args:
578 atom (str): The package whose prebuilt is being queried.
579 build_target (build_target_util.BuildTarget): The build target whose
580 sysroot should be searched, or the SDK if not provided.
Alex Klein149fd3b2019-12-16 16:01:05 -0700581 useflags: Any additional USE flags that should be set. May be a string
582 of properly formatted USE flags, or an iterable of individual flags.
Alex Kleinda39c6d2019-09-16 14:36:36 -0600583 """
584 assert atom
585
586 board = build_target.name if build_target else None
Alex Klein149fd3b2019-12-16 16:01:05 -0700587 extra_env = None
588 if useflags:
589 new_flags = useflags
590 if not isinstance(useflags, six.string_types):
591 new_flags = ' '.join(useflags)
592
593 existing = os.environ.get('USE', '')
594 final_flags = '%s %s' % (existing, new_flags)
595 extra_env = {'USE': final_flags.strip()}
596 return portage_util.HasPrebuilt(atom, board=board, extra_env=extra_env)
Alex Klein36b117f2019-09-30 15:13:46 -0600597
598
David Burger0f9dd4e2019-10-08 12:33:42 -0600599def builds(atom, build_target, packages=None):
Alex Klein36b117f2019-09-30 15:13:46 -0600600 """Check if |build_target| builds |atom| (has it in its depgraph)."""
601 cros_build_lib.AssertInsideChroot()
602
Chris McDonalda22b74f2019-11-22 13:55:06 -0700603 graph, _sdk_graph = dependency.GetBuildDependency(build_target.name, packages)
Alex Klein36b117f2019-09-30 15:13:46 -0600604 return any(atom in package for package in graph['package_deps'])
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600605
606
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600607def determine_chrome_version(build_target):
Michael Mortensenc2615b72019-10-15 08:12:24 -0600608 """Returns the current Chrome version for the board (or in buildroot).
609
610 Args:
Michael Mortensenb51a1f02019-10-16 13:28:20 -0600611 build_target (build_target_util.BuildTarget): The board build target.
Michael Mortensenc2615b72019-10-15 08:12:24 -0600612 """
Michael Mortensen9fe740c2019-10-29 14:42:48 -0600613 # TODO(crbug/1019770): Long term we should not need the try/catch here once
614 # the builds function above only returns True for chrome when
615 # determine_chrome_version will succeed.
616 try:
617 cpv = portage_util.PortageqBestVisible(constants.CHROME_CP,
618 build_target.name,
619 cwd=constants.SOURCE_ROOT)
620 except cros_build_lib.RunCommandError as e:
621 # Return None because portage failed when trying to determine the chrome
622 # version.
623 logging.warning('Caught exception in determine_chrome_package: %s', e)
624 return None
Michael Mortensenc2615b72019-10-15 08:12:24 -0600625 # Something like 78.0.3877.4_rc -> 78.0.3877.4
626 return cpv.version_no_rev.partition('_')[0]
627
628
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600629def determine_android_package(board):
630 """Returns the active Android container package in use by the board.
631
632 Args:
633 board: The board name this is specific to.
634 """
Michael Mortensene0f4b542019-10-24 15:30:23 -0600635 try:
636 packages = portage_util.GetPackageDependencies(board, 'virtual/target-os')
637 # We assume there is only one Android package in the depgraph.
638 for package in packages:
639 if package.startswith('chromeos-base/android-container-') or \
640 package.startswith('chromeos-base/android-vm-'):
641 return package
642 return None
643 except cros_build_lib.RunCommandError as e:
644 # Return None because a command (likely portage) failed when trying to
645 # determine the package.
646 logging.warning('Caught exception in determine_android_package: %s', e)
647 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600648
649
650def determine_android_version(boards=None):
651 """Determine the current Android version in buildroot now and return it.
652
653 This uses the typical portage logic to determine which version of Android
654 is active right now in the buildroot.
655
656 Args:
657 boards: List of boards to check version of.
658
659 Returns:
660 The Android build ID of the container for the boards.
661
662 Raises:
663 NoAndroidVersionError: if no unique Android version can be determined.
664 """
665 if not boards:
666 return None
667 # Verify that all boards have the same version.
668 version = None
669 for board in boards:
670 package = determine_android_package(board)
671 if not package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600672 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600673 cpv = portage_util.SplitCPV(package)
674 if not cpv:
675 raise NoAndroidVersionError(
676 'Android version could not be determined for %s' % board)
677 if not version:
678 version = cpv.version_no_rev
679 elif version != cpv.version_no_rev:
680 raise NoAndroidVersionError(
681 'Different Android versions (%s vs %s) for %s' %
682 (version, cpv.version_no_rev, boards))
683 return version
684
685def determine_android_branch(board):
686 """Returns the Android branch in use by the active container ebuild."""
687 try:
688 android_package = determine_android_package(board)
689 except cros_build_lib.RunCommandError:
690 raise NoAndroidBranchError(
691 'Android branch could not be determined for %s' % board)
692 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600693 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600694 ebuild_path = portage_util.FindEbuildForBoardPackage(android_package, board)
695 # We assume all targets pull from the same branch and that we always
696 # have an ARM_TARGET, ARM_USERDEBUG_TARGET, or an X86_USERDEBUG_TARGET.
697 targets = ['ARM_TARGET', 'ARM_USERDEBUG_TARGET', 'X86_USERDEBUG_TARGET']
698 ebuild_content = osutils.SourceEnvironment(ebuild_path, targets)
699 for target in targets:
700 if target in ebuild_content:
701 branch = re.search(r'(.*?)-linux-', ebuild_content[target])
702 if branch is not None:
703 return branch.group(1)
704 raise NoAndroidBranchError(
705 'Android branch could not be determined for %s (ebuild empty?)' % board)
706
707
708def determine_android_target(board):
Michael Mortensen14960d02019-10-18 07:53:59 -0600709 """Returns the Android target in use by the active container ebuild."""
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600710 try:
711 android_package = determine_android_package(board)
712 except cros_build_lib.RunCommandError:
713 raise NoAndroidTargetError(
714 'Android Target could not be determined for %s' % board)
715 if not android_package:
Michael Mortensenedf76532019-10-16 14:22:37 -0600716 return None
Michael Mortensenb70e8a82019-10-10 18:43:41 -0600717 if android_package.startswith('chromeos-base/android-vm-'):
718 return 'bertha'
719 elif android_package.startswith('chromeos-base/android-container-'):
720 return 'cheets'
721
722 raise NoAndroidTargetError(
723 'Android Target cannot be determined for the package: %s' %
724 android_package)
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600725
726
727def determine_platform_version():
728 """Returns the platform version from the source root."""
Michael Mortensen009cb662019-10-21 11:38:43 -0600729 # Platform version is something like '12575.0.0'.
Michael Mortensen9fdb14b2019-10-17 11:17:30 -0600730 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
731 return version.VersionString()
Michael Mortensen009cb662019-10-21 11:38:43 -0600732
733
734def determine_milestone_version():
735 """Returns the platform version from the source root."""
736 # Milestone version is something like '79'.
737 version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
738 return version.chrome_branch
739
740def determine_full_version():
741 """Returns the full version from the source root."""
742 # Full version is something like 'R79-12575.0.0'.
743 milestone_version = determine_milestone_version()
744 platform_version = determine_platform_version()
745 full_version = ('R%s-%s' % (milestone_version, platform_version))
746 return full_version