blob: 0023d6d526a352d8fc4e48d793f9a5898ca56807 [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
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -060015import sys
Alex Klein87531182019-08-12 15:23:37 -060016
Yaakov Shaul730814a2019-09-10 13:58:25 -060017
Alex Kleineb77ffa2019-05-28 14:47:44 -060018from chromite.lib import constants
Evan Hernandezb51f1522019-08-15 11:29:40 -060019from chromite.lib import cros_build_lib
Alex Klein4de25e82019-08-05 15:58:39 -060020from chromite.lib import cros_logging as logging
Alex Kleineb77ffa2019-05-28 14:47:44 -060021from chromite.lib import git
Alex Kleineb77ffa2019-05-28 14:47:44 -060022from chromite.lib import portage_util
Alex Kleind6195b62019-08-06 16:01:16 -060023from chromite.lib import uprev_lib
Alex Kleineb77ffa2019-05-28 14:47:44 -060024
Alex Klein87531182019-08-12 15:23:37 -060025# Registered handlers for uprevving versioned packages.
26_UPREV_FUNCS = {}
27
Alex Kleineb77ffa2019-05-28 14:47:44 -060028
29class Error(Exception):
30 """Module's base error class."""
31
32
Alex Klein4de25e82019-08-05 15:58:39 -060033class UnknownPackageError(Error):
34 """Uprev attempted for a package without a registered handler."""
35
36
Alex Kleineb77ffa2019-05-28 14:47:44 -060037class UprevError(Error):
38 """An error occurred while uprevving packages."""
39
40
Alex Klein4de25e82019-08-05 15:58:39 -060041class AndroidIsPinnedUprevError(UprevError):
42 """Raised when we try to uprev while Android is pinned."""
43
44 def __init__(self, new_android_atom):
45 """Initialize a AndroidIsPinnedUprevError.
46
47 Args:
48 new_android_atom: The Android atom that we failed to
49 uprev to, due to Android being pinned.
50 """
51 assert new_android_atom
52 msg = ('Failed up uprev to Android version %s as Android was pinned.' %
53 new_android_atom)
54 super(AndroidIsPinnedUprevError, self).__init__(msg)
55 self.new_android_atom = new_android_atom
Alex Klein87531182019-08-12 15:23:37 -060056
57
Yaakov Shaul1eafe832019-09-10 16:50:26 -060058class EbuildManifestError(Error):
59 """Error when running ebuild manifest."""
60
61
Yaakov Shaul730814a2019-09-10 13:58:25 -060062UprevVersionedPackageModifications = collections.namedtuple(
63 'UprevVersionedPackageModifications', ('new_version', 'files'))
Alex Klein34afcbc2019-08-22 16:14:31 -060064
Yaakov Shaul730814a2019-09-10 13:58:25 -060065
66class UprevVersionedPackageResult(object):
67 """Data object for uprev_versioned_package."""
68
69 def __init__(self):
70 self.modified = []
71
72 def add_result(self, new_version, modified_files):
73 """Adds version/ebuilds tuple to result.
74
75 Args:
76 new_version: New version number of package.
77 modified_files: List of files modified for the given version.
78 """
79 result = UprevVersionedPackageModifications(new_version, modified_files)
80 self.modified.append(result)
81 return self
Alex Klein34afcbc2019-08-22 16:14:31 -060082
83 @property
84 def uprevved(self):
Yaakov Shaul730814a2019-09-10 13:58:25 -060085 return bool(self.modified)
Alex Klein34afcbc2019-08-22 16:14:31 -060086
87
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -060088def patch_ebuild_vars(ebuild_path, variables):
89 """Updates variables in ebuild.
90
91 Use this function rather than portage_util.EBuild.UpdateEBuild when you
92 want to preserve the variable position and quotes within the ebuild.
93
94 Args:
95 ebuild_path: The path of the ebuild.
96 variables: Dictionary of variables to update in ebuild.
97 """
98 try:
99 for line in fileinput.input(ebuild_path, inplace=1):
100 varname, eq, _ = line.partition('=')
101 if eq == '=' and varname.strip() in variables:
102 value = variables[varname]
103 sys.stdout.write('%s="%s"\n' % (varname, value))
104 else:
105 sys.stdout.write(line)
106 finally:
107 fileinput.close()
108
109
Alex Klein87531182019-08-12 15:23:37 -0600110def uprevs_versioned_package(package):
111 """Decorator to register package uprev handlers."""
112 assert package
113
114 def register(func):
115 """Registers |func| as a handler for |package|."""
116 _UPREV_FUNCS[package] = func
117
118 @functools.wraps(func)
119 def pass_through(*args, **kwargs):
120 return func(*args, **kwargs)
121
122 return pass_through
123
124 return register
125
126
Alex Klein4de25e82019-08-05 15:58:39 -0600127def uprev_android(tracking_branch, android_package, android_build_branch,
128 chroot, build_targets=None, android_version=None,
129 android_gts_build_branch=None):
130 """Returns the portage atom for the revved Android ebuild - see man emerge."""
131 command = ['cros_mark_android_as_stable',
132 '--tracking_branch=%s' % tracking_branch,
133 '--android_package=%s' % android_package,
134 '--android_build_branch=%s' % android_build_branch]
135 if build_targets:
136 command.append('--boards=%s' % ':'.join(bt.name for bt in build_targets))
137 if android_version:
138 command.append('--force_version=%s' % android_version)
139 if android_gts_build_branch:
140 command.append('--android_gts_build_branch=%s' % android_gts_build_branch)
141
Mike Frysinger45602c72019-09-22 02:15:11 -0400142 result = cros_build_lib.run(command, redirect_stdout=True,
143 enter_chroot=True,
144 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600145
146 android_atom = _parse_android_atom(result)
147 if not android_atom:
148 logging.info('Found nothing to rev.')
149 return None
150
151 for target in build_targets or []:
152 # Sanity check: We should always be able to merge the version of
153 # Android we just unmasked.
154 command = ['emerge-%s' % target.name, '-p', '--quiet',
155 '=%s' % android_atom]
156 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400157 cros_build_lib.run(command, enter_chroot=True,
158 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600159 except cros_build_lib.RunCommandError:
160 logging.error(
161 'Cannot emerge-%s =%s\nIs Android pinned to an older '
162 'version?', target, android_atom)
163 raise AndroidIsPinnedUprevError(android_atom)
164
165 return android_atom
166
167
168def _parse_android_atom(result):
169 """Helper to parse the atom from the cros_mark_android_as_stable output.
170
171 This function is largely just intended to make testing easier.
172
173 Args:
174 result (cros_build_lib.CommandResult): The cros_mark_android_as_stable
175 command result.
176 """
177 portage_atom_string = result.output.strip()
178
179 android_atom = None
180 if portage_atom_string:
181 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
182
183 return android_atom
184
185
Alex Kleineb77ffa2019-05-28 14:47:44 -0600186def uprev_build_targets(build_targets, overlay_type, chroot=None,
187 output_dir=None):
188 """Uprev the set provided build targets, or all if not specified.
189
190 Args:
191 build_targets (list[build_target_util.BuildTarget]|None): The build targets
192 whose overlays should be uprevved, empty or None for all.
193 overlay_type (str): One of the valid overlay types except None (see
194 constants.VALID_OVERLAYS).
195 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
196 output_dir (str|None): The path to optionally dump result files.
197 """
198 # Need a valid overlay, but exclude None.
199 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
200
201 if build_targets:
202 overlays = portage_util.FindOverlaysForBoards(
203 overlay_type, boards=[t.name for t in build_targets])
204 else:
205 overlays = portage_util.FindOverlays(overlay_type)
206
207 return uprev_overlays(overlays, build_targets=build_targets, chroot=chroot,
208 output_dir=output_dir)
209
210
211def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
212 """Uprev the given overlays.
213
214 Args:
215 overlays (list[str]): The list of overlay paths.
216 build_targets (list[build_target_util.BuildTarget]|None): The build targets
217 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
218 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
219 output_dir (str|None): The path to optionally dump result files.
220
221 Returns:
222 list[str] - The paths to all of the modified ebuild files. This includes the
223 new files that were added (i.e. the new versions) and all of the removed
224 files (i.e. the old versions).
225 """
226 assert overlays
227
228 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
229
Alex Kleind6195b62019-08-06 16:01:16 -0600230 uprev_manager = uprev_lib.UprevOverlayManager(overlays, manifest,
231 build_targets=build_targets,
232 chroot=chroot,
233 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600234 uprev_manager.uprev()
235
236 return uprev_manager.modified_ebuilds
237
238
Alex Klein87531182019-08-12 15:23:37 -0600239def uprev_versioned_package(package, build_targets, refs, chroot):
240 """Call registered uprev handler function for the package.
241
242 Args:
243 package (portage_util.CPV): The package being uprevved.
244 build_targets (list[build_target_util.BuildTarget]): The build targets to
245 clean on a successful uprev.
246 refs (list[uprev_lib.GitRef]):
247 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
248
249 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600250 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600251 """
252 assert package
253
254 if package.cp not in _UPREV_FUNCS:
255 raise UnknownPackageError(
256 'Package "%s" does not have a registered handler.' % package.cp)
257
258 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
259
260
Evan Hernandezb51f1522019-08-15 11:29:40 -0600261# TODO(evanhernandez): Remove this. Only a quick hack for testing.
262@uprevs_versioned_package('sample/sample')
263def uprev_sample(*_args, **_kwargs):
264 """Mimics an uprev by changing files in sandbox repos.
265
266 See: uprev_versioned_package.
267 """
268 paths = [
269 os.path.join(constants.SOURCE_ROOT, 'infra/dummies', repo, 'sample.txt')
270 for repo in ('general-sandbox', 'merge-sandbox')
271 ]
272
Yaakov Shaul730814a2019-09-10 13:58:25 -0600273 return UprevVersionedPackageResult().add_result('1.2.3', paths)
Evan Hernandezb51f1522019-08-15 11:29:40 -0600274
275
Yaakov Shaul395ae832019-09-09 14:45:32 -0600276@uprevs_versioned_package('afdo/kernel-profiles')
277def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600278 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600279
280 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600281
282 Raises:
283 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600284 """
285 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
286 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
287
David Burger92485342019-09-10 17:52:45 -0600288 with open(path, 'r') as f:
289 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600290
Yaakov Shaul730814a2019-09-10 13:58:25 -0600291 result = UprevVersionedPackageResult()
Yaakov Shaul395ae832019-09-09 14:45:32 -0600292 for version, version_info in versions.items():
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600293 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
294 'sys-kernel', version)
295 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
296 '%s-9999.ebuild' % version)
Yaakov Shaula187b152019-09-11 12:41:32 -0600297 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
298 '%s-9999.ebuild' % version)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600299 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600300 patch_ebuild_vars(ebuild_path,
301 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600302
303 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600304 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400305 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600306 except cros_build_lib.RunCommandError as e:
307 raise EbuildManifestError(
308 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600309 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600310
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600311 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600312
Yaakov Shaul730814a2019-09-10 13:58:25 -0600313 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
314
315 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600316
317
Yaakov Shaul45990452019-09-16 14:52:06 -0600318@uprevs_versioned_package('afdo/chrome-profiles')
319def uprev_chrome_afdo(*_args, **_kwargs):
320 """Updates chrome ebuilds with versions from chrome_afdo.json.
321
322 See: uprev_versioned_package.
323
324 Raises:
325 EbuildManifestError: When ebuild manifest does not complete successfuly.
326 """
327 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
328 'toolchain-utils', 'afdo_metadata', 'chrome_afdo.json')
329
330 with open(path, 'r') as f:
331 versions = json.load(f)
332
333 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
334 'chromeos-base', 'chromeos-chrome')
335 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
336 'chromeos-chrome-9999.ebuild')
337 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
338 'chromeos-chrome-9999.ebuild')
339
340 result = UprevVersionedPackageResult()
341 for version, version_info in versions.items():
342 afdo_profile_version = version_info['name']
343 varname = 'AFDO_FILE["%s"]' % version
344 patch_ebuild_vars(ebuild_path, {varname: afdo_profile_version})
345
346 try:
347 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400348 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul45990452019-09-16 14:52:06 -0600349 except cros_build_lib.RunCommandError as e:
350 raise EbuildManifestError(
351 'Error encountered when regenerating the manifest for ebuild: %s\n%s' %
352 (chroot_ebuild_path, e), e)
353
354 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
355 result.add_result('chromeos-chrome', [ebuild_path, manifest_path])
356
357 return result
358
359
Alex Klein87531182019-08-12 15:23:37 -0600360@uprevs_versioned_package(constants.CHROME_CP)
361def uprev_chrome(build_targets, refs, chroot):
362 """Uprev chrome and its related packages.
363
364 See: uprev_versioned_package.
365 """
366 # Determine the version from the refs (tags), i.e. the chrome versions are the
367 # tag names.
368 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
369
370 uprev_manager = uprev_lib.UprevChromeManager(
371 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600372 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600373 # Start with chrome itself, as we can't do anything else unless chrome
374 # uprevs successfully.
375 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600376 return result
Alex Klein87531182019-08-12 15:23:37 -0600377
378 # With a successful chrome rev, also uprev related packages.
379 for package in constants.OTHER_CHROME_PACKAGES:
380 uprev_manager.uprev(package)
381
David Burger37f48672019-09-18 17:07:56 -0600382 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600383
384
Alex Kleinbbef2b32019-08-27 10:38:50 -0600385def get_best_visible(atom, build_target=None):
386 """Returns the best visible CPV for the given atom.
387
388 Args:
389 atom (str): The atom to look up.
390 build_target (build_target_util.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600391 sysroot should be searched, or the SDK if not provided.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600392 """
David Burger1e0fe232019-07-01 14:52:07 -0600393 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600394
395 board = build_target.name if build_target else None
396 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600397
398
399def has_prebuilt(atom, build_target=None):
400 """Check if a prebuilt exists.
401
402 Args:
403 atom (str): The package whose prebuilt is being queried.
404 build_target (build_target_util.BuildTarget): The build target whose
405 sysroot should be searched, or the SDK if not provided.
406 """
407 assert atom
408
409 board = build_target.name if build_target else None
410 return portage_util.HasPrebuilt(atom, board=board)