Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(210)

Side by Side Diff: cros_mark_as_stable.py

Issue 3798003: Robustify and speed up cros_mark_all_as_stable. (Closed) Base URL: ssh://git@gitrw.chromium.org:9222/crosutils.git
Patch Set: Check no changes Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « cros_mark_all_as_stable ('k') | cros_mark_as_stable_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 2
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style license that can be 4 # Use of this source code is governed by a BSD-style license that can be
5 # found in the LICENSE file. 5 # found in the LICENSE file.
6 6
7 """This module uprevs a given package's ebuild to the next revision.""" 7 """This module uprevs a given package's ebuild to the next revision."""
8 8
9 9
10 import fileinput 10 import fileinput
11 import gflags 11 import gflags
12 import os 12 import os
13 import re 13 import re
14 import shutil 14 import shutil
15 import subprocess 15 import subprocess
16 import sys 16 import sys
17 from portage.versions import pkgsplit, pkgsplit, vercmp
17 18
18 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) 19 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
19 from cros_build_lib import Info, Warning, Die 20 from cros_build_lib import Info, RunCommand, Warning, Die
20 21
21 22
22 gflags.DEFINE_string('board', 'x86-generic', 23 gflags.DEFINE_string('board', 'x86-generic',
23 'Board for which the package belongs.', short_name='b') 24 'Board for which the package belongs.', short_name='b')
24 gflags.DEFINE_string('commit_ids', '', 25 gflags.DEFINE_string('commit_ids', '',
25 """Optional list of commit ids for each package. 26 """Optional list of commit ids for each package.
26 This list must either be empty or have the same length as 27 This list must either be empty or have the same length as
27 the packages list. If not set all rev'd ebuilds will have 28 the packages list. If not set all rev'd ebuilds will have
28 empty commit id's.""", 29 empty commit id's.""",
29 short_name='i') 30 short_name='i')
30 gflags.DEFINE_string('packages', '', 31 gflags.DEFINE_string('packages', '',
31 'Space separated list of packages to mark as stable.', 32 'Space separated list of packages to mark as stable.',
32 short_name='p') 33 short_name='p')
33 gflags.DEFINE_string('push_options', '', 34 gflags.DEFINE_string('push_options', '',
34 'Options to use with git-cl push using push command.') 35 'Options to use with git-cl push using push command.')
35 gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'], 36 gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'],
36 'Path to root src directory.', 37 'Path to root src directory.',
37 short_name='r') 38 short_name='r')
38 gflags.DEFINE_string('tracking_branch', 'cros/master', 39 gflags.DEFINE_string('tracking_branch', 'cros/master',
39 'Used with commit to specify branch to track against.', 40 'Used with commit to specify branch to track against.',
40 short_name='t') 41 short_name='t')
42 gflags.DEFINE_boolean('all', False,
43 'Mark all packages as stable.')
41 gflags.DEFINE_boolean('verbose', False, 44 gflags.DEFINE_boolean('verbose', False,
42 'Prints out verbose information about what is going on.', 45 'Prints out verbose information about what is going on.',
43 short_name='v') 46 short_name='v')
44 47
45 48
46 # Takes two strings, package_name and commit_id. 49 # Takes two strings, package_name and commit_id.
47 _GIT_COMMIT_MESSAGE = \ 50 _GIT_COMMIT_MESSAGE = \
48 'Marking 9999 ebuild for %s with commit %s as stable.' 51 'Marking 9999 ebuild for %s with commit %s as stable.'
49 52
50 # Dictionary of valid commands with usage information. 53 # Dictionary of valid commands with usage information.
(...skipping 11 matching lines...) Expand all
62 65
63 # ======================= Global Helper Functions ======================== 66 # ======================= Global Helper Functions ========================
64 67
65 68
66 def _Print(message): 69 def _Print(message):
67 """Verbose print function.""" 70 """Verbose print function."""
68 if gflags.FLAGS.verbose: 71 if gflags.FLAGS.verbose:
69 Info(message) 72 Info(message)
70 73
71 74
72 def _BuildEBuildDictionary(overlays, package_list, commit_id_list): 75 def _BestEBuild(ebuilds):
73 for index in range(len(package_list)): 76 """Returns the newest EBuild from a list of EBuild objects."""
74 package = package_list[index] 77 winner = ebuilds[0]
75 commit_id = '' 78 for ebuild in ebuilds[1:]:
76 if commit_id_list: 79 if vercmp(winner.version, ebuild.version) < 0:
77 commit_id = commit_id_list[index] 80 winner = ebuild
78 ebuild = _EBuild(package, commit_id) 81 return winner
79 if ebuild.ebuild_path: 82
80 for overlay in overlays: 83
81 if ebuild.ebuild_path.startswith(overlay): 84 def _FindStableEBuilds(files):
82 overlays[overlay].append(ebuild) 85 """Return a list of stable ebuilds from specified list of files.
83 break 86
84 else: 87 Args:
85 Die('No overlay found for %s' % ebuild.ebuild_path) 88 files: List of files.
86 else: 89 """
87 Die('No ebuild found for %s' % package) 90 workon_dir = False
91 stable_ebuilds = []
92 unstable_ebuilds = []
93 for path in files:
94 if path.endswith('.ebuild') and not os.path.islink(path):
95 ebuild = _EBuild(path)
96 if ebuild.is_workon:
97 workon_dir = True
98 if ebuild.is_stable:
99 stable_ebuilds.append(ebuild)
100 else:
101 unstable_ebuilds.append(ebuild)
102
103 # If we found a workon ebuild in this directory, apply some sanity checks.
104 if workon_dir:
105 if len(unstable_ebuilds) > 1:
106 Die('Found multiple unstable ebuilds in %s' % root)
107 if len(stable_ebuilds) > 1:
108 stable_ebuilds = [_BestEBuild(stable_ebuilds)]
109
110 # Print a warning if multiple stable ebuilds are found in the same
111 # directory. Storing multiple stable ebuilds is error-prone because
112 # the older ebuilds will not get rev'd.
113 #
114 # We make a special exception for x11-drivers/xf86-video-msm for legacy
115 # reasons.
116 if stable_ebuilds[0].package != 'x11-drivers/xf86-video-msm':
117 Warning('Found multiple stable ebuilds in %s' % root)
118
119 if not unstable_ebuilds:
120 Die('Missing 9999 ebuild in %s' % root)
121 if not stable_ebuilds:
122 Die('Missing stable ebuild in %s' % root)
123
124 if stable_ebuilds:
125 return stable_ebuilds[0]
126 else:
127 return None
128
129
130 def _BuildEBuildDictionary(overlays, all, packages):
131 """Build a dictionary of the ebuilds in the specified overlays.
132
133 overlays: A map which maps overlay directories to arrays of stable EBuilds
134 inside said directories.
135 all: Whether to include all ebuilds in the specified directories. If true,
136 then we gather all packages in the directories regardless of whether
137 they are in our set of packages.
138 packages: A set of the packages we want to gather.
139 """
140 for overlay in overlays:
141 for root_dir, dirs, files in os.walk(overlay):
142 # Add stable ebuilds to overlays[overlay].
143 paths = [os.path.join(root_dir, path) for path in files]
144 ebuild = _FindStableEBuilds(paths)
145
146 # If the --all option isn't used, we only want to update packages that
147 # are in packages.
148 if ebuild and (all or ebuild.package in packages):
149 overlays[overlay].append(ebuild)
88 150
89 151
90 def _CheckOnStabilizingBranch(): 152 def _CheckOnStabilizingBranch():
91 """Returns true if the git branch is on the stabilizing branch.""" 153 """Returns true if the git branch is on the stabilizing branch."""
92 current_branch = _SimpleRunCommand('git branch | grep \*').split()[1] 154 current_branch = _SimpleRunCommand('git branch | grep \*').split()[1]
93 return current_branch == _STABLE_BRANCH_NAME 155 return current_branch == _STABLE_BRANCH_NAME
94 156
95 157
96 def _CheckSaneArguments(package_list, commit_id_list, command): 158 def _CheckSaneArguments(package_list, command):
97 """Checks to make sure the flags are sane. Dies if arguments are not sane.""" 159 """Checks to make sure the flags are sane. Dies if arguments are not sane."""
98 if not command in _COMMAND_DICTIONARY.keys(): 160 if not command in _COMMAND_DICTIONARY.keys():
99 _PrintUsageAndDie('%s is not a valid command' % command) 161 _PrintUsageAndDie('%s is not a valid command' % command)
100 if not gflags.FLAGS.packages and command == 'commit': 162 if not gflags.FLAGS.packages and command == 'commit' and not gflags.FLAGS.all:
101 _PrintUsageAndDie('Please specify at least one package') 163 _PrintUsageAndDie('Please specify at least one package')
102 if not gflags.FLAGS.board and command == 'commit': 164 if not gflags.FLAGS.board and command == 'commit':
103 _PrintUsageAndDie('Please specify a board') 165 _PrintUsageAndDie('Please specify a board')
104 if not os.path.isdir(gflags.FLAGS.srcroot): 166 if not os.path.isdir(gflags.FLAGS.srcroot):
105 _PrintUsageAndDie('srcroot is not a valid path') 167 _PrintUsageAndDie('srcroot is not a valid path')
106 if commit_id_list and (len(package_list) != len(commit_id_list)):
107 _PrintUsageAndDie(
108 'Package list is not the same length as the commit id list')
109 168
110 169
111 def _Clean(): 170 def _Clean():
112 """Cleans up uncommitted changes on either stabilizing branch or master.""" 171 """Cleans up uncommitted changes on either stabilizing branch or master."""
113 _SimpleRunCommand('git reset HEAD --hard') 172 _SimpleRunCommand('git reset HEAD --hard')
114 _SimpleRunCommand('git checkout %s' % gflags.FLAGS.tracking_branch) 173 _SimpleRunCommand('git checkout %s' % gflags.FLAGS.tracking_branch)
115 174
116 175
117 def _PrintUsageAndDie(error_message=''): 176 def _PrintUsageAndDie(error_message=''):
118 """Prints optional error_message the usage and returns an error exit code.""" 177 """Prints optional error_message the usage and returns an error exit code."""
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
207 Returns True on success. 266 Returns True on success.
208 """ 267 """
209 self._Checkout(gflags.FLAGS.tracking_branch, create=False) 268 self._Checkout(gflags.FLAGS.tracking_branch, create=False)
210 delete_cmd = 'git branch -D %s' % self.branch_name 269 delete_cmd = 'git branch -D %s' % self.branch_name
211 _SimpleRunCommand(delete_cmd) 270 _SimpleRunCommand(delete_cmd)
212 271
213 272
214 class _EBuild(object): 273 class _EBuild(object):
215 """Wrapper class for an ebuild.""" 274 """Wrapper class for an ebuild."""
216 275
217 def __init__(self, package, commit_id=None): 276 def __init__(self, path):
218 """Initializes all data about an ebuild. 277 """Initializes all data about an ebuild.
219 278
220 Uses equery to find the ebuild path and sets data about an ebuild for 279 Uses equery to find the ebuild path and sets data about an ebuild for
221 easy reference. 280 easy reference.
222 """ 281 """
223 self.package = package 282 self.ebuild_path = path
224 self.ebuild_path = self._FindEBuildPath(package)
225 (self.ebuild_path_no_revision, 283 (self.ebuild_path_no_revision,
226 self.ebuild_path_no_version, 284 self.ebuild_path_no_version,
227 self.current_revision) = self._ParseEBuildPath(self.ebuild_path) 285 self.current_revision) = self._ParseEBuildPath(self.ebuild_path)
228 self.commit_id = commit_id 286 _, self.category, pkgpath, filename = path.rsplit('/', 3)
287 filename_no_suffix = os.path.join(filename.replace('.ebuild', ''))
288 self.pkgname, version_no_rev, rev = pkgsplit(filename_no_suffix)
289 self.version = '%s-%s' % (version_no_rev, rev)
290 self.package = '%s/%s' % (self.category, self.pkgname)
291 self.is_workon = False
292 self.is_stable = False
229 293
230 @classmethod 294 for line in fileinput.input(path):
231 def _FindEBuildPath(cls, package): 295 if line.startswith('inherit ') and 'cros-workon' in line:
232 """Static method that returns the full path of an ebuild.""" 296 self.is_workon = True
233 _Print('Looking for unstable ebuild for %s' % package) 297 elif (line.startswith('KEYWORDS=') and '~' not in line and
234 equery_cmd = ( 298 ('amd64' in line or 'x86' in line or 'arm' in line)):
235 'ACCEPT_KEYWORDS="x86 arm amd64" equery-%s which %s 2> /dev/null' 299 self.is_stable = True
236 % (gflags.FLAGS.board, package)) 300 fileinput.close()
237 path = _SimpleRunCommand(equery_cmd) 301
238 if path: 302 def GetCommitId(self):
239 _Print('Unstable ebuild found at %s' % path) 303 """Get the commit id for this ebuild."""
240 return path.rstrip() 304
305 # Grab and evaluate CROS_WORKON variables from this ebuild.
306 unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version
307 cmd = ('CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s" '
308 'eval $(grep -E "^CROS_WORKON" %s) && '
309 'echo $CROS_WORKON_PROJECT '
310 '$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR'
311 % (self.pkgname, self.pkgname, unstable_ebuild))
312 project, subdir = _SimpleRunCommand(cmd).split()
313
314 # Calculate srcdir.
315 srcroot = gflags.FLAGS.srcroot
316 if self.category == 'chromeos-base':
317 dir = 'platform'
318 else:
319 dir = 'third_party'
320 srcdir = os.path.join(srcroot, dir, subdir)
321
322 # TODO(anush): This hack is only necessary because the kernel ebuild has
323 # 'if' statements, so we can't grab the CROS_WORKON_LOCALNAME properly.
324 # We should clean up the kernel ebuild and remove this hack.
325 if not os.path.exists(srcdir) and subdir == 'kernel/':
326 srcdir = os.path.join(srcroot, 'third_party/kernel/files')
327
328 if not os.path.exists(srcdir):
329 Die('Cannot find commit id for %s' % self.ebuild_path)
330
331 # Verify that we're grabbing the commit id from the right project name.
332 # NOTE: chromeos-kernel has the wrong project name, so it fails this
333 # check.
334 # TODO(davidjames): Fix the project name in the chromeos-kernel ebuild.
335 cmd = 'cd %s && git config --get remote.cros.projectname' % srcdir
336 actual_project =_SimpleRunCommand(cmd).rstrip()
337 if project not in (actual_project, 'chromeos-kernel'):
338 Die('Project name mismatch for %s (%s != %s)' % (unstable_ebuild, project,
339 actual_project))
340
341 # Get commit id.
342 output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir)
343 if not output:
344 Die('Missing commit id for %s' % self.ebuild_path)
345 return output.rstrip()
241 346
242 @classmethod 347 @classmethod
243 def _ParseEBuildPath(cls, ebuild_path): 348 def _ParseEBuildPath(cls, ebuild_path):
244 """Static method that parses the path of an ebuild 349 """Static method that parses the path of an ebuild
245 350
246 Returns a tuple containing the (ebuild path without the revision 351 Returns a tuple containing the (ebuild path without the revision
247 string, without the version string, and the current revision number for 352 string, without the version string, and the current revision number for
248 the ebuild). 353 the ebuild).
249 """ 354 """
250 # Get the ebuild name without the revision string. 355 # Get the ebuild name without the revision string.
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 redirect_file.write(line.replace("~", "")) 415 redirect_file.write(line.replace("~", ""))
311 elif line.startswith('EAPI'): 416 elif line.startswith('EAPI'):
312 # Always add new commit_id after EAPI definition. 417 # Always add new commit_id after EAPI definition.
313 redirect_file.write(line) 418 redirect_file.write(line)
314 redirect_file.write('CROS_WORKON_COMMIT="%s"\n' % commit_id) 419 redirect_file.write('CROS_WORKON_COMMIT="%s"\n' % commit_id)
315 elif not line.startswith('CROS_WORKON_COMMIT'): 420 elif not line.startswith('CROS_WORKON_COMMIT'):
316 # Skip old CROS_WORKON_COMMIT definition. 421 # Skip old CROS_WORKON_COMMIT definition.
317 redirect_file.write(line) 422 redirect_file.write(line)
318 fileinput.close() 423 fileinput.close()
319 424
320 _Print('Adding new stable ebuild to git') 425 # If the new ebuild is identical to the old ebuild, return False and
321 _SimpleRunCommand('git add %s' % new_ebuild_path) 426 # delete our changes.
427 old_ebuild_path = self._ebuild.ebuild_path
428 diff_cmd = ['diff', '-Bu', old_ebuild_path, new_ebuild_path]
429 if 0 == RunCommand(diff_cmd, exit_code=True,
430 print_cmd=gflags.FLAGS.verbose):
431 os.unlink(new_ebuild_path)
432 return False
433 else:
434 _Print('Adding new stable ebuild to git')
435 _SimpleRunCommand('git add %s' % new_ebuild_path)
322 436
323 _Print('Removing old ebuild from git') 437 _Print('Removing old ebuild from git')
324 _SimpleRunCommand('git rm %s' % self._ebuild.ebuild_path) 438 _SimpleRunCommand('git rm %s' % old_ebuild_path)
439 return True
325 440
326 def CommitChange(self, message): 441 def CommitChange(self, message):
327 """Commits current changes in git locally. 442 """Commits current changes in git locally.
328 443
329 This method will take any changes from invocations to RevEBuild 444 This method will take any changes from invocations to RevEBuild
330 and commits them locally in the git repository that contains os.pwd. 445 and commits them locally in the git repository that contains os.pwd.
331 446
332 Args: 447 Args:
333 message: the commit string to write when committing to git. 448 message: the commit string to write when committing to git.
334 449
(...skipping 10 matching lines...) Expand all
345 try: 460 try:
346 argv = gflags.FLAGS(argv) 461 argv = gflags.FLAGS(argv)
347 if len(argv) != 2: 462 if len(argv) != 2:
348 _PrintUsageAndDie('Must specify a valid command') 463 _PrintUsageAndDie('Must specify a valid command')
349 else: 464 else:
350 command = argv[1] 465 command = argv[1]
351 except gflags.FlagsError, e : 466 except gflags.FlagsError, e :
352 _PrintUsageAndDie(str(e)) 467 _PrintUsageAndDie(str(e))
353 468
354 package_list = gflags.FLAGS.packages.split() 469 package_list = gflags.FLAGS.packages.split()
355 if gflags.FLAGS.commit_ids: 470 _CheckSaneArguments(package_list, command)
356 commit_id_list = gflags.FLAGS.commit_ids.split()
357 else:
358 commit_id_list = None
359 _CheckSaneArguments(package_list, commit_id_list, command)
360 471
361 overlays = { 472 overlays = {
362 '%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [], 473 '%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [],
363 '%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: [] 474 '%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: []
364 } 475 }
365 _BuildEBuildDictionary(overlays, package_list, commit_id_list) 476 all = gflags.FLAGS.all
477
478 if command == 'commit':
479 _BuildEBuildDictionary(overlays, all, package_list)
366 480
367 for overlay, ebuilds in overlays.items(): 481 for overlay, ebuilds in overlays.items():
368 if not os.path.exists(overlay): 482 if not os.path.exists(overlay):
369 continue 483 continue
370 os.chdir(overlay) 484 os.chdir(overlay)
371 485
372 if command == 'clean': 486 if command == 'clean':
373 _Clean() 487 _Clean()
374 elif command == 'push': 488 elif command == 'push':
375 _PushChange() 489 _PushChange()
376 elif command == 'commit' and ebuilds: 490 elif command == 'commit' and ebuilds:
377 work_branch = _GitBranch(_STABLE_BRANCH_NAME)
378 work_branch.CreateBranch()
379 if not work_branch.Exists():
380 Die('Unable to create stabilizing branch in %s' % overlay)
381 for ebuild in ebuilds: 491 for ebuild in ebuilds:
382 try: 492 try:
383 _Print('Working on %s' % ebuild.package) 493 _Print('Working on %s' % ebuild.package)
384 worker = EBuildStableMarker(ebuild) 494 worker = EBuildStableMarker(ebuild)
385 worker.RevEBuild(ebuild.commit_id) 495 commit_id = ebuild.GetCommitId()
386 message = _GIT_COMMIT_MESSAGE % (ebuild.package, ebuild.commit_id) 496 if worker.RevEBuild(commit_id):
387 worker.CommitChange(message) 497 if not _CheckOnStabilizingBranch():
498 work_branch = _GitBranch(_STABLE_BRANCH_NAME)
499 work_branch.CreateBranch()
500 if not work_branch.Exists():
501 Die('Unable to create stabilizing branch in %s' % overlay)
502 message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id)
503 worker.CommitChange(message)
388 except (OSError, IOError): 504 except (OSError, IOError):
389 Warning('Cannot rev %s\n' % ebuild.package, 505 Warning('Cannot rev %s\n' % ebuild.package,
390 'Note you will have to go into %s ' 506 'Note you will have to go into %s '
391 'and reset the git repo yourself.' % overlay) 507 'and reset the git repo yourself.' % overlay)
392 raise 508 raise
393 509
394 510
395 if __name__ == '__main__': 511 if __name__ == '__main__':
396 main(sys.argv) 512 main(sys.argv)
397 513
OLDNEW
« no previous file with comments | « cros_mark_all_as_stable ('k') | cros_mark_as_stable_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698