blob: aeb14abee35535c9658e046b64ae7989aac52320 [file] [log] [blame]
Alex Kleinf4047132019-05-30 11:25:38 -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"""Simplified cros_mark_as_stable script."""
7
8from __future__ import print_function
9
10import os
Mike Frysinger2688ef62020-02-16 00:00:46 -050011import sys
Alex Kleinf4047132019-05-30 11:25:38 -060012
13from chromite.lib import constants
14from chromite.lib import commandline
15from chromite.lib import cros_build_lib
16from chromite.lib import cros_logging as logging
17from chromite.lib import git
18from chromite.lib import osutils
19from chromite.lib import parallel
20from chromite.lib import portage_util
21
22from chromite.cbuildbot import manifest_version
23
Mike Frysinger2688ef62020-02-16 00:00:46 -050024
25assert sys.version_info >= (3, 6), 'This module requires Python 3.6+'
26
27
Alex Kleinf4047132019-05-30 11:25:38 -060028# Commit message subject for uprevving Portage packages.
29GIT_COMMIT_SUBJECT = 'Marking set of ebuilds as stable'
30
31# Commit message for uprevving Portage packages.
32_GIT_COMMIT_MESSAGE = 'Marking 9999 ebuild for %s as stable.'
33
34
35def GetParser():
36 """Build the argument parser."""
37 parser = commandline.ArgumentParser(description=__doc__)
38
39 parser.add_argument('--all', action='store_true',
40 help='Mark all packages as stable.')
41 parser.add_argument('--boards', help='Colon-separated list of boards.')
42 parser.add_argument('--chroot', type='path', help='The chroot path.')
43 parser.add_argument('--force', action='store_true',
44 help='Force the stabilization of blacklisted packages. '
45 '(only compatible with -p)')
46 parser.add_argument('--overlay-type', required=True,
47 choices=['public', 'private', 'both'],
48 help='Populates --overlays based on "public", "private", '
49 'or "both".')
50 parser.add_argument('--packages',
51 help='Colon separated list of packages to rev.')
52 parser.add_argument('--verbose', action='store_true',
53 help='Prints out debug info.')
54 parser.add_argument('--dump-files', action='store_true',
Mike Frysinger80de5012019-08-01 14:10:53 -040055 help='Dump the revved packages, new files list, and '
56 'removed files list files. This is mostly for'
57 'debugging right now.')
Alex Kleinf4047132019-05-30 11:25:38 -060058
59 return parser
60
61
62def _ParseArguments(argv):
63 """Parse and validate arguments."""
64 parser = GetParser()
65 options = parser.parse_args(argv)
66
67 # Parse, cleanup, and populate options.
68 if options.packages:
69 options.packages = options.packages.split(':')
70 if options.boards:
71 options.boards = options.boards.split(':')
72 options.overlays = portage_util.FindOverlays(options.overlay_type)
73
74 # Verify options.
75 if not options.packages and not options.all:
76 parser.error('Please specify at least one package (--packages)')
77 if options.force and options.all:
78 parser.error('Cannot use --force with --all. You must specify a list of '
79 'packages you want to force uprev.')
80
81 options.Freeze()
82 return options
83
84
85def main(argv):
86 options = _ParseArguments(argv)
87
88 if not options.boards:
89 overlays = portage_util.FindOverlays(options.overlay_type)
90 else:
91 overlays = set()
92 for board in options.boards:
93 board_overlays = portage_util.FindOverlays(options.overlay_type,
94 board=board)
95 overlays = overlays.union(board_overlays)
96
97 overlays = list(overlays)
98
99 manifest = git.ManifestCheckout.Cached(constants.SOURCE_ROOT)
100
101 _WorkOnCommit(options, overlays, manifest, options.packages or None)
102
103
104def CleanStalePackages(boards, package_atoms, chroot):
105 """Cleans up stale package info from a previous build.
106
107 Args:
108 boards: Boards to clean the packages from.
109 package_atoms: A list of package atoms to unmerge.
110 chroot (str): The chroot path.
111 """
112 if package_atoms:
113 logging.info('Cleaning up stale packages %s.', package_atoms)
114
115 # First unmerge all the packages for a board, then eclean it.
116 # We need these two steps to run in order (unmerge/eclean),
117 # but we can let all the boards run in parallel.
118 def _CleanStalePackages(board):
119 if board:
120 suffix = '-' + board
Mike Frysinger45602c72019-09-22 02:15:11 -0400121 runcmd = cros_build_lib.run
Alex Kleinf4047132019-05-30 11:25:38 -0600122 else:
123 suffix = ''
Mike Frysinger45602c72019-09-22 02:15:11 -0400124 runcmd = cros_build_lib.sudo_run
Alex Kleinf4047132019-05-30 11:25:38 -0600125
126 chroot_args = ['--chroot', chroot] if chroot else None
127 emerge, eclean = 'emerge' + suffix, 'eclean' + suffix
128 if not osutils.FindMissingBinaries([emerge, eclean]):
129 if package_atoms:
130 # If nothing was found to be unmerged, emerge will exit(1).
131 result = runcmd([emerge, '-q', '--unmerge'] + list(package_atoms),
132 enter_chroot=True, chroot_args=chroot_args,
133 extra_env={'CLEAN_DELAY': '0'},
Mike Frysingerf5a3b2d2019-12-12 14:36:17 -0500134 check=False, cwd=constants.SOURCE_ROOT)
Alex Kleinf4047132019-05-30 11:25:38 -0600135 if result.returncode not in (0, 1):
136 raise cros_build_lib.RunCommandError('unexpected error', result)
137 runcmd([eclean, '-d', 'packages'],
138 cwd=constants.SOURCE_ROOT, enter_chroot=True,
Mike Frysinger0282d222019-12-17 17:15:48 -0500139 chroot_args=chroot_args, stdout=True,
140 stderr=True)
Alex Kleinf4047132019-05-30 11:25:38 -0600141
142 tasks = []
143 for board in boards:
144 tasks.append([board])
145 tasks.append([None])
146
147 parallel.RunTasksInProcessPool(_CleanStalePackages, tasks)
148
149
150def _WorkOnCommit(options, overlays, manifest, package_list):
151 """Commit uprevs of overlays belonging to different git projects in parallel.
152
153 Args:
154 options: The options object returned by the argument parser.
155 overlays: A list of overlays to work on.
156 manifest: The manifest of the given source root.
157 package_list: A list of packages passed from commandline to work on.
158 """
159 overlay_ebuilds = _GetOverlayToEbuildsMap(overlays, package_list,
160 options.force)
161
162 with parallel.Manager() as manager:
163 # Contains the array of packages we actually revved.
164 revved_packages = manager.list()
165 new_package_atoms = manager.list()
166 new_ebuild_files = manager.list()
167 removed_ebuild_files = manager.list()
168
169 inputs = [[manifest, [overlay], overlay_ebuilds, revved_packages,
170 new_package_atoms, new_ebuild_files, removed_ebuild_files]
171 for overlay in overlays]
172 parallel.RunTasksInProcessPool(_UprevOverlays, inputs)
173
174 if options.chroot and os.path.exists(options.chroot):
175 CleanStalePackages(options.boards or [], new_package_atoms,
176 options.chroot)
177
178 if options.dump_files:
179 osutils.WriteFile('/tmp/revved_packages', '\n'.join(revved_packages))
180 osutils.WriteFile('/tmp/new_ebuild_files', '\n'.join(new_ebuild_files))
181 osutils.WriteFile('/tmp/removed_ebuild_files',
182 '\n'.join(removed_ebuild_files))
183
184
185def _GetOverlayToEbuildsMap(overlays, package_list, force):
186 """Get ebuilds for overlays.
187
188 Args:
189 overlays: A list of overlays to work on.
190 package_list: A list of packages passed from commandline to work on.
191 force (bool): Whether to use packages even if in blacklist.
192
193 Returns:
194 A dict mapping each overlay to a list of ebuilds belonging to it.
195 """
196 root_version = manifest_version.VersionInfo.from_repo(constants.SOURCE_ROOT)
197 subdir_removal = manifest_version.VersionInfo('10363.0.0')
198 require_subdir_support = root_version < subdir_removal
199 use_all = not package_list
200
201 overlay_ebuilds = {}
202 inputs = [[overlay, use_all, package_list, force, require_subdir_support]
203 for overlay in overlays]
204 result = parallel.RunTasksInProcessPool(
205 portage_util.GetOverlayEBuilds, inputs)
206 for idx, ebuilds in enumerate(result):
207 overlay_ebuilds[overlays[idx]] = ebuilds
208
209 return overlay_ebuilds
210
211
212def _UprevOverlays(manifest, overlays, overlay_ebuilds,
213 revved_packages, new_package_atoms, new_ebuild_files,
214 removed_ebuild_files):
215 """Execute uprevs for overlays in sequence.
216
217 Args:
218 manifest: The manifest of the given source root.
219 overlays: A list over overlays to commit.
220 overlay_ebuilds: A dict mapping overlays to their ebuilds.
221 revved_packages: A shared list of revved packages.
222 new_package_atoms: A shared list of new package atoms.
223 new_ebuild_files: New stable ebuild paths.
224 removed_ebuild_files: Old ebuild paths that were removed.
225 """
226 for overlay in overlays:
227 if not os.path.isdir(overlay):
228 logging.warning('Skipping %s, which is not a directory.', overlay)
229 continue
230
231 ebuilds = overlay_ebuilds.get(overlay, [])
232 if not ebuilds:
233 continue
234
235 with parallel.Manager() as manager:
236 # Contains the array of packages we actually revved.
237 messages = manager.list()
238
239 inputs = [[overlay, ebuild, manifest, new_ebuild_files,
240 removed_ebuild_files, messages, revved_packages,
241 new_package_atoms] for ebuild in ebuilds]
242 parallel.RunTasksInProcessPool(_WorkOnEbuild, inputs)
243
244
245def _WorkOnEbuild(overlay, ebuild, manifest, new_ebuild_files,
246 removed_ebuild_files, messages, revved_packages,
247 new_package_atoms):
248 """Work on a single ebuild.
249
250 Args:
251 overlay: The overlay where the ebuild belongs to.
252 ebuild: The ebuild to work on.
253 manifest: The manifest of the given source root.
254 new_ebuild_files: New stable ebuild paths that were created.
255 removed_ebuild_files: Old ebuild paths that were removed.
256 messages: A share list of commit messages.
257 revved_packages: A shared list of revved packages.
258 new_package_atoms: A shared list of new package atoms.
259 """
260 logging.debug('Working on %s, info %s', ebuild.package,
261 ebuild.cros_workon_vars)
262 try:
263 result = ebuild.RevWorkOnEBuild(os.path.join(constants.SOURCE_ROOT, 'src'),
264 manifest)
David Burger8cf4a762020-03-09 10:33:38 -0600265 except portage_util.InvalidUprevSourceError as e:
266 logging.error('An error occurred while uprevving %s: %s',
267 ebuild.package, e)
268 raise
Mike Frysingerb32cc472020-05-15 00:17:54 -0400269 except OSError:
Alex Kleinf4047132019-05-30 11:25:38 -0600270 logging.warning(
271 'Cannot rev %s\n'
272 'Note you will have to go into %s '
273 'and reset the git repo yourself.', ebuild.package, overlay)
274 raise
275
276 if result:
277 new_package, ebuild_path_to_add, ebuild_path_to_remove = result
278
279 if ebuild_path_to_add:
280 new_ebuild_files.append(ebuild_path_to_add)
281 if ebuild_path_to_remove:
282 osutils.SafeUnlink(ebuild_path_to_remove)
283 removed_ebuild_files.append(ebuild_path_to_remove)
284
285 messages.append(_GIT_COMMIT_MESSAGE % ebuild.package)
286 revved_packages.append(ebuild.package)
287 new_package_atoms.append('=%s' % new_package)