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