summaryrefslogtreecommitdiff
path: root/chromium/tools/merge_to_android.py
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/tools/merge_to_android.py')
-rwxr-xr-xchromium/tools/merge_to_android.py394
1 files changed, 0 insertions, 394 deletions
diff --git a/chromium/tools/merge_to_android.py b/chromium/tools/merge_to_android.py
deleted file mode 100755
index 9a2e78d..0000000
--- a/chromium/tools/merge_to_android.py
+++ /dev/null
@@ -1,394 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright (C) 2012 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-"""Merge master-chromium to master within the Android tree."""
-
-import logging
-import optparse
-import os
-import re
-import shutil
-import subprocess
-import sys
-
-import merge_common
-
-
-AUTOGEN_MESSAGE = 'This commit was generated by merge_to_master.py.'
-WEBVIEW_PROJECT = 'frameworks/webview'
-
-
-def _GetAbsPath(project):
- """Returns the full path to a given project (either Chromium or Android)."""
- if project in merge_common.ALL_PROJECTS:
- abs_path = os.path.join(merge_common.REPOSITORY_ROOT, project)
- else:
- abs_path = os.path.join(os.environ['ANDROID_BUILD_TOP'], project)
- if not os.path.exists(abs_path):
- raise merge_common.MergeError('Cannot find path ' + abs_path)
- return abs_path
-
-
-def _CheckoutSingleProject(project, target_branch):
- """Checks out the tip of the target_branch into a local branch (merge-to-XXX).
-
- Args:
- project: a Chromium project (., third_party/foo) or frameworks/webview.
- target_branch: name of the target branch (in the goog remote).
- """
- dest_dir = _GetAbsPath(project)
- tracking_branch = 'goog/' + target_branch
- logging.debug('Check out %-45s at %-16s', project, tracking_branch)
- merge_common.GetCommandStdout(['git', 'remote', 'update', 'goog'],
- cwd=dest_dir)
- merge_common.GetCommandStdout(['git', 'checkout',
- '-b', 'merge-to-' + target_branch,
- '-t', tracking_branch], cwd=dest_dir)
-
-
-def _FetchSingleProject(project, remote, remote_ref):
- """Fetches a remote ref for the given project and returns the fetched SHA.
-
- Args:
- project: a Chromium project (., third_party/foo) or frameworks/webview.
- remote: Git remote name (goog for most projects, history for squashed ones).
- remote_ref: the remote ref to fetch (e.g., refs/archive/chromium-XXX).
-
- Returns:
- The SHA1 of the FETCH_HEAD.
- """
- dest_dir = _GetAbsPath(project)
- logging.debug('Fetch %-45s %s:%s', project, remote, remote_ref)
- merge_common.GetCommandStdout(['git', 'fetch', remote, remote_ref],
- cwd=dest_dir)
- return merge_common.GetCommandStdout(['git', 'rev-parse', 'FETCH_HEAD'],
- cwd=dest_dir).strip()
-
-
-def _MergeSingleProject(project, merge_sha, revision, target_branch, flatten):
- """Merges a single project at a given SHA.
-
- Args:
- project: a Chromium project (., third_party/foo) or frameworks/webview.
- merge_sha: the SHA to merge.
- revision: Abbrev. commitish in the main Chromium repository.
- target_branch: name of the target branch.
- flatten: True: squash history while merging; False: perform a normal merge.
- """
- dest_dir = _GetAbsPath(project)
- if flatten:
- # Make the previous merges into grafts so we can do a correct merge.
- old_sha = merge_common.GetCommandStdout(['git', 'rev-parse', 'HEAD'],
- cwd=dest_dir).strip()
- merge_log = os.path.join(dest_dir, '.merged-revisions')
- if os.path.exists(merge_log):
- shutil.copyfile(merge_log,
- os.path.join(dest_dir, '.git', 'info', 'grafts'))
-
- # Early out if there is nothing to merge.
- if not merge_common.GetCommandStdout(['git', 'rev-list', '-1',
- 'HEAD..' + merge_sha], cwd=dest_dir):
- logging.debug('No new commits to merge in project %s', project)
- return
-
- logging.debug('Merging project %s (flatten: %s)...', project, flatten)
- merge_cmd = ['git', 'merge', '--no-commit']
- merge_cmd += ['--squash'] if flatten else ['--no-ff']
- merge_cmd += [merge_sha]
- # Merge conflicts cause 'git merge' to return 1, so ignore errors
- merge_common.GetCommandStdout(merge_cmd, cwd=dest_dir, ignore_errors=True)
-
- if flatten:
- dirs_to_prune = merge_common.PRUNE_WHEN_FLATTENING.get(project, [])
- if dirs_to_prune:
- merge_common.GetCommandStdout(['git', 'rm', '--ignore-unmatch', '-rf'] +
- dirs_to_prune, cwd=dest_dir)
-
- if project in merge_common.ALL_PROJECTS:
- commit_msg = 'Merge from Chromium at DEPS revision %s' % revision
- else:
- commit_msg = 'Merge master-chromium into %s at %s' % (target_branch,
- revision)
- commit_msg += '\n\n' + AUTOGEN_MESSAGE
- merge_common.CheckNoConflictsAndCommitMerge(commit_msg, cwd=dest_dir)
-
- if flatten:
- # Generate the new grafts file and commit it on top of the merge.
- new_sha = merge_common.GetCommandStdout(['git', 'rev-parse', 'HEAD'],
- cwd=dest_dir).strip()
- with open(merge_log, 'a+') as f:
- f.write('%s %s %s\n' % (new_sha, old_sha, merge_sha))
- merge_common.GetCommandStdout(['git', 'add', '.merged-revisions'],
- cwd=dest_dir)
- merge_common.GetCommandStdout(
- ['git', 'commit', '-m',
- 'Record Chromium merge at DEPS revision %s\n\n%s' %
- (revision, AUTOGEN_MESSAGE)], cwd=dest_dir)
-
-
-def _IsAncestor(ref1, ref2, cwd):
- """Checks whether ref1 is a ancestor of ref2 in the given Git repo."""
- cmd = ['git', 'merge-base', '--is-ancestor', ref1, ref2]
- ret = subprocess.call(cmd, cwd=cwd)
- if ret == 0:
- return True
- elif ret == 1:
- return False
- else:
- raise merge_common.CommandError(ret, ' '.join(cmd), cwd, 'N/A', 'N/A')
-
-
-def _MergeChromiumProjects(revision, target_branch, repo_shas=None,
- force=False):
- """Merges the Chromium projects from master-chromium to target_branch.
-
- The larger projects' histories are flattened in the process.
- When repo_shas != None, it checks that the SHAs of the projects in the
- archive match exactly the SHAs of the projects in repo.prop.
-
- Args:
- revision: Abbrev. commitish in the main Chromium repository.
- target_branch: target branch name to merge and push to.
- repo_shas: optional dict. of expected revisions (only for --repo-prop).
- force: True: merge anyways using the SHAs from repo.prop; False: bail out if
- projects mismatch (archive vs repo.prop).
- """
- # Sync and checkout ToT for all projects (creating the merge-to-XXX branch)
- # and fetch the archive snapshot.
- fetched_shas = {}
- remote_ref = 'refs/archive/chromium-%s' % revision
- for project in merge_common.PROJECTS_WITH_FLAT_HISTORY:
- _CheckoutSingleProject(project, target_branch)
- fetched_shas[project] = _FetchSingleProject(project, 'history', remote_ref)
- for project in merge_common.PROJECTS_WITH_FULL_HISTORY:
- _CheckoutSingleProject(project, target_branch)
- fetched_shas[project] = _FetchSingleProject(project, 'goog', remote_ref)
-
- if repo_shas:
- project_shas_mismatch = False
- for project, merge_sha in fetched_shas.items(): # the dict can be modified.
- expected_sha = repo_shas.get(project)
- if expected_sha != merge_sha:
- logging.warn('The SHA for project %s specified in the repo.prop (%s) '
- 'and the one in the archive (%s) differ.',
- project, expected_sha, merge_sha)
- dest_dir = _GetAbsPath(project)
- if expected_sha is None:
- reason = 'cannot find a SHA in the repo.pro for %s' % project
- elif _IsAncestor(merge_sha, expected_sha, cwd=dest_dir):
- reason = 'the SHA in repo.prop is ahead of the SHA in the archive. '
- log_cmd = ['git', 'log', '--oneline', '--graph', '--max-count=10',
- '%s..%s' % (merge_sha, expected_sha)]
- log_cmd_output = merge_common.GetCommandStdout(log_cmd, cwd=dest_dir)
- reason += 'showing partial log (%s): \n %s' % (' '.join(log_cmd),
- log_cmd_output)
- elif _IsAncestor(expected_sha, merge_sha, cwd=dest_dir):
- reason = 'The SHA is already merged in the archive'
- else:
- reason = 'The project history diverged. Consult your Git historian.'
-
- project_shas_mismatch = True
- if force:
- logging.debug('Merging the SHA in repo.prop anyways (due to --force)')
- fetched_shas[project] = expected_sha
- else:
- logging.debug('Reason: %s', reason)
- if not force and project_shas_mismatch:
- raise merge_common.MergeError(
- 'The revision of some projects in the archive is different from the '
- 'one provided in build.prop. See the log for more details. Re-run '
- 'with --force to continue.')
-
- for project in merge_common.PROJECTS_WITH_FLAT_HISTORY:
- _MergeSingleProject(project, fetched_shas[project], revision, target_branch,
- flatten=True)
- for project in merge_common.PROJECTS_WITH_FULL_HISTORY:
- _MergeSingleProject(project, fetched_shas[project], revision, target_branch,
- flatten=False)
-
-
-def _GetNearestUpstreamAbbrevSHA(reference='history/master-chromium'):
- """Returns the abbrev. upstream SHA which closest to the given reference."""
- logging.debug('Getting upstream SHA for %s...', reference)
- merge_common.GetCommandStdout(['git', 'remote', 'update', 'history'])
- upstream_commit = merge_common.Abbrev(merge_common.GetCommandStdout([
- 'git', 'merge-base', 'history/upstream-master', reference]))
-
- # Pedantic check: look for the existence of a merge commit which contains the
- # |upstream_commit| in its message and is its children.
- merge_parents = merge_common.GetCommandStdout([
- 'git', 'rev-list', reference, '--grep', upstream_commit, '--merges',
- '--parents', '-1'])
- if upstream_commit not in merge_parents:
- raise merge_common.MergeError(
- 'Found upstream commit %s, but the merge child (%s) could not be found '
- 'or is not a parent of the upstream SHA')
- logging.debug('Found nearest Chromium revision %s', upstream_commit)
- return upstream_commit
-
-
-def _MergeWithRepoProp(repo_prop_file, target_branch, force):
- """Performs a merge using a repo.prop file (from Android build waterfall).
-
- This does NOT merge (unless forced with force=True) the pinned
- revisions in repo.prop, as a repo.prop can snapshot an intermediate state
- (between two automerger cycles). Instead, this looks up the archived snapshot
- (generated by the chromium->master-chromium auto-merger) which is closest to
- the given repo.prop (following the main Chromium project) and merges that one.
- If the projects revisions don't match, it fails with detailed error messages.
-
- Args:
- repo_prop_file: Path to a downloaded repo.prop file.
- target_branch: name of the target branch to merget to.
- force: ignores the aforementioned check and merged anyways.
- """
- chromium_sha = None
- webview_sha = None
- repo_shas = {} # 'project/path' -> 'sha'
- with open(repo_prop_file) as prop:
- for line in prop:
- repo, sha = line.split()
- # Translate the Android repo paths into the relative project paths used in
- # merge_common (e.g., platform/external/chromium_org/foo -> foo).
- m = (
- re.match(r'^platform/(frameworks/.+)$', repo) or
- re.match(r'^platform/external/chromium_org/?(.*?)(-history)?$', repo))
- if m:
- project = m.group(1) if m.group(1) else '.' # '.' = Main project.
- repo_shas[project] = sha
-
- chromium_sha = repo_shas.get('.')
- webview_sha = repo_shas.get(WEBVIEW_PROJECT)
- if not chromium_sha or not webview_sha:
- raise merge_common.MergeError('SHAs for projects not found; '
- 'invalid build.prop?')
-
- # Check that the revisions in repo.prop and the on in the archive match.
- archived_chromium_revision = _GetNearestUpstreamAbbrevSHA(chromium_sha)
- logging.info('Merging Chromium at %s and WebView at %s',
- archived_chromium_revision, webview_sha)
- _MergeChromiumProjects(archived_chromium_revision, target_branch, repo_shas,
- force)
-
- _CheckoutSingleProject(WEBVIEW_PROJECT, target_branch)
- _MergeSingleProject(WEBVIEW_PROJECT, webview_sha,
- archived_chromium_revision, target_branch, flatten=False)
-
-
-def Push(target_branch):
- """Push the finished snapshot to the Android repository.
-
- Creates first a CL for frameworks/webview (if the merge-to-XXX branch exists)
- then wait for user confirmation and pushes the Chromium merges. This is to
- give an opportunity to get a +2 for frameworks/webview and then push both
- frameworks/webview and the Chromium projects atomically(ish).
-
- Args:
- target_branch: name of the target branch (in the goog remote).
- """
- merge_branch = 'merge-to-%s' % target_branch
-
- # Create a Gerrit CL for the frameworks/webview project (if needed).
- dest_dir = _GetAbsPath(WEBVIEW_PROJECT)
- did_upload_webview_cl = False
- if merge_common.GetCommandStdout(['git', 'branch', '--list', merge_branch],
- cwd=dest_dir):
- # Check that there was actually something to merge.
- merge_range = 'goog/%s..%s' % (target_branch, merge_branch)
- if merge_common.GetCommandStdout(['git', 'rev-list', '-1', merge_range],
- cwd=dest_dir):
- logging.info('Uploading a merge CL for %s...', WEBVIEW_PROJECT)
- refspec = '%s:refs/for/%s' % (merge_branch, target_branch)
- upload = merge_common.GetCommandStdout(['git', 'push', 'goog', refspec],
- cwd=dest_dir)
- logging.info(upload)
- did_upload_webview_cl = True
-
- prompt_msg = 'About push the Chromium projects merge. '
- if not did_upload_webview_cl:
- logging.info('No merge CL needed for %s.', WEBVIEW_PROJECT)
- else:
- prompt_msg += ('At this point you should have the CL +2-ed and merge it '
- 'together with this push.')
- prompt_msg += '\nPress "y" to continue: '
- if raw_input(prompt_msg) != 'y':
- logging.warn('Push aborted by the user!')
- return
-
- logging.debug('Pushing Chromium projects to %s ...', target_branch)
- refspec = '%s:%s' % (merge_branch, target_branch)
- for path in merge_common.ALL_PROJECTS:
- logging.debug('Pushing %s', path)
- dest_dir = _GetAbsPath(path)
- # Delete the graft before pushing otherwise git will attempt to push all the
- # grafted-in objects to the server as well as the ones we want.
- graftfile = os.path.join(dest_dir, '.git', 'info', 'grafts')
- if os.path.exists(graftfile):
- os.remove(graftfile)
- merge_common.GetCommandStdout(['git', 'push', 'goog', refspec],
- cwd=dest_dir)
-
-
-def main():
- parser = optparse.OptionParser(usage='%prog [options]')
- parser.epilog = ('Takes the current master-chromium branch of the Chromium '
- 'projects in Android and merges them into master to publish '
- 'them.')
- parser.add_option(
- '', '--revision',
- default=None,
- help=('Merge to the specified archived master-chromium revision (abbrev. '
- 'SHA or release version) rather than using HEAD. e.g., '
- '--revision=a1b2c3d4e5f6 or --revision=38.0.2125.24'))
- parser.add_option(
- '', '--repo-prop',
- default=None, metavar='FILE',
- help=('Merge to the revisions specified in this repo.prop file.'))
- parser.add_option(
- '', '--force',
- default=False, action='store_true',
- help=('Skip history checks and merged anyways (only for --repo-prop).'))
- parser.add_option(
- '', '--push',
- default=False, action='store_true',
- help=('Push the result of a previous merge to the server.'))
- parser.add_option(
- '', '--target',
- default='master', metavar='BRANCH',
- help=('Target branch to push to. Defaults to master.'))
- (options, args) = parser.parse_args()
- if args:
- parser.print_help()
- return 1
-
- logging.basicConfig(format='%(message)s', level=logging.DEBUG,
- stream=sys.stdout)
-
- if options.push:
- Push(options.target)
- elif options.repo_prop:
- _MergeWithRepoProp(os.path.expanduser(options.repo_prop),
- options.target, options.force)
- elif options.revision:
- _MergeChromiumProjects(options.revision, options.target)
- else:
- first_upstream_sha = _GetNearestUpstreamAbbrevSHA()
- _MergeChromiumProjects(first_upstream_sha, options.target)
-
- return 0
-
-if __name__ == '__main__':
- sys.exit(main())