| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 | |
| 7 """Merge Skia into Android.""" | |
| 8 | |
| 9 | |
| 10 import os | |
| 11 import sys | |
| 12 | |
| 13 from build_step import BuildStep, BuildStepFailure, BuildStepWarning | |
| 14 import skia_vars | |
| 15 from sync_android import ANDROID_CHECKOUT_PATH, REPO, GitAuthenticate | |
| 16 from py.utils.git_utils import GIT | |
| 17 from py.utils import git_utils | |
| 18 from py.utils import misc | |
| 19 from py.utils import shell_utils | |
| 20 | |
| 21 SKIA_REPO_URL = skia_vars.GetGlobalVariable('skia_git_url') | |
| 22 SKIA_REV_URL = skia_vars.GetGlobalVariable('revlink_tmpl') | |
| 23 | |
| 24 MASTER_SKIA_URL = ('https://googleplex-android-review.googlesource.com/' | |
| 25 'platform/external/skia') | |
| 26 MASTER_SKIA_REFS = 'HEAD:refs/heads/master-skia' | |
| 27 | |
| 28 UPSTREAM_REMOTE_NAME = 'upstream' | |
| 29 | |
| 30 ANDROID_USER_CONFIG = 'include/core/SkUserConfig.h' | |
| 31 UPSTREAM_USER_CONFIG = 'include/config/SkUserConfig.h' | |
| 32 | |
| 33 EXTERNAL_SKIA = os.path.join(ANDROID_CHECKOUT_PATH, 'external', 'skia') | |
| 34 # Path to gyp_to_android.py. | |
| 35 PLATFORM_TOOLS_BIN = os.path.join(EXTERNAL_SKIA, 'platform_tools', 'android', | |
| 36 'bin') | |
| 37 sys.path.append(PLATFORM_TOOLS_BIN) | |
| 38 import gyp_to_android | |
| 39 | |
| 40 LOCAL_BRANCH_NAME = 'merge' | |
| 41 | |
| 42 | |
| 43 def RepoAbandon(branch): | |
| 44 """Run 'repo abandon <branch>' | |
| 45 | |
| 46 'repo abandon' is similar to 'git branch -D', and is only necessary after | |
| 47 a branch created by 'repo start' is no longer needed.""" | |
| 48 shell_utils.run([REPO, 'abandon', branch]) | |
| 49 | |
| 50 | |
| 51 class MergeIntoAndroid(BuildStep): | |
| 52 """BuildStep which merges Skia into Android, with a generated Android.mk and | |
| 53 SkUserConfig.h""" | |
| 54 | |
| 55 def _Run(self): | |
| 56 with misc.ChDir(EXTERNAL_SKIA): | |
| 57 # Check to see whether there is an upstream yet. | |
| 58 if not UPSTREAM_REMOTE_NAME in shell_utils.run([GIT, 'remote', 'show']): | |
| 59 try: | |
| 60 shell_utils.run([GIT, 'remote', 'add', UPSTREAM_REMOTE_NAME, | |
| 61 SKIA_REPO_URL]) | |
| 62 except shell_utils.CommandFailedException as e: | |
| 63 if 'remote %s already exists' % UPSTREAM_REMOTE_NAME in e.output: | |
| 64 # Accept this error. The upstream remote name should have been in | |
| 65 # the output of git remote show, which would have made us skip this | |
| 66 # redundant command anyway. | |
| 67 print ('%s was already added. Why did it not show in git remote' | |
| 68 ' show?' % UPSTREAM_REMOTE_NAME) | |
| 69 else: | |
| 70 raise e | |
| 71 | |
| 72 # Update the upstream remote. | |
| 73 shell_utils.run([GIT, 'fetch', UPSTREAM_REMOTE_NAME]) | |
| 74 | |
| 75 # Create a stack of commits to submit, one at a time, until we reach a | |
| 76 # commit that has already been merged. | |
| 77 commit_stack = [] | |
| 78 head = git_utils.ShortHash('HEAD') | |
| 79 | |
| 80 print 'HEAD is at %s' % head | |
| 81 | |
| 82 if self._got_revision: | |
| 83 # Merge the revision that started this build. | |
| 84 commit = git_utils.ShortHash(self._got_revision) | |
| 85 else: | |
| 86 raise Exception('This build has no _got_revision to merge!') | |
| 87 | |
| 88 print ('Starting with %s, look for commits that have not been merged to ' | |
| 89 'HEAD' % commit) | |
| 90 while not git_utils.AIsAncestorOfB(commit, head): | |
| 91 print 'Adding %s to list of commits to merge.' % commit | |
| 92 commit_stack.append(commit) | |
| 93 if git_utils.IsMerge(commit): | |
| 94 # Skia's commit history is not linear. There is no obvious way to | |
| 95 # merge each branch in, one commit at a time. So just start with the | |
| 96 # merge commit. | |
| 97 print '%s is a merge. Skipping merge of its parents.' % commit | |
| 98 break | |
| 99 commit = git_utils.ShortHash(commit + '~1') | |
| 100 else: | |
| 101 print '%s has already been merged.' % commit | |
| 102 | |
| 103 if len(commit_stack) == 0: | |
| 104 raise BuildStepWarning('Nothing to merge; did someone already merge %s?' | |
| 105 ' Exiting.' % commit) | |
| 106 | |
| 107 print 'Merging %s commit(s):\n%s' % (len(commit_stack), | |
| 108 '\n'.join(reversed(commit_stack))) | |
| 109 | |
| 110 # Now we have a list of commits to merge. | |
| 111 while len(commit_stack) > 0: | |
| 112 commit_to_merge = commit_stack.pop() | |
| 113 | |
| 114 print 'Attempting to merge ' + commit_to_merge | |
| 115 | |
| 116 # Start the merge. | |
| 117 try: | |
| 118 shell_utils.run([GIT, 'merge', commit_to_merge, '--no-commit']) | |
| 119 except shell_utils.CommandFailedException: | |
| 120 # Merge conflict. There may be a more elegant solution, but for now, | |
| 121 # undo the merge, and allow (/make) a human to do it. | |
| 122 git_utils.MergeAbort() | |
| 123 raise Exception('Failed to merge %s. Fall back to manual human ' | |
| 124 'merge.' % commit_to_merge) | |
| 125 | |
| 126 | |
| 127 # Grab the upstream version of SkUserConfig, which will be used to | |
| 128 # generate Android's version. | |
| 129 shell_utils.run([GIT, 'checkout', commit_to_merge, '--', | |
| 130 UPSTREAM_USER_CONFIG]) | |
| 131 | |
| 132 # We don't want to commit the upstream version, so remove it from the | |
| 133 # index. | |
| 134 shell_utils.run([GIT, 'reset', 'HEAD', UPSTREAM_USER_CONFIG]) | |
| 135 | |
| 136 # Now generate Android.mk and SkUserConfig.h | |
| 137 gyp_failed = False | |
| 138 try: | |
| 139 gyp_to_android.main() | |
| 140 except AssertionError as e: | |
| 141 print e | |
| 142 # Failed to generate the makefiles. Make a human fix the problem. | |
| 143 git_utils.MergeAbort() | |
| 144 raise Exception('Failed to generate makefiles for %s. Fall back to ' | |
| 145 'manual human merge.' % commit_to_merge) | |
| 146 except SystemExit as e: | |
| 147 gyp_failed = True | |
| 148 | |
| 149 if not gyp_failed: | |
| 150 git_utils.Add('Android.mk') | |
| 151 git_utils.Add(ANDROID_USER_CONFIG) | |
| 152 git_utils.Add(os.path.join('tests', 'Android.mk')) | |
| 153 git_utils.Add(os.path.join('tools', 'Android.mk')) | |
| 154 git_utils.Add(os.path.join('bench', 'Android.mk')) | |
| 155 git_utils.Add(os.path.join('gm', 'Android.mk')) | |
| 156 git_utils.Add(os.path.join('dm', 'Android.mk')) | |
| 157 | |
| 158 # Remove upstream user config, which is no longer needed. | |
| 159 os.remove(UPSTREAM_USER_CONFIG) | |
| 160 | |
| 161 # Create a new branch. | |
| 162 shell_utils.run([REPO, 'start', LOCAL_BRANCH_NAME, '.']) | |
| 163 | |
| 164 try: | |
| 165 orig_msg = shell_utils.run([GIT, 'show', commit_to_merge, | |
| 166 '--format="%s"', '-s']).rstrip() | |
| 167 message = 'Merge %s into master-skia\n\n' + SKIA_REV_URL | |
| 168 if gyp_failed: | |
| 169 message += '\n\nFIXME: Failed to generate makefiles!' | |
| 170 shell_utils.run([GIT, 'commit', '-m', message % (orig_msg, | |
| 171 commit_to_merge)]) | |
| 172 except shell_utils.CommandFailedException: | |
| 173 # It is possible that someone else already did the merge (for example, | |
| 174 # if they are testing a build slave). Clean up and exit. | |
| 175 RepoAbandon(LOCAL_BRANCH_NAME) | |
| 176 raise BuildStepWarning('Nothing to merge; did someone already merge ' | |
| 177 '%s?' % commit_to_merge) | |
| 178 | |
| 179 # For some reason, sometimes the bot's authentication from sync_android | |
| 180 # does not carry over to this step. Authenticate again. | |
| 181 with GitAuthenticate(): | |
| 182 # Now push to master-skia branch | |
| 183 try: | |
| 184 shell_utils.run([GIT, 'push', MASTER_SKIA_URL, MASTER_SKIA_REFS]) | |
| 185 except shell_utils.CommandFailedException: | |
| 186 # It's possible someone submitted in between our sync and push or | |
| 187 # push failed for some other reason. Abandon and let the next | |
| 188 # attempt try again. | |
| 189 RepoAbandon(LOCAL_BRANCH_NAME) | |
| 190 raise BuildStepFailure('git push failed!') | |
| 191 | |
| 192 # Our branch is no longer needed. Remove it. | |
| 193 shell_utils.run([REPO, 'sync', '-j32', '.']) | |
| 194 shell_utils.run([REPO, 'prune', '.']) | |
| 195 | |
| 196 # If gyp failed, this probably means there was an error in the gyp | |
| 197 # files. We still want to push the commit. This way, when it gets | |
| 198 # fixed with a future commit, we don't remain hung up on this one. | |
| 199 if gyp_failed: | |
| 200 raise BuildStepFailure('Merged %s, but failed to generate makefiles.' | |
| 201 ' Is there a mistake in the gyp files?' % | |
| 202 commit_to_merge) | |
| 203 | |
| 204 | |
| 205 if '__main__' == __name__: | |
| 206 sys.exit(BuildStep.RunBuildStep(MergeIntoAndroid)) | |
| OLD | NEW |