blob: eb3874aff867883487e2c4ec6d112737fb79d80b [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 Klein36b117f2019-09-30 15:13:46 -060025if cros_build_lib.IsInsideChroot():
26 from chromite.service import dependency
27
Alex Klein87531182019-08-12 15:23:37 -060028# Registered handlers for uprevving versioned packages.
29_UPREV_FUNCS = {}
30
Alex Kleineb77ffa2019-05-28 14:47:44 -060031
32class Error(Exception):
33 """Module's base error class."""
34
35
Alex Klein4de25e82019-08-05 15:58:39 -060036class UnknownPackageError(Error):
37 """Uprev attempted for a package without a registered handler."""
38
39
Alex Kleineb77ffa2019-05-28 14:47:44 -060040class UprevError(Error):
41 """An error occurred while uprevving packages."""
42
43
Alex Klein4de25e82019-08-05 15:58:39 -060044class AndroidIsPinnedUprevError(UprevError):
45 """Raised when we try to uprev while Android is pinned."""
46
47 def __init__(self, new_android_atom):
48 """Initialize a AndroidIsPinnedUprevError.
49
50 Args:
51 new_android_atom: The Android atom that we failed to
52 uprev to, due to Android being pinned.
53 """
54 assert new_android_atom
55 msg = ('Failed up uprev to Android version %s as Android was pinned.' %
56 new_android_atom)
57 super(AndroidIsPinnedUprevError, self).__init__(msg)
58 self.new_android_atom = new_android_atom
Alex Klein87531182019-08-12 15:23:37 -060059
60
Yaakov Shaul1eafe832019-09-10 16:50:26 -060061class EbuildManifestError(Error):
62 """Error when running ebuild manifest."""
63
64
Yaakov Shaul730814a2019-09-10 13:58:25 -060065UprevVersionedPackageModifications = collections.namedtuple(
66 'UprevVersionedPackageModifications', ('new_version', 'files'))
Alex Klein34afcbc2019-08-22 16:14:31 -060067
Yaakov Shaul730814a2019-09-10 13:58:25 -060068
69class UprevVersionedPackageResult(object):
70 """Data object for uprev_versioned_package."""
71
72 def __init__(self):
73 self.modified = []
74
75 def add_result(self, new_version, modified_files):
76 """Adds version/ebuilds tuple to result.
77
78 Args:
79 new_version: New version number of package.
80 modified_files: List of files modified for the given version.
81 """
82 result = UprevVersionedPackageModifications(new_version, modified_files)
83 self.modified.append(result)
84 return self
Alex Klein34afcbc2019-08-22 16:14:31 -060085
86 @property
87 def uprevved(self):
Yaakov Shaul730814a2019-09-10 13:58:25 -060088 return bool(self.modified)
Alex Klein34afcbc2019-08-22 16:14:31 -060089
90
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -060091def patch_ebuild_vars(ebuild_path, variables):
92 """Updates variables in ebuild.
93
94 Use this function rather than portage_util.EBuild.UpdateEBuild when you
95 want to preserve the variable position and quotes within the ebuild.
96
97 Args:
98 ebuild_path: The path of the ebuild.
99 variables: Dictionary of variables to update in ebuild.
100 """
101 try:
102 for line in fileinput.input(ebuild_path, inplace=1):
103 varname, eq, _ = line.partition('=')
104 if eq == '=' and varname.strip() in variables:
105 value = variables[varname]
106 sys.stdout.write('%s="%s"\n' % (varname, value))
107 else:
108 sys.stdout.write(line)
109 finally:
110 fileinput.close()
111
112
Alex Klein87531182019-08-12 15:23:37 -0600113def uprevs_versioned_package(package):
114 """Decorator to register package uprev handlers."""
115 assert package
116
117 def register(func):
118 """Registers |func| as a handler for |package|."""
119 _UPREV_FUNCS[package] = func
120
121 @functools.wraps(func)
122 def pass_through(*args, **kwargs):
123 return func(*args, **kwargs)
124
125 return pass_through
126
127 return register
128
129
Alex Klein4de25e82019-08-05 15:58:39 -0600130def uprev_android(tracking_branch, android_package, android_build_branch,
131 chroot, build_targets=None, android_version=None,
132 android_gts_build_branch=None):
133 """Returns the portage atom for the revved Android ebuild - see man emerge."""
134 command = ['cros_mark_android_as_stable',
135 '--tracking_branch=%s' % tracking_branch,
136 '--android_package=%s' % android_package,
137 '--android_build_branch=%s' % android_build_branch]
138 if build_targets:
139 command.append('--boards=%s' % ':'.join(bt.name for bt in build_targets))
140 if android_version:
141 command.append('--force_version=%s' % android_version)
142 if android_gts_build_branch:
143 command.append('--android_gts_build_branch=%s' % android_gts_build_branch)
144
Mike Frysinger45602c72019-09-22 02:15:11 -0400145 result = cros_build_lib.run(command, redirect_stdout=True,
146 enter_chroot=True,
147 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600148
149 android_atom = _parse_android_atom(result)
150 if not android_atom:
151 logging.info('Found nothing to rev.')
152 return None
153
154 for target in build_targets or []:
155 # Sanity check: We should always be able to merge the version of
156 # Android we just unmasked.
157 command = ['emerge-%s' % target.name, '-p', '--quiet',
158 '=%s' % android_atom]
159 try:
Mike Frysinger45602c72019-09-22 02:15:11 -0400160 cros_build_lib.run(command, enter_chroot=True,
161 chroot_args=chroot.get_enter_args())
Alex Klein4de25e82019-08-05 15:58:39 -0600162 except cros_build_lib.RunCommandError:
163 logging.error(
164 'Cannot emerge-%s =%s\nIs Android pinned to an older '
165 'version?', target, android_atom)
166 raise AndroidIsPinnedUprevError(android_atom)
167
168 return android_atom
169
170
171def _parse_android_atom(result):
172 """Helper to parse the atom from the cros_mark_android_as_stable output.
173
174 This function is largely just intended to make testing easier.
175
176 Args:
177 result (cros_build_lib.CommandResult): The cros_mark_android_as_stable
178 command result.
179 """
180 portage_atom_string = result.output.strip()
181
182 android_atom = None
183 if portage_atom_string:
184 android_atom = portage_atom_string.splitlines()[-1].partition('=')[-1]
185
186 return android_atom
187
188
Alex Kleineb77ffa2019-05-28 14:47:44 -0600189def uprev_build_targets(build_targets, overlay_type, chroot=None,
190 output_dir=None):
191 """Uprev the set provided build targets, or all if not specified.
192
193 Args:
194 build_targets (list[build_target_util.BuildTarget]|None): The build targets
195 whose overlays should be uprevved, empty or None for all.
196 overlay_type (str): One of the valid overlay types except None (see
197 constants.VALID_OVERLAYS).
198 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
199 output_dir (str|None): The path to optionally dump result files.
200 """
201 # Need a valid overlay, but exclude None.
202 assert overlay_type and overlay_type in constants.VALID_OVERLAYS
203
204 if build_targets:
205 overlays = portage_util.FindOverlaysForBoards(
206 overlay_type, boards=[t.name for t in build_targets])
207 else:
208 overlays = portage_util.FindOverlays(overlay_type)
209
210 return uprev_overlays(overlays, build_targets=build_targets, chroot=chroot,
211 output_dir=output_dir)
212
213
214def uprev_overlays(overlays, build_targets=None, chroot=None, output_dir=None):
215 """Uprev the given overlays.
216
217 Args:
218 overlays (list[str]): The list of overlay paths.
219 build_targets (list[build_target_util.BuildTarget]|None): The build targets
220 to clean in |chroot|, if desired. No effect unless |chroot| is provided.
221 chroot (chroot_lib.Chroot|None): The chroot to clean, if desired.
222 output_dir (str|None): The path to optionally dump result files.
223
224 Returns:
225 list[str] - The paths to all of the modified ebuild files. This includes the
226 new files that were added (i.e. the new versions) and all of the removed
227 files (i.e. the old versions).
228 """
229 assert overlays
230
231 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
232
Alex Kleind6195b62019-08-06 16:01:16 -0600233 uprev_manager = uprev_lib.UprevOverlayManager(overlays, manifest,
234 build_targets=build_targets,
235 chroot=chroot,
236 output_dir=output_dir)
Alex Kleineb77ffa2019-05-28 14:47:44 -0600237 uprev_manager.uprev()
238
239 return uprev_manager.modified_ebuilds
240
241
Alex Klein87531182019-08-12 15:23:37 -0600242def uprev_versioned_package(package, build_targets, refs, chroot):
243 """Call registered uprev handler function for the package.
244
245 Args:
246 package (portage_util.CPV): The package being uprevved.
247 build_targets (list[build_target_util.BuildTarget]): The build targets to
248 clean on a successful uprev.
249 refs (list[uprev_lib.GitRef]):
250 chroot (chroot_lib.Chroot): The chroot to enter for cleaning.
251
252 Returns:
Alex Klein34afcbc2019-08-22 16:14:31 -0600253 UprevVersionedPackageResult: The result.
Alex Klein87531182019-08-12 15:23:37 -0600254 """
255 assert package
256
257 if package.cp not in _UPREV_FUNCS:
258 raise UnknownPackageError(
259 'Package "%s" does not have a registered handler.' % package.cp)
260
261 return _UPREV_FUNCS[package.cp](build_targets, refs, chroot)
262
263
Evan Hernandezb51f1522019-08-15 11:29:40 -0600264# TODO(evanhernandez): Remove this. Only a quick hack for testing.
265@uprevs_versioned_package('sample/sample')
266def uprev_sample(*_args, **_kwargs):
267 """Mimics an uprev by changing files in sandbox repos.
268
269 See: uprev_versioned_package.
270 """
271 paths = [
272 os.path.join(constants.SOURCE_ROOT, 'infra/dummies', repo, 'sample.txt')
273 for repo in ('general-sandbox', 'merge-sandbox')
274 ]
275
Yaakov Shaul730814a2019-09-10 13:58:25 -0600276 return UprevVersionedPackageResult().add_result('1.2.3', paths)
Evan Hernandezb51f1522019-08-15 11:29:40 -0600277
278
Yaakov Shaul395ae832019-09-09 14:45:32 -0600279@uprevs_versioned_package('afdo/kernel-profiles')
280def uprev_kernel_afdo(*_args, **_kwargs):
David Burger92485342019-09-10 17:52:45 -0600281 """Updates kernel ebuilds with versions from kernel_afdo.json.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600282
283 See: uprev_versioned_package.
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600284
285 Raises:
286 EbuildManifestError: When ebuild manifest does not complete successfuly.
Yaakov Shaul395ae832019-09-09 14:45:32 -0600287 """
288 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
289 'toolchain-utils', 'afdo_metadata', 'kernel_afdo.json')
290
David Burger92485342019-09-10 17:52:45 -0600291 with open(path, 'r') as f:
292 versions = json.load(f)
Yaakov Shaul395ae832019-09-09 14:45:32 -0600293
Yaakov Shaul730814a2019-09-10 13:58:25 -0600294 result = UprevVersionedPackageResult()
Yaakov Shaul395ae832019-09-09 14:45:32 -0600295 for version, version_info in versions.items():
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600296 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
297 'sys-kernel', version)
298 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
299 '%s-9999.ebuild' % version)
Yaakov Shaula187b152019-09-11 12:41:32 -0600300 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
301 '%s-9999.ebuild' % version)
Yaakov Shaul730814a2019-09-10 13:58:25 -0600302 afdo_profile_version = version_info['name']
Yaakov Shaulcb1cfc32019-09-16 13:51:19 -0600303 patch_ebuild_vars(ebuild_path,
304 dict(AFDO_PROFILE_VERSION=afdo_profile_version))
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600305
306 try:
Yaakov Shaul730814a2019-09-10 13:58:25 -0600307 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400308 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600309 except cros_build_lib.RunCommandError as e:
310 raise EbuildManifestError(
311 'Error encountered when regenerating the manifest for ebuild: %s\n%s'
Yaakov Shaula187b152019-09-11 12:41:32 -0600312 % (chroot_ebuild_path, e), e)
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600313
Yaakov Shauldd8b4112019-09-11 11:44:03 -0600314 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
Yaakov Shaul1eafe832019-09-10 16:50:26 -0600315
Yaakov Shaul730814a2019-09-10 13:58:25 -0600316 result.add_result(afdo_profile_version, [ebuild_path, manifest_path])
317
318 return result
Yaakov Shaul395ae832019-09-09 14:45:32 -0600319
320
Yaakov Shaul45990452019-09-16 14:52:06 -0600321@uprevs_versioned_package('afdo/chrome-profiles')
322def uprev_chrome_afdo(*_args, **_kwargs):
323 """Updates chrome ebuilds with versions from chrome_afdo.json.
324
325 See: uprev_versioned_package.
326
327 Raises:
328 EbuildManifestError: When ebuild manifest does not complete successfuly.
329 """
330 path = os.path.join(constants.SOURCE_ROOT, 'src', 'third_party',
331 'toolchain-utils', 'afdo_metadata', 'chrome_afdo.json')
332
333 with open(path, 'r') as f:
334 versions = json.load(f)
335
336 path = os.path.join('src', 'third_party', 'chromiumos-overlay',
337 'chromeos-base', 'chromeos-chrome')
338 ebuild_path = os.path.join(constants.SOURCE_ROOT, path,
339 'chromeos-chrome-9999.ebuild')
340 chroot_ebuild_path = os.path.join(constants.CHROOT_SOURCE_ROOT, path,
341 'chromeos-chrome-9999.ebuild')
342
343 result = UprevVersionedPackageResult()
344 for version, version_info in versions.items():
345 afdo_profile_version = version_info['name']
346 varname = 'AFDO_FILE["%s"]' % version
347 patch_ebuild_vars(ebuild_path, {varname: afdo_profile_version})
348
349 try:
350 cmd = ['ebuild', chroot_ebuild_path, 'manifest', '--force']
Mike Frysinger45602c72019-09-22 02:15:11 -0400351 cros_build_lib.run(cmd, enter_chroot=True)
Yaakov Shaul45990452019-09-16 14:52:06 -0600352 except cros_build_lib.RunCommandError as e:
353 raise EbuildManifestError(
354 'Error encountered when regenerating the manifest for ebuild: %s\n%s' %
355 (chroot_ebuild_path, e), e)
356
357 manifest_path = os.path.join(constants.SOURCE_ROOT, path, 'Manifest')
358 result.add_result('chromeos-chrome', [ebuild_path, manifest_path])
359
360 return result
361
362
Alex Klein87531182019-08-12 15:23:37 -0600363@uprevs_versioned_package(constants.CHROME_CP)
364def uprev_chrome(build_targets, refs, chroot):
365 """Uprev chrome and its related packages.
366
367 See: uprev_versioned_package.
368 """
369 # Determine the version from the refs (tags), i.e. the chrome versions are the
370 # tag names.
371 chrome_version = uprev_lib.get_chrome_version_from_refs(refs)
372
373 uprev_manager = uprev_lib.UprevChromeManager(
374 chrome_version, build_targets=build_targets, chroot=chroot)
David Burger37f48672019-09-18 17:07:56 -0600375 result = UprevVersionedPackageResult()
Alex Klein87531182019-08-12 15:23:37 -0600376 # Start with chrome itself, as we can't do anything else unless chrome
377 # uprevs successfully.
378 if not uprev_manager.uprev(constants.CHROME_CP):
David Burger37f48672019-09-18 17:07:56 -0600379 return result
Alex Klein87531182019-08-12 15:23:37 -0600380
381 # With a successful chrome rev, also uprev related packages.
382 for package in constants.OTHER_CHROME_PACKAGES:
383 uprev_manager.uprev(package)
384
David Burger37f48672019-09-18 17:07:56 -0600385 return result.add_result(chrome_version, uprev_manager.modified_ebuilds)
Alex Klein87531182019-08-12 15:23:37 -0600386
387
Alex Kleinbbef2b32019-08-27 10:38:50 -0600388def get_best_visible(atom, build_target=None):
389 """Returns the best visible CPV for the given atom.
390
391 Args:
392 atom (str): The atom to look up.
393 build_target (build_target_util.BuildTarget): The build target whose
Alex Kleinda39c6d2019-09-16 14:36:36 -0600394 sysroot should be searched, or the SDK if not provided.
Alex Kleinbbef2b32019-08-27 10:38:50 -0600395 """
David Burger1e0fe232019-07-01 14:52:07 -0600396 assert atom
Alex Kleinbbef2b32019-08-27 10:38:50 -0600397
398 board = build_target.name if build_target else None
399 return portage_util.PortageqBestVisible(atom, board=board)
Alex Kleinda39c6d2019-09-16 14:36:36 -0600400
401
402def has_prebuilt(atom, build_target=None):
403 """Check if a prebuilt exists.
404
405 Args:
406 atom (str): The package whose prebuilt is being queried.
407 build_target (build_target_util.BuildTarget): The build target whose
408 sysroot should be searched, or the SDK if not provided.
409 """
410 assert atom
411
412 board = build_target.name if build_target else None
413 return portage_util.HasPrebuilt(atom, board=board)
Alex Klein36b117f2019-09-30 15:13:46 -0600414
415
416def builds(atom, build_target):
417 """Check if |build_target| builds |atom| (has it in its depgraph)."""
418 cros_build_lib.AssertInsideChroot()
419
420 graph = dependency.GetBuildDependency(build_target.name)
421 return any(atom in package for package in graph['package_deps'])