OLD | NEW |
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 | 17 |
18 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) | 18 sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) |
19 from cros_build_lib import Info, RunCommand, Warning, Die | 19 from cros_build_lib import Info, RunCommand, Warning, Die |
20 | 20 |
21 | 21 |
22 gflags.DEFINE_string('board', 'x86-generic', | 22 gflags.DEFINE_string('board', 'x86-generic', |
23 'Board for which the package belongs.', short_name='b') | 23 'Board for which the package belongs.', short_name='b') |
24 gflags.DEFINE_string('commit_ids', '', | |
25 """Optional list of commit ids for each package. | |
26 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 empty commit id's.""", | |
29 short_name='i') | |
30 gflags.DEFINE_string('packages', '', | 24 gflags.DEFINE_string('packages', '', |
31 'Space separated list of packages to mark as stable.', | 25 'Space separated list of packages to mark as stable.', |
32 short_name='p') | 26 short_name='p') |
33 gflags.DEFINE_string('push_options', '', | 27 gflags.DEFINE_string('push_options', '', |
34 'Options to use with git-cl push using push command.') | 28 'Options to use with git-cl push using push command.') |
35 gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'], | 29 gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'], |
36 'Path to root src directory.', | 30 'Path to root src directory.', |
37 short_name='r') | 31 short_name='r') |
38 gflags.DEFINE_string('tracking_branch', 'cros/master', | 32 gflags.DEFINE_string('tracking_branch', 'cros/master', |
39 'Used with commit to specify branch to track against.', | 33 'Used with commit to specify branch to track against.', |
(...skipping 24 matching lines...) Expand all Loading... |
64 | 58 |
65 # ======================= Global Helper Functions ======================== | 59 # ======================= Global Helper Functions ======================== |
66 | 60 |
67 | 61 |
68 def _Print(message): | 62 def _Print(message): |
69 """Verbose print function.""" | 63 """Verbose print function.""" |
70 if gflags.FLAGS.verbose: | 64 if gflags.FLAGS.verbose: |
71 Info(message) | 65 Info(message) |
72 | 66 |
73 | 67 |
| 68 def _CleanStalePackages(board, package_array): |
| 69 """Cleans up stale package info from a previous build.""" |
| 70 Info('Cleaning up stale packages %s.' % package_array) |
| 71 unmerge_board_cmd = ['emerge-%s' % board, '--unmerge'] |
| 72 unmerge_board_cmd.extend(package_array) |
| 73 RunCommand(unmerge_board_cmd, env=env) |
| 74 |
| 75 unmerge_host_cmd = ['sudo', 'emerge', '--unmerge'] |
| 76 unmerge_host_cmd.extend(package_array) |
| 77 RunCommand(unmerge_host_cmd, env=env) |
| 78 |
| 79 RunCommand(['eclean-%s' % board, '-d', 'packages'], redirect_stderr=True) |
| 80 RunCommand(['sudo', 'eclean', '-d', 'packages'], redirect_stderr=True) |
| 81 |
| 82 |
74 def _BestEBuild(ebuilds): | 83 def _BestEBuild(ebuilds): |
75 """Returns the newest EBuild from a list of EBuild objects.""" | 84 """Returns the newest EBuild from a list of EBuild objects.""" |
76 from portage.versions import vercmp | 85 from portage.versions import vercmp |
77 winner = ebuilds[0] | 86 winner = ebuilds[0] |
78 for ebuild in ebuilds[1:]: | 87 for ebuild in ebuilds[1:]: |
79 if vercmp(winner.version, ebuild.version) < 0: | 88 if vercmp(winner.version, ebuild.version) < 0: |
80 winner = ebuild | 89 winner = ebuild |
81 return winner | 90 return winner |
82 | 91 |
83 | 92 |
(...skipping 12 matching lines...) Expand all Loading... |
96 if ebuild.is_workon: | 105 if ebuild.is_workon: |
97 workon_dir = True | 106 workon_dir = True |
98 if ebuild.is_stable: | 107 if ebuild.is_stable: |
99 stable_ebuilds.append(ebuild) | 108 stable_ebuilds.append(ebuild) |
100 else: | 109 else: |
101 unstable_ebuilds.append(ebuild) | 110 unstable_ebuilds.append(ebuild) |
102 | 111 |
103 # If we found a workon ebuild in this directory, apply some sanity checks. | 112 # If we found a workon ebuild in this directory, apply some sanity checks. |
104 if workon_dir: | 113 if workon_dir: |
105 if len(unstable_ebuilds) > 1: | 114 if len(unstable_ebuilds) > 1: |
106 Die('Found multiple unstable ebuilds in %s' % root) | 115 Die('Found multiple unstable ebuilds in %s' % os.path.dirname(path)) |
107 if len(stable_ebuilds) > 1: | 116 if len(stable_ebuilds) > 1: |
108 stable_ebuilds = [_BestEBuild(stable_ebuilds)] | 117 stable_ebuilds = [_BestEBuild(stable_ebuilds)] |
109 | 118 |
110 # Print a warning if multiple stable ebuilds are found in the same | 119 # Print a warning if multiple stable ebuilds are found in the same |
111 # directory. Storing multiple stable ebuilds is error-prone because | 120 # directory. Storing multiple stable ebuilds is error-prone because |
112 # the older ebuilds will not get rev'd. | 121 # the older ebuilds will not get rev'd. |
113 # | 122 # |
114 # We make a special exception for x11-drivers/xf86-video-msm for legacy | 123 # We make a special exception for x11-drivers/xf86-video-msm for legacy |
115 # reasons. | 124 # reasons. |
116 if stable_ebuilds[0].package != 'x11-drivers/xf86-video-msm': | 125 if stable_ebuilds[0].package != 'x11-drivers/xf86-video-msm': |
117 Warning('Found multiple stable ebuilds in %s' % root) | 126 Warning('Found multiple stable ebuilds in %s' % os.path.dirname(path)) |
118 | 127 |
119 if not unstable_ebuilds: | 128 if not unstable_ebuilds: |
120 Die('Missing 9999 ebuild in %s' % root) | 129 Die('Missing 9999 ebuild in %s' % os.path.dirname(path)) |
121 if not stable_ebuilds: | 130 if not stable_ebuilds: |
122 Die('Missing stable ebuild in %s' % root) | 131 Die('Missing stable ebuild in %s' % os.path.dirname(path)) |
123 | 132 |
124 if stable_ebuilds: | 133 if stable_ebuilds: |
125 return stable_ebuilds[0] | 134 return stable_ebuilds[0] |
126 else: | 135 else: |
127 return None | 136 return None |
128 | 137 |
129 | 138 |
130 def _BuildEBuildDictionary(overlays, all, packages): | 139 def _BuildEBuildDictionary(overlays, all, packages): |
131 """Build a dictionary of the ebuilds in the specified overlays. | 140 """Build a dictionary of the ebuilds in the specified overlays. |
132 | 141 |
133 overlays: A map which maps overlay directories to arrays of stable EBuilds | 142 overlays: A map which maps overlay directories to arrays of stable EBuilds |
134 inside said directories. | 143 inside said directories. |
135 all: Whether to include all ebuilds in the specified directories. If true, | 144 all: Whether to include all ebuilds in the specified directories. If true, |
136 then we gather all packages in the directories regardless of whether | 145 then we gather all packages in the directories regardless of whether |
137 they are in our set of packages. | 146 they are in our set of packages. |
138 packages: A set of the packages we want to gather. | 147 packages: A set of the packages we want to gather. |
139 """ | 148 """ |
140 for overlay in overlays: | 149 for overlay in overlays: |
141 for root_dir, dirs, files in os.walk(overlay): | 150 for package_dir, dirs, files in os.walk(overlay): |
142 # Add stable ebuilds to overlays[overlay]. | 151 # Add stable ebuilds to overlays[overlay]. |
143 paths = [os.path.join(root_dir, path) for path in files] | 152 paths = [os.path.join(package_dir, path) for path in files] |
144 ebuild = _FindStableEBuilds(paths) | 153 ebuild = _FindStableEBuilds(paths) |
145 | 154 |
146 # If the --all option isn't used, we only want to update packages that | 155 # If the --all option isn't used, we only want to update packages that |
147 # are in packages. | 156 # are in packages. |
148 if ebuild and (all or ebuild.package in packages): | 157 if ebuild and (all or ebuild.package in packages): |
149 overlays[overlay].append(ebuild) | 158 overlays[overlay].append(ebuild) |
150 | 159 |
151 | 160 |
152 def _CheckOnStabilizingBranch(): | 161 def _CheckOnStabilizingBranch(): |
153 """Returns true if the git branch is on the stabilizing branch.""" | 162 """Returns true if the git branch is on the stabilizing branch.""" |
(...skipping 173 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 srcdir = os.path.join(srcroot, 'third_party/kernel/files') | 336 srcdir = os.path.join(srcroot, 'third_party/kernel/files') |
328 | 337 |
329 if not os.path.exists(srcdir): | 338 if not os.path.exists(srcdir): |
330 Die('Cannot find commit id for %s' % self.ebuild_path) | 339 Die('Cannot find commit id for %s' % self.ebuild_path) |
331 | 340 |
332 # Verify that we're grabbing the commit id from the right project name. | 341 # Verify that we're grabbing the commit id from the right project name. |
333 # NOTE: chromeos-kernel has the wrong project name, so it fails this | 342 # NOTE: chromeos-kernel has the wrong project name, so it fails this |
334 # check. | 343 # check. |
335 # TODO(davidjames): Fix the project name in the chromeos-kernel ebuild. | 344 # TODO(davidjames): Fix the project name in the chromeos-kernel ebuild. |
336 cmd = 'cd %s && git config --get remote.cros.projectname' % srcdir | 345 cmd = 'cd %s && git config --get remote.cros.projectname' % srcdir |
337 actual_project =_SimpleRunCommand(cmd).rstrip() | 346 actual_project = _SimpleRunCommand(cmd).rstrip() |
338 if project not in (actual_project, 'chromeos-kernel'): | 347 if project not in (actual_project, 'chromeos-kernel'): |
339 Die('Project name mismatch for %s (%s != %s)' % (unstable_ebuild, project, | 348 Die('Project name mismatch for %s (%s != %s)' % (unstable_ebuild, project, |
340 actual_project)) | 349 actual_project)) |
341 | 350 |
342 # Get commit id. | 351 # Get commit id. |
343 output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir) | 352 output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir) |
344 if not output: | 353 if not output: |
345 Die('Missing commit id for %s' % self.ebuild_path) | 354 Die('Missing commit id for %s' % self.ebuild_path) |
346 return output.rstrip() | 355 return output.rstrip() |
347 | 356 |
(...skipping 21 matching lines...) Expand all Loading... |
369 revision = int(rev_string) | 378 revision = int(rev_string) |
370 return (ebuild_no_rev, ebuild_no_version, revision) | 379 return (ebuild_no_rev, ebuild_no_version, revision) |
371 | 380 |
372 | 381 |
373 class EBuildStableMarker(object): | 382 class EBuildStableMarker(object): |
374 """Class that revs the ebuild and commits locally or pushes the change.""" | 383 """Class that revs the ebuild and commits locally or pushes the change.""" |
375 | 384 |
376 def __init__(self, ebuild): | 385 def __init__(self, ebuild): |
377 self._ebuild = ebuild | 386 self._ebuild = ebuild |
378 | 387 |
379 def RevEBuild(self, commit_id="", redirect_file=None): | 388 def RevEBuild(self, commit_id='', redirect_file=None): |
380 """Revs an ebuild given the git commit id. | 389 """Revs an ebuild given the git commit id. |
381 | 390 |
382 By default this class overwrites a new ebuild given the normal | 391 By default this class overwrites a new ebuild given the normal |
383 ebuild rev'ing logic. However, a user can specify a redirect_file | 392 ebuild rev'ing logic. However, a user can specify a redirect_file |
384 to redirect the new stable ebuild to another file. | 393 to redirect the new stable ebuild to another file. |
385 | 394 |
386 Args: | 395 Args: |
387 commit_id: String corresponding to the commit hash of the developer | 396 commit_id: String corresponding to the commit hash of the developer |
388 package to rev. | 397 package to rev. |
389 redirect_file: Optional file to write the new ebuild. By default | 398 redirect_file: Optional file to write the new ebuild. By default |
390 it is written using the standard rev'ing logic. This file must be | 399 it is written using the standard rev'ing logic. This file must be |
391 opened and closed by the caller. | 400 opened and closed by the caller. |
392 | 401 |
393 Raises: | 402 Raises: |
394 OSError: Error occurred while creating a new ebuild. | 403 OSError: Error occurred while creating a new ebuild. |
395 IOError: Error occurred while writing to the new revved ebuild file. | 404 IOError: Error occurred while writing to the new revved ebuild file. |
| 405 Returns: |
| 406 True if the revved package is different than the old ebuild. |
396 """ | 407 """ |
397 # TODO(sosa): Change to a check. | 408 # TODO(sosa): Change to a check. |
398 if not self._ebuild: | 409 if not self._ebuild: |
399 Die('Invalid ebuild given to EBuildStableMarker') | 410 Die('Invalid ebuild given to EBuildStableMarker') |
400 | 411 |
401 new_ebuild_path = '%s-r%d.ebuild' % (self._ebuild.ebuild_path_no_revision, | 412 new_ebuild_path = '%s-r%d.ebuild' % (self._ebuild.ebuild_path_no_revision, |
402 self._ebuild.current_revision + 1) | 413 self._ebuild.current_revision + 1) |
403 | 414 |
404 _Print('Creating new stable ebuild %s' % new_ebuild_path) | 415 _Print('Creating new stable ebuild %s' % new_ebuild_path) |
405 workon_ebuild = '%s-9999.ebuild' % self._ebuild.ebuild_path_no_version | 416 workon_ebuild = '%s-9999.ebuild' % self._ebuild.ebuild_path_no_version |
406 if not os.path.exists(workon_ebuild): | 417 if not os.path.exists(workon_ebuild): |
407 Die('Missing 9999 ebuild: %s' % workon_ebuild) | 418 Die('Missing 9999 ebuild: %s' % workon_ebuild) |
408 shutil.copyfile(workon_ebuild, new_ebuild_path) | 419 shutil.copyfile(workon_ebuild, new_ebuild_path) |
409 | 420 |
410 for line in fileinput.input(new_ebuild_path, inplace=1): | 421 for line in fileinput.input(new_ebuild_path, inplace=1): |
411 # Has to be done here to get changes to sys.stdout from fileinput.input. | 422 # Has to be done here to get changes to sys.stdout from fileinput.input. |
412 if not redirect_file: | 423 if not redirect_file: |
413 redirect_file = sys.stdout | 424 redirect_file = sys.stdout |
414 if line.startswith('KEYWORDS'): | 425 if line.startswith('KEYWORDS'): |
415 # Actually mark this file as stable by removing ~'s. | 426 # Actually mark this file as stable by removing ~'s. |
416 redirect_file.write(line.replace("~", "")) | 427 redirect_file.write(line.replace('~', '')) |
417 elif line.startswith('EAPI'): | 428 elif line.startswith('EAPI'): |
418 # Always add new commit_id after EAPI definition. | 429 # Always add new commit_id after EAPI definition. |
419 redirect_file.write(line) | 430 redirect_file.write(line) |
420 redirect_file.write('CROS_WORKON_COMMIT="%s"\n' % commit_id) | 431 redirect_file.write('CROS_WORKON_COMMIT="%s"\n' % commit_id) |
421 elif not line.startswith('CROS_WORKON_COMMIT'): | 432 elif not line.startswith('CROS_WORKON_COMMIT'): |
422 # Skip old CROS_WORKON_COMMIT definition. | 433 # Skip old CROS_WORKON_COMMIT definition. |
423 redirect_file.write(line) | 434 redirect_file.write(line) |
424 fileinput.close() | 435 fileinput.close() |
425 | 436 |
426 # If the new ebuild is identical to the old ebuild, return False and | |
427 # delete our changes. | |
428 old_ebuild_path = self._ebuild.ebuild_path | 437 old_ebuild_path = self._ebuild.ebuild_path |
429 diff_cmd = ['diff', '-Bu', old_ebuild_path, new_ebuild_path] | 438 diff_cmd = ['diff', '-Bu', old_ebuild_path, new_ebuild_path] |
430 if 0 == RunCommand(diff_cmd, exit_code=True, | 439 if 0 == RunCommand(diff_cmd, exit_code=True, redirect_stdout=True, |
431 print_cmd=gflags.FLAGS.verbose): | 440 redirect_stderr=True, print_cmd=gflags.FLAGS.verbose): |
432 os.unlink(new_ebuild_path) | 441 os.unlink(new_ebuild_path) |
433 return False | 442 return False |
434 else: | 443 else: |
435 _Print('Adding new stable ebuild to git') | 444 _Print('Adding new stable ebuild to git') |
436 _SimpleRunCommand('git add %s' % new_ebuild_path) | 445 _SimpleRunCommand('git add %s' % new_ebuild_path) |
437 | 446 |
438 _Print('Removing old ebuild from git') | 447 _Print('Removing old ebuild from git') |
439 _SimpleRunCommand('git rm %s' % old_ebuild_path) | 448 _SimpleRunCommand('git rm %s' % old_ebuild_path) |
440 return True | 449 return True |
441 | 450 |
(...skipping 25 matching lines...) Expand all Loading... |
467 except gflags.FlagsError, e : | 476 except gflags.FlagsError, e : |
468 _PrintUsageAndDie(str(e)) | 477 _PrintUsageAndDie(str(e)) |
469 | 478 |
470 package_list = gflags.FLAGS.packages.split() | 479 package_list = gflags.FLAGS.packages.split() |
471 _CheckSaneArguments(package_list, command) | 480 _CheckSaneArguments(package_list, command) |
472 | 481 |
473 overlays = { | 482 overlays = { |
474 '%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [], | 483 '%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [], |
475 '%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: [] | 484 '%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: [] |
476 } | 485 } |
477 all = gflags.FLAGS.all | |
478 | 486 |
479 if command == 'commit': | 487 if command == 'commit': |
480 _BuildEBuildDictionary(overlays, all, package_list) | 488 _BuildEBuildDictionary(overlays, gflags.FLAGS.all, package_list) |
481 | 489 |
482 for overlay, ebuilds in overlays.items(): | 490 for overlay, ebuilds in overlays.items(): |
483 if not os.path.exists(overlay): | 491 if not os.path.exists(overlay): continue |
484 continue | |
485 os.chdir(overlay) | 492 os.chdir(overlay) |
486 | 493 |
487 if command == 'clean': | 494 if command == 'clean': |
488 _Clean() | 495 _Clean() |
489 elif command == 'push': | 496 elif command == 'push': |
490 _PushChange() | 497 _PushChange() |
491 elif command == 'commit' and ebuilds: | 498 elif command == 'commit' and ebuilds: |
| 499 work_branch = _GitBranch(_STABLE_BRANCH_NAME) |
| 500 work_branch.CreateBranch() |
| 501 if not work_branch.Exists(): |
| 502 Die('Unable to create stabilizing branch in %s' % overlay) |
| 503 |
| 504 # Contains the array of packages we actually revved. |
| 505 revved_packages = [] |
492 for ebuild in ebuilds: | 506 for ebuild in ebuilds: |
493 try: | 507 try: |
494 _Print('Working on %s' % ebuild.package) | 508 _Print('Working on %s' % ebuild.package) |
495 worker = EBuildStableMarker(ebuild) | 509 worker = EBuildStableMarker(ebuild) |
496 commit_id = ebuild.GetCommitId() | 510 commit_id = ebuild.GetCommitId() |
497 if worker.RevEBuild(commit_id): | 511 if worker.RevEBuild(commit_id): |
498 if not _CheckOnStabilizingBranch(): | |
499 work_branch = _GitBranch(_STABLE_BRANCH_NAME) | |
500 work_branch.CreateBranch() | |
501 if not work_branch.Exists(): | |
502 Die('Unable to create stabilizing branch in %s' % overlay) | |
503 message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id) | 512 message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id) |
504 worker.CommitChange(message) | 513 worker.CommitChange(message) |
| 514 revved_packages.append(ebuild.package) |
| 515 |
505 except (OSError, IOError): | 516 except (OSError, IOError): |
506 Warning('Cannot rev %s\n' % ebuild.package, | 517 Warning('Cannot rev %s\n' % ebuild.package, |
507 'Note you will have to go into %s ' | 518 'Note you will have to go into %s ' |
508 'and reset the git repo yourself.' % overlay) | 519 'and reset the git repo yourself.' % overlay) |
509 raise | 520 raise |
510 | 521 |
| 522 if revved_packages: |
| 523 _CleanStalePackages(gflags.FLAGS.board, revved_packages) |
| 524 else: |
| 525 work_branch.Delete() |
| 526 |
511 | 527 |
512 if __name__ == '__main__': | 528 if __name__ == '__main__': |
513 main(sys.argv) | 529 main(sys.argv) |
514 | |
OLD | NEW |