Index: cros_mark_as_stable.py |
diff --git a/cros_mark_as_stable.py b/cros_mark_as_stable.py |
index 9e5666e612496f79984031b9e907579e2e94cf8b..c302969e9148ffc0e710b7319cef149f0df9246c 100755 |
--- a/cros_mark_as_stable.py |
+++ b/cros_mark_as_stable.py |
@@ -14,9 +14,10 @@ import re |
import shutil |
import subprocess |
import sys |
+from portage.versions import pkgsplit, pkgsplit, vercmp |
sys.path.append(os.path.join(os.path.dirname(__file__), 'lib')) |
-from cros_build_lib import Info, Warning, Die |
+from cros_build_lib import Info, RunCommand, Warning, Die |
gflags.DEFINE_string('board', 'x86-generic', |
@@ -38,6 +39,8 @@ gflags.DEFINE_string('srcroot', '%s/trunk/src' % os.environ['HOME'], |
gflags.DEFINE_string('tracking_branch', 'cros/master', |
'Used with commit to specify branch to track against.', |
short_name='t') |
+gflags.DEFINE_boolean('all', False, |
+ 'Mark all packages as stable.') |
gflags.DEFINE_boolean('verbose', False, |
'Prints out verbose information about what is going on.', |
short_name='v') |
@@ -69,22 +72,81 @@ def _Print(message): |
Info(message) |
-def _BuildEBuildDictionary(overlays, package_list, commit_id_list): |
- for index in range(len(package_list)): |
- package = package_list[index] |
- commit_id = '' |
- if commit_id_list: |
- commit_id = commit_id_list[index] |
- ebuild = _EBuild(package, commit_id) |
- if ebuild.ebuild_path: |
- for overlay in overlays: |
- if ebuild.ebuild_path.startswith(overlay): |
- overlays[overlay].append(ebuild) |
- break |
- else: |
- Die('No overlay found for %s' % ebuild.ebuild_path) |
- else: |
- Die('No ebuild found for %s' % package) |
+def _BestEBuild(ebuilds): |
+ """Returns the newest EBuild from a list of EBuild objects.""" |
+ winner = ebuilds[0] |
+ for ebuild in ebuilds[1:]: |
+ if vercmp(winner.version, ebuild.version) < 0: |
+ winner = ebuild |
+ return winner |
+ |
+ |
+def _FindStableEBuilds(files): |
+ """Return a list of stable ebuilds from specified list of files. |
+ |
+ Args: |
+ files: List of files. |
+ """ |
+ workon_dir = False |
+ stable_ebuilds = [] |
+ unstable_ebuilds = [] |
+ for path in files: |
+ if path.endswith('.ebuild') and not os.path.islink(path): |
+ ebuild = _EBuild(path) |
+ if ebuild.is_workon: |
+ workon_dir = True |
+ if ebuild.is_stable: |
+ stable_ebuilds.append(ebuild) |
+ else: |
+ unstable_ebuilds.append(ebuild) |
+ |
+ # If we found a workon ebuild in this directory, apply some sanity checks. |
+ if workon_dir: |
+ if len(unstable_ebuilds) > 1: |
+ Die('Found multiple unstable ebuilds in %s' % root) |
+ if len(stable_ebuilds) > 1: |
+ stable_ebuilds = [_BestEBuild(stable_ebuilds)] |
+ |
+ # Print a warning if multiple stable ebuilds are found in the same |
+ # directory. Storing multiple stable ebuilds is error-prone because |
+ # the older ebuilds will not get rev'd. |
+ # |
+ # We make a special exception for x11-drivers/xf86-video-msm for legacy |
+ # reasons. |
+ if stable_ebuilds[0].package != 'x11-drivers/xf86-video-msm': |
+ Warning('Found multiple stable ebuilds in %s' % root) |
+ |
+ if not unstable_ebuilds: |
+ Die('Missing 9999 ebuild in %s' % root) |
+ if not stable_ebuilds: |
+ Die('Missing stable ebuild in %s' % root) |
+ |
+ if stable_ebuilds: |
+ return stable_ebuilds[0] |
+ else: |
+ return None |
+ |
+ |
+def _BuildEBuildDictionary(overlays, all, packages): |
+ """Build a dictionary of the ebuilds in the specified overlays. |
+ |
+ overlays: A map which maps overlay directories to arrays of stable EBuilds |
+ inside said directories. |
+ all: Whether to include all ebuilds in the specified directories. If true, |
+ then we gather all packages in the directories regardless of whether |
+ they are in our set of packages. |
+ packages: A set of the packages we want to gather. |
+ """ |
+ for overlay in overlays: |
+ for root_dir, dirs, files in os.walk(overlay): |
+ # Add stable ebuilds to overlays[overlay]. |
+ paths = [os.path.join(root_dir, path) for path in files] |
+ ebuild = _FindStableEBuilds(paths) |
+ |
+ # If the --all option isn't used, we only want to update packages that |
+ # are in packages. |
+ if ebuild and (all or ebuild.package in packages): |
+ overlays[overlay].append(ebuild) |
def _CheckOnStabilizingBranch(): |
@@ -93,19 +155,16 @@ def _CheckOnStabilizingBranch(): |
return current_branch == _STABLE_BRANCH_NAME |
-def _CheckSaneArguments(package_list, commit_id_list, command): |
+def _CheckSaneArguments(package_list, command): |
"""Checks to make sure the flags are sane. Dies if arguments are not sane.""" |
if not command in _COMMAND_DICTIONARY.keys(): |
_PrintUsageAndDie('%s is not a valid command' % command) |
- if not gflags.FLAGS.packages and command == 'commit': |
+ if not gflags.FLAGS.packages and command == 'commit' and not gflags.FLAGS.all: |
_PrintUsageAndDie('Please specify at least one package') |
if not gflags.FLAGS.board and command == 'commit': |
_PrintUsageAndDie('Please specify a board') |
if not os.path.isdir(gflags.FLAGS.srcroot): |
_PrintUsageAndDie('srcroot is not a valid path') |
- if commit_id_list and (len(package_list) != len(commit_id_list)): |
- _PrintUsageAndDie( |
- 'Package list is not the same length as the commit id list') |
def _Clean(): |
@@ -214,30 +273,76 @@ class _GitBranch(object): |
class _EBuild(object): |
"""Wrapper class for an ebuild.""" |
- def __init__(self, package, commit_id=None): |
+ def __init__(self, path): |
"""Initializes all data about an ebuild. |
Uses equery to find the ebuild path and sets data about an ebuild for |
easy reference. |
""" |
- self.package = package |
- self.ebuild_path = self._FindEBuildPath(package) |
+ self.ebuild_path = path |
(self.ebuild_path_no_revision, |
self.ebuild_path_no_version, |
self.current_revision) = self._ParseEBuildPath(self.ebuild_path) |
- self.commit_id = commit_id |
+ _, self.category, pkgpath, filename = path.rsplit('/', 3) |
+ filename_no_suffix = os.path.join(filename.replace('.ebuild', '')) |
+ self.pkgname, version_no_rev, rev = pkgsplit(filename_no_suffix) |
+ self.version = '%s-%s' % (version_no_rev, rev) |
+ self.package = '%s/%s' % (self.category, self.pkgname) |
+ self.is_workon = False |
+ self.is_stable = False |
+ |
+ for line in fileinput.input(path): |
+ if line.startswith('inherit ') and 'cros-workon' in line: |
+ self.is_workon = True |
+ elif (line.startswith('KEYWORDS=') and '~' not in line and |
+ ('amd64' in line or 'x86' in line or 'arm' in line)): |
+ self.is_stable = True |
+ fileinput.close() |
- @classmethod |
- def _FindEBuildPath(cls, package): |
- """Static method that returns the full path of an ebuild.""" |
- _Print('Looking for unstable ebuild for %s' % package) |
- equery_cmd = ( |
- 'ACCEPT_KEYWORDS="x86 arm amd64" equery-%s which %s 2> /dev/null' |
- % (gflags.FLAGS.board, package)) |
- path = _SimpleRunCommand(equery_cmd) |
- if path: |
- _Print('Unstable ebuild found at %s' % path) |
- return path.rstrip() |
+ def GetCommitId(self): |
+ """Get the commit id for this ebuild.""" |
+ |
+ # Grab and evaluate CROS_WORKON variables from this ebuild. |
+ unstable_ebuild = '%s-9999.ebuild' % self.ebuild_path_no_version |
+ cmd = ('CROS_WORKON_LOCALNAME="%s" CROS_WORKON_PROJECT="%s" ' |
+ 'eval $(grep -E "^CROS_WORKON" %s) && ' |
+ 'echo $CROS_WORKON_PROJECT ' |
+ '$CROS_WORKON_LOCALNAME/$CROS_WORKON_SUBDIR' |
+ % (self.pkgname, self.pkgname, unstable_ebuild)) |
+ project, subdir = _SimpleRunCommand(cmd).split() |
+ |
+ # Calculate srcdir. |
+ srcroot = gflags.FLAGS.srcroot |
+ if self.category == 'chromeos-base': |
+ dir = 'platform' |
+ else: |
+ dir = 'third_party' |
+ srcdir = os.path.join(srcroot, dir, subdir) |
+ |
+ # TODO(anush): This hack is only necessary because the kernel ebuild has |
+ # 'if' statements, so we can't grab the CROS_WORKON_LOCALNAME properly. |
+ # We should clean up the kernel ebuild and remove this hack. |
+ if not os.path.exists(srcdir) and subdir == 'kernel/': |
+ srcdir = os.path.join(srcroot, 'third_party/kernel/files') |
+ |
+ if not os.path.exists(srcdir): |
+ Die('Cannot find commit id for %s' % self.ebuild_path) |
+ |
+ # Verify that we're grabbing the commit id from the right project name. |
+ # NOTE: chromeos-kernel has the wrong project name, so it fails this |
+ # check. |
+ # TODO(davidjames): Fix the project name in the chromeos-kernel ebuild. |
+ cmd = 'cd %s && git config --get remote.cros.projectname' % srcdir |
+ actual_project =_SimpleRunCommand(cmd).rstrip() |
+ if project not in (actual_project, 'chromeos-kernel'): |
+ Die('Project name mismatch for %s (%s != %s)' % (unstable_ebuild, project, |
+ actual_project)) |
+ |
+ # Get commit id. |
+ output = _SimpleRunCommand('cd %s && git rev-parse HEAD' % srcdir) |
+ if not output: |
+ Die('Missing commit id for %s' % self.ebuild_path) |
+ return output.rstrip() |
@classmethod |
def _ParseEBuildPath(cls, ebuild_path): |
@@ -317,11 +422,21 @@ class EBuildStableMarker(object): |
redirect_file.write(line) |
fileinput.close() |
- _Print('Adding new stable ebuild to git') |
- _SimpleRunCommand('git add %s' % new_ebuild_path) |
+ # If the new ebuild is identical to the old ebuild, return False and |
+ # delete our changes. |
+ old_ebuild_path = self._ebuild.ebuild_path |
+ diff_cmd = ['diff', '-Bu', old_ebuild_path, new_ebuild_path] |
+ if 0 == RunCommand(diff_cmd, exit_code=True, |
+ print_cmd=gflags.FLAGS.verbose): |
+ os.unlink(new_ebuild_path) |
+ return False |
+ else: |
+ _Print('Adding new stable ebuild to git') |
+ _SimpleRunCommand('git add %s' % new_ebuild_path) |
- _Print('Removing old ebuild from git') |
- _SimpleRunCommand('git rm %s' % self._ebuild.ebuild_path) |
+ _Print('Removing old ebuild from git') |
+ _SimpleRunCommand('git rm %s' % old_ebuild_path) |
+ return True |
def CommitChange(self, message): |
"""Commits current changes in git locally. |
@@ -352,17 +467,16 @@ def main(argv): |
_PrintUsageAndDie(str(e)) |
package_list = gflags.FLAGS.packages.split() |
- if gflags.FLAGS.commit_ids: |
- commit_id_list = gflags.FLAGS.commit_ids.split() |
- else: |
- commit_id_list = None |
- _CheckSaneArguments(package_list, commit_id_list, command) |
+ _CheckSaneArguments(package_list, command) |
overlays = { |
'%s/private-overlays/chromeos-overlay' % gflags.FLAGS.srcroot: [], |
'%s/third_party/chromiumos-overlay' % gflags.FLAGS.srcroot: [] |
} |
- _BuildEBuildDictionary(overlays, package_list, commit_id_list) |
+ all = gflags.FLAGS.all |
+ |
+ if command == 'commit': |
+ _BuildEBuildDictionary(overlays, all, package_list) |
for overlay, ebuilds in overlays.items(): |
if not os.path.exists(overlay): |
@@ -374,17 +488,19 @@ def main(argv): |
elif command == 'push': |
_PushChange() |
elif command == 'commit' and ebuilds: |
- work_branch = _GitBranch(_STABLE_BRANCH_NAME) |
- work_branch.CreateBranch() |
- if not work_branch.Exists(): |
- Die('Unable to create stabilizing branch in %s' % overlay) |
for ebuild in ebuilds: |
try: |
_Print('Working on %s' % ebuild.package) |
worker = EBuildStableMarker(ebuild) |
- worker.RevEBuild(ebuild.commit_id) |
- message = _GIT_COMMIT_MESSAGE % (ebuild.package, ebuild.commit_id) |
- worker.CommitChange(message) |
+ commit_id = ebuild.GetCommitId() |
+ if worker.RevEBuild(commit_id): |
+ if not _CheckOnStabilizingBranch(): |
+ work_branch = _GitBranch(_STABLE_BRANCH_NAME) |
+ work_branch.CreateBranch() |
+ if not work_branch.Exists(): |
+ Die('Unable to create stabilizing branch in %s' % overlay) |
+ message = _GIT_COMMIT_MESSAGE % (ebuild.package, commit_id) |
+ worker.CommitChange(message) |
except (OSError, IOError): |
Warning('Cannot rev %s\n' % ebuild.package, |
'Note you will have to go into %s ' |