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