blob: 78a00acec804082375a0b615088b90ed94932d59 [file] [log] [blame]
Chris Sosadad0d322011-01-31 16:37:33 -08001#!/usr/bin/python
2
Mike Frysinger110750a2012-03-26 14:19:20 -04003# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Chris Sosadad0d322011-01-31 16:37:33 -08004# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7"""This module uprevs a given package's ebuild to the next revision."""
8
Mike Frysinger110750a2012-03-26 14:19:20 -04009import multiprocessing
Chris Sosa62ad8522011-03-08 17:46:17 -080010import optparse
Chris Sosadad0d322011-01-31 16:37:33 -080011import os
Chris Sosadad0d322011-01-31 16:37:33 -080012import sys
13
J. Richard Barnetted422f622011-11-17 09:39:46 -080014import constants
Chris Sosa471532a2011-02-01 15:10:06 -080015if __name__ == '__main__':
David James710b7dc2012-02-07 16:49:59 -080016 sys.path.insert(0, constants.SOURCE_ROOT)
Chris Sosa471532a2011-02-01 15:10:06 -080017
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040018from chromite.buildbot import cbuildbot_background as background
David James66009462012-03-25 10:08:38 -070019from chromite.buildbot import portage_utilities
Chris Sosac13bba52011-05-24 15:14:09 -070020from chromite.lib import cros_build_lib
21
Chris Sosadad0d322011-01-31 16:37:33 -080022
Chris Sosa62ad8522011-03-08 17:46:17 -080023# TODO(sosa): Remove during OO refactor.
24VERBOSE = False
Chris Sosadad0d322011-01-31 16:37:33 -080025
Chris Sosadad0d322011-01-31 16:37:33 -080026# Dictionary of valid commands with usage information.
27COMMAND_DICTIONARY = {
Chris Sosadad0d322011-01-31 16:37:33 -080028 'commit':
29 'Marks given ebuilds as stable locally',
30 'push':
31 'Pushes previous marking of ebuilds to remote repo',
32 }
33
Chris Sosadad0d322011-01-31 16:37:33 -080034
Chris Sosadad0d322011-01-31 16:37:33 -080035# ======================= Global Helper Functions ========================
36
37
38def _Print(message):
39 """Verbose print function."""
Chris Sosac31bd2d2011-04-29 17:53:35 -070040 if VERBOSE:
Chris Sosac13bba52011-05-24 15:14:09 -070041 cros_build_lib.Info(message)
Chris Sosadad0d322011-01-31 16:37:33 -080042
43
David Jamescc09c9b2012-01-26 22:10:13 -080044def CleanStalePackages(boards, package_atoms):
Chris Sosabf153872011-04-28 14:21:09 -070045 """Cleans up stale package info from a previous build.
46 Args:
David Jamescc09c9b2012-01-26 22:10:13 -080047 boards: Boards to clean the packages from.
Chris Sosabf153872011-04-28 14:21:09 -070048 package_atoms: The actual package atom to unmerge.
49 """
50 if package_atoms:
Chris Sosac13bba52011-05-24 15:14:09 -070051 cros_build_lib.Info('Cleaning up stale packages %s.' % package_atoms)
Chris Sosadad0d322011-01-31 16:37:33 -080052
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040053 # First unmerge all the packages for a board, then eclean it.
54 # We need these two steps to run in order (unmerge/eclean),
55 # but we can let all the boards run in parallel.
56 def _CleanStalePackages(board):
57 if board:
58 suffix = '-' + board
59 runcmd = cros_build_lib.RunCommand
60 else:
61 suffix = ''
62 runcmd = cros_build_lib.SudoRunCommand
Chris Sosadad0d322011-01-31 16:37:33 -080063
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040064 if package_atoms:
65 runcmd(['emerge' + suffix, '-q', '--unmerge'] + package_atoms);
66 runcmd(['eclean' + suffix, '-d', 'packages'],
67 redirect_stdout=True, redirect_stderr=True)
68
69 tasks = []
David Jamescc09c9b2012-01-26 22:10:13 -080070 for board in boards:
Mike Frysingerb7ab9b82012-04-04 16:22:43 -040071 tasks.append([board])
72 tasks.append([None])
73
74 background.RunTasksInProcessPool(_CleanStalePackages, tasks)
Chris Sosadad0d322011-01-31 16:37:33 -080075
76
Mike Frysinger2ebe3732012-05-08 17:04:12 -040077# TODO(build): This code needs to be gutted and rebased to cros_build_lib.
78def _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -080079 """Returns true if there are local commits."""
Brian Harring609dc4e2012-05-07 02:17:44 -070080 current_branch = cros_build_lib.GetCurrentBranch(cwd)
81
82 if current_branch != stable_branch:
Chris Sosadad0d322011-01-31 16:37:33 -080083 return False
Brian Harring609dc4e2012-05-07 02:17:44 -070084 output = cros_build_lib.RunGitCommand(
85 cwd, ['rev-parse', 'HEAD', tracking_branch]).output.split()
86 return output[0] == output[1]
Chris Sosadad0d322011-01-31 16:37:33 -080087
88
Chris Sosa62ad8522011-03-08 17:46:17 -080089def _CheckSaneArguments(package_list, command, options):
Chris Sosadad0d322011-01-31 16:37:33 -080090 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
91 if not command in COMMAND_DICTIONARY.keys():
92 _PrintUsageAndDie('%s is not a valid command' % command)
Chris Sosa62ad8522011-03-08 17:46:17 -080093 if not options.packages and command == 'commit' and not options.all:
Chris Sosadad0d322011-01-31 16:37:33 -080094 _PrintUsageAndDie('Please specify at least one package')
David Jamescc09c9b2012-01-26 22:10:13 -080095 if not options.boards and command == 'commit':
Chris Sosadad0d322011-01-31 16:37:33 -080096 _PrintUsageAndDie('Please specify a board')
Chris Sosa62ad8522011-03-08 17:46:17 -080097 if not os.path.isdir(options.srcroot):
Chris Sosadad0d322011-01-31 16:37:33 -080098 _PrintUsageAndDie('srcroot is not a valid path')
Chris Sosa62ad8522011-03-08 17:46:17 -080099 options.srcroot = os.path.abspath(options.srcroot)
Chris Sosadad0d322011-01-31 16:37:33 -0800100
101
102def _PrintUsageAndDie(error_message=''):
103 """Prints optional error_message the usage and returns an error exit code."""
104 command_usage = 'Commands: \n'
105 # Add keys and usage information from dictionary.
106 commands = sorted(COMMAND_DICTIONARY.keys())
107 for command in commands:
108 command_usage += ' %s: %s\n' % (command, COMMAND_DICTIONARY[command])
109 commands_str = '|'.join(commands)
Chris Sosac13bba52011-05-24 15:14:09 -0700110 cros_build_lib.Warning('Usage: %s FLAGS [%s]\n\n%s' % (
111 sys.argv[0], commands_str, command_usage))
Chris Sosadad0d322011-01-31 16:37:33 -0800112 if error_message:
Chris Sosac13bba52011-05-24 15:14:09 -0700113 cros_build_lib.Die(error_message)
Chris Sosadad0d322011-01-31 16:37:33 -0800114 else:
115 sys.exit(1)
116
117
Chris Sosadad0d322011-01-31 16:37:33 -0800118# ======================= End Global Helper Functions ========================
119
120
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400121def PushChange(stable_branch, tracking_branch, dryrun, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800122 """Pushes commits in the stable_branch to the remote git repository.
123
David Jamesee2da622012-02-23 09:32:16 -0800124 Pushes local commits from calls to CommitChange to the remote git
David James66009462012-03-25 10:08:38 -0700125 repository specified by current working directory. If changes are
126 found to commit, they will be merged to the merge branch and pushed.
127 In that case, the local repository will be left on the merge branch.
Chris Sosadad0d322011-01-31 16:37:33 -0800128
129 Args:
130 stable_branch: The local branch with commits we want to push.
131 tracking_branch: The tracking branch of the local branch.
Chris Sosa62ad8522011-03-08 17:46:17 -0800132 dryrun: Use git push --dryrun to emulate a push.
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400133 cwd: The directory to run commands in.
Chris Sosadad0d322011-01-31 16:37:33 -0800134 Raises:
135 OSError: Error occurred while pushing.
136 """
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400137 if not _DoWeHaveLocalCommits(stable_branch, tracking_branch, cwd):
Brian Harring609dc4e2012-05-07 02:17:44 -0700138 cros_build_lib.Info('No work found to push in %s. Exiting', cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800139 return
140
David James66009462012-03-25 10:08:38 -0700141 # For the commit queue, our local branch may contain commits that were
142 # just tested and pushed during the CommitQueueCompletion stage. Sync
143 # and rebase our local branch on top of the remote commits.
Brian Harring609dc4e2012-05-07 02:17:44 -0700144 remote, push_branch = cros_build_lib.GetTrackingBranch(cwd, for_push=True)
David James66009462012-03-25 10:08:38 -0700145 cros_build_lib.SyncPushBranch(cwd, remote, push_branch)
146
147 # Check whether any local changes remain after the sync.
Brian Harring609dc4e2012-05-07 02:17:44 -0700148 if not _DoWeHaveLocalCommits(stable_branch, push_branch, cwd):
149 cros_build_lib.Info('All changes already pushed for %s. Exiting', cwd)
David James66009462012-03-25 10:08:38 -0700150 return
151
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400152 description = cros_build_lib.RunCommandCaptureOutput(
Brian Harring609dc4e2012-05-07 02:17:44 -0700153 ['git', 'log', '--format=format:%s%n%n%b', '%s..%s' % (
154 push_branch, stable_branch)], cwd=cwd).output
Chris Sosadad0d322011-01-31 16:37:33 -0800155 description = 'Marking set of ebuilds as stable\n\n%s' % description
Brian Harring609dc4e2012-05-07 02:17:44 -0700156 cros_build_lib.Info('For %s, using description %s', cwd, description)
David James66009462012-03-25 10:08:38 -0700157 cros_build_lib.CreatePushBranch(constants.MERGE_BRANCH, cwd)
Brian Harring609dc4e2012-05-07 02:17:44 -0700158 cros_build_lib.RunGitCommand(cwd, ['merge', '--squash', stable_branch])
159 cros_build_lib.RunGitCommand(cwd, ['commit', '-m', description])
160 cros_build_lib.RunGitCommand(cwd, ['config', 'push.default', 'tracking'])
161 cros_build_lib.GitPushWithRetry(constants.MERGE_BRANCH, cwd,
Ryan Cui05a31ba2011-05-31 17:47:37 -0700162 dryrun=dryrun)
Chris Sosadad0d322011-01-31 16:37:33 -0800163
164
165class GitBranch(object):
166 """Wrapper class for a git branch."""
167
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400168 def __init__(self, branch_name, tracking_branch, cwd):
Chris Sosadad0d322011-01-31 16:37:33 -0800169 """Sets up variables but does not create the branch."""
170 self.branch_name = branch_name
171 self.tracking_branch = tracking_branch
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400172 self.cwd = cwd
Chris Sosadad0d322011-01-31 16:37:33 -0800173
174 def CreateBranch(self):
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400175 self.Checkout()
Chris Sosadad0d322011-01-31 16:37:33 -0800176
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400177 def Checkout(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800178 """Function used to check out to another GitBranch."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400179 if not branch:
180 branch = self.branch_name
181 if branch == self.tracking_branch or self.Exists(branch):
182 git_cmd = ['git', 'checkout', '-f', branch]
Chris Sosadad0d322011-01-31 16:37:33 -0800183 else:
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400184 git_cmd = ['repo', 'start', branch, '.']
185 cros_build_lib.RunCommandCaptureOutput(git_cmd, print_cmd=False,
186 cwd=self.cwd)
Chris Sosadad0d322011-01-31 16:37:33 -0800187
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400188 def Exists(self, branch=None):
Chris Sosadad0d322011-01-31 16:37:33 -0800189 """Returns True if the branch exists."""
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400190 if not branch:
191 branch = self.branch_name
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400192 branches = cros_build_lib.RunCommandCaptureOutput(['git', 'branch'],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400193 print_cmd=False,
194 cwd=self.cwd).output
195 return branch in branches.split()
Chris Sosadad0d322011-01-31 16:37:33 -0800196
197
Mike Frysinger368bbb72012-05-23 15:57:10 -0400198def main(argv):
Chris Sosa62ad8522011-03-08 17:46:17 -0800199 parser = optparse.OptionParser('cros_mark_as_stable OPTIONS packages')
200 parser.add_option('--all', action='store_true',
201 help='Mark all packages as stable.')
David Jamescc09c9b2012-01-26 22:10:13 -0800202 parser.add_option('-b', '--boards',
203 help='Colon-separated list of boards')
Chris Sosa62ad8522011-03-08 17:46:17 -0800204 parser.add_option('--drop_file',
205 help='File to list packages that were revved.')
206 parser.add_option('--dryrun', action='store_true',
207 help='Passes dry-run to git push if pushing a change.')
208 parser.add_option('-o', '--overlays',
209 help='Colon-separated list of overlays to modify.')
210 parser.add_option('-p', '--packages',
211 help='Colon separated list of packages to rev.')
212 parser.add_option('-r', '--srcroot',
213 default='%s/trunk/src' % os.environ['HOME'],
214 help='Path to root src directory.')
Chris Sosa62ad8522011-03-08 17:46:17 -0800215 parser.add_option('--verbose', action='store_true',
216 help='Prints out debug info.')
217 (options, args) = parser.parse_args()
Chris Sosadad0d322011-01-31 16:37:33 -0800218
Chris Sosa62ad8522011-03-08 17:46:17 -0800219 global VERBOSE
220 VERBOSE = options.verbose
J. Richard Barnette2fa9fd62011-12-02 17:10:10 -0800221 portage_utilities.EBuild.VERBOSE = options.verbose
Chris Sosa62ad8522011-03-08 17:46:17 -0800222
223 if len(args) != 1:
Ryan Cui05a31ba2011-05-31 17:47:37 -0700224 _PrintUsageAndDie('Must specify a valid command [commit, push]')
Chris Sosa62ad8522011-03-08 17:46:17 -0800225
226 command = args[0]
227 package_list = None
228 if options.packages:
229 package_list = options.packages.split(':')
230
231 _CheckSaneArguments(package_list, command, options)
232 if options.overlays:
Chris Sosadad0d322011-01-31 16:37:33 -0800233 overlays = {}
Chris Sosa62ad8522011-03-08 17:46:17 -0800234 for path in options.overlays.split(':'):
Ryan Cui05a31ba2011-05-31 17:47:37 -0700235 if not os.path.isdir(path):
Chris Sosac13bba52011-05-24 15:14:09 -0700236 cros_build_lib.Die('Cannot find overlay: %s' % path)
Chris Sosadad0d322011-01-31 16:37:33 -0800237 overlays[path] = []
238 else:
Chris Sosac13bba52011-05-24 15:14:09 -0700239 cros_build_lib.Warning('Missing --overlays argument')
Chris Sosadad0d322011-01-31 16:37:33 -0800240 overlays = {
Chris Sosa62ad8522011-03-08 17:46:17 -0800241 '%s/private-overlays/chromeos-overlay' % options.srcroot: [],
242 '%s/third_party/chromiumos-overlay' % options.srcroot: []
Chris Sosadad0d322011-01-31 16:37:33 -0800243 }
244
245 if command == 'commit':
J. Richard Barnettef6697cf2011-11-18 12:42:08 -0800246 portage_utilities.BuildEBuildDictionary(
J. Richard Barnetted422f622011-11-17 09:39:46 -0800247 overlays, options.all, package_list)
Chris Sosadad0d322011-01-31 16:37:33 -0800248
Brian Harring609dc4e2012-05-07 02:17:44 -0700249 manifest = cros_build_lib.ManifestCheckout.Cached(options.srcroot)
Ryan Cui4656a3c2011-05-24 12:30:30 -0700250
David Jamesa8457b52011-05-28 00:03:20 -0700251 # Contains the array of packages we actually revved.
252 revved_packages = []
253 new_package_atoms = []
254
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400255 # Slight optimization hack: process the chromiumos overlay before any other
256 # cros-workon overlay first so we can do background cache generation in it.
257 # A perfect solution would walk all the overlays, figure out any dependencies
258 # between them (with layout.conf), and then process them in dependency order.
259 # However, this operation isn't slow enough to warrant that level of
260 # complexity, so we'll just special case the main overlay.
261 #
262 # Similarly, generate the cache in the portage-stable tree asap. We know
263 # we won't have any cros-workon packages in there, so generating the cache
264 # is the only thing it'll be doing. The chromiumos overlay instead might
265 # have revbumping to do before it can generate the cache.
Mike Frysinger110750a2012-03-26 14:19:20 -0400266 keys = overlays.keys()
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400267 for overlay in ('/third_party/chromiumos-overlay',
268 '/third_party/portage-stable'):
269 for k in keys:
270 if k.endswith(overlay):
271 keys.remove(k)
272 keys.insert(0, k)
273 break
Chris Sosadad0d322011-01-31 16:37:33 -0800274
Mike Frysinger110750a2012-03-26 14:19:20 -0400275 cache_queue = multiprocessing.Queue()
276 with background.BackgroundTaskRunner(cache_queue,
277 portage_utilities.RegenCache):
278 for overlay in keys:
279 ebuilds = overlays[overlay]
280 if not os.path.isdir(overlay):
281 cros_build_lib.Warning("Skipping %s" % overlay)
282 continue
Chris Sosadad0d322011-01-31 16:37:33 -0800283
Brian Harring609dc4e2012-05-07 02:17:44 -0700284 tracking_branch = cros_build_lib.GetTrackingBranchViaManifest(
285 overlay, manifest=manifest, for_push=True)[1]
286
Mike Frysinger110750a2012-03-26 14:19:20 -0400287 if command == 'push':
288 PushChange(constants.STABLE_EBUILD_BRANCH, tracking_branch,
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400289 options.dryrun, cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400290 elif command == 'commit' and ebuilds:
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400291 existing_branch = cros_build_lib.GetCurrentBranch(overlay)
292 work_branch = GitBranch(constants.STABLE_EBUILD_BRANCH, tracking_branch,
293 cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400294 work_branch.CreateBranch()
295 if not work_branch.Exists():
296 cros_build_lib.Die('Unable to create stabilizing branch in %s' %
297 overlay)
Ryan Cuif0d57b42011-07-27 17:43:42 -0700298
Mike Frysinger110750a2012-03-26 14:19:20 -0400299 # In the case of uprevving overlays that have patches applied to them,
300 # include the patched changes in the stabilizing branch.
301 if existing_branch:
Mike Frysinger7dafd0e2012-05-08 15:47:16 -0400302 cros_build_lib.RunCommand(['git', 'rebase', existing_branch],
Mike Frysinger2ebe3732012-05-08 17:04:12 -0400303 print_cmd=False, cwd=overlay)
Mike Frysinger110750a2012-03-26 14:19:20 -0400304
305 for ebuild in ebuilds:
306 try:
307 _Print('Working on %s' % ebuild.package)
308 new_package = ebuild.RevWorkOnEBuild(options.srcroot)
309 if new_package:
310 revved_packages.append(ebuild.package)
311 new_package_atoms.append('=%s' % new_package)
312 except (OSError, IOError):
313 cros_build_lib.Warning('Cannot rev %s\n' % ebuild.package +
314 'Note you will have to go into %s '
315 'and reset the git repo yourself.' % overlay)
316 raise
317
Mike Frysingerb9bd2f82012-05-08 14:55:23 -0400318 if command == 'commit':
Mike Frysinger110750a2012-03-26 14:19:20 -0400319 # Regenerate caches if need be. We do this all the time to
320 # catch when users make changes without updating cache files.
321 cache_queue.put([overlay])
Chris Sosadad0d322011-01-31 16:37:33 -0800322
David Jamesa8457b52011-05-28 00:03:20 -0700323 if command == 'commit':
David Jamescc09c9b2012-01-26 22:10:13 -0800324 CleanStalePackages(options.boards.split(':'), new_package_atoms)
David Jamesa8457b52011-05-28 00:03:20 -0700325 if options.drop_file:
326 fh = open(options.drop_file, 'w')
327 fh.write(' '.join(revved_packages))
328 fh.close()