def _GetSVNRevisionAndSHA1(git_branch, svn_revision):
    logging.debug('Getting SVN revision and SHA1 ...')

    if svn_revision == 'HEAD':
        # Just use the latest commit.
        commit = merge_common.GetCommandStdout([
            'git', 'log', '-n1', '--grep=git-svn-id:', '--format=%H%n%b',
            git_branch
        ])
        sha1 = commit.split()[0]
        svn_revision = _ParseSvnRevisionFromGitCommitMessage(commit)
        return (svn_revision, sha1)

    if svn_revision is None:
        # Fetch LKGR from upstream.
        svn_revision = GetLKGR()
    output = merge_common.GetCommandStdout([
        'git', 'log',
        '--grep=git-svn-id: .*@%s' % svn_revision, '--format=%H', git_branch
    ])
    if not output:
        raise merge_common.TemporaryMergeError(
            'Revision %s not found in git repo.' % svn_revision)
    # The log grep will sometimes match reverts/reapplies of commits. We take the
    # oldest (last) match because the first time it appears in history is
    # overwhelmingly likely to be the correct commit.
    sha1 = output.split()[-1]
    return (svn_revision, sha1)
def _GenerateLastChange(version):
    """Write a build/util/LASTCHANGE file containing the current revision.

  The revision number is compiled into the binary at build time from this file.

  Args:
    version: The version to mention in generated commit messages.
  """
    logging.debug('Updating LASTCHANGE ...')
    svn_revision, sha1 = _GetSVNRevisionAndSHA1('HEAD', 'HEAD')
    with open(
            os.path.join(merge_common.REPOSITORY_ROOT,
                         'build/util/LASTCHANGE'), 'w') as f:
        f.write('LASTCHANGE=%s\n' % svn_revision)
    merge_common.GetCommandStdout(
        ['git', 'add', '-f', 'build/util/LASTCHANGE'])
    logging.debug('Updating LASTCHANGE.blink ...')
    with open(
            os.path.join(merge_common.REPOSITORY_ROOT,
                         'build/util/LASTCHANGE.blink'), 'w') as f:
        f.write('LASTCHANGE=%s\n' % _GetBlinkRevision())
    merge_common.GetCommandStdout(
        ['git', 'add', '-f', 'build/util/LASTCHANGE.blink'])
    if _ModifiedFilesInIndex():
        merge_common.GetCommandStdout([
            'git', 'commit', '-m',
            'Update LASTCHANGE file after merge of Chromium at %s\n\n%s' %
            (version, AUTOGEN_MESSAGE)
        ])
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 _ReadGitFile(sha1, path, git_url=None, git_branch=None):
    """Reads a file from a (possibly remote) git project at a specific revision.

  Args:
    sha1: The SHA1 at which to read.
    path: The relative path of the file to read.
    git_url: The URL of the git server, if reading a remote project.
    git_branch: The branch to fetch, if reading a remote project.
  Returns:
    The contents of the specified file.
  """
    if git_url:
        merge_common.GetCommandStdout(
            ['git', 'fetch', '-f', git_url, git_branch])
    return merge_common.GetCommandStdout(
        ['git', 'show', '%s:%s' % (sha1, 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()
Exemplo n.º 7
0
def _GetSVNRevision(commitish='history/master-chromium'):
    logging.debug('Getting SVN revision ...')
    commit = merge_common.GetCommandStdout([
        'git', 'log', '-n1', '--grep=git-svn-id:', '--format=%H%n%b', commitish
    ])
    svn_revision = re.search(r'^git-svn-id: .*@([0-9]+)',
                             commit,
                             flags=re.MULTILINE).group(1)
    return svn_revision
def Snapshot(svn_revision, root_sha1, release, target, unattended,
             buildspec_url):
    """Takes a snapshot of the Chromium tree and merges it into Android.

  Android makefiles and a top-level NOTICE file are generated and committed
  after the merge.

  Args:
    svn_revision: The SVN revision in the Chromium repository to merge from.
    root_sha1: The sha1 in the Chromium git mirror to merge from.
    release: The Chromium release version to merge from (e.g. "30.0.1599.20").
             Only one of svn_revision, root_sha1 and release should be
             specified.
    target: The target branch to merge to.
    unattended: Run in unattended mode.
    buildspec_url: URL for buildspec repository, used when merging a release.

  Returns:
    True if new commits were merged; False if no new commits were present.
  """
    if svn_revision:
        svn_revision, root_sha1 = _GetSVNRevisionAndSHA1(
            SRC_GIT_BRANCH, svn_revision)
    elif root_sha1:
        svn_revision = _GetSVNRevisionFromSha(root_sha1)

    if svn_revision and root_sha1:
        version = svn_revision
        if not merge_common.GetCommandStdout(
            ['git', 'rev-list', '-1', 'HEAD..' + root_sha1]):
            logging.info('No new commits to merge at %s (%s)', svn_revision,
                         root_sha1)
            return False
    elif release:
        version = release
        root_sha1 = None
    else:
        raise merge_common.MergeError('No merge source specified')

    logging.info('Snapshotting Chromium at %s (%s)', version, root_sha1)

    # 1. Merge, accounting for excluded directories
    _MergeProjects(version, root_sha1, target, unattended, buildspec_url)

    # 2. Generate Android makefiles
    _GenerateMakefiles(version, unattended)

    # 3. Check for incompatible licenses
    _CheckLicenses()

    # 4. Generate Android NOTICE file
    _GenerateNoticeFile(version)

    # 5. Generate LASTCHANGE file
    _GenerateLastChange(version)

    return True
Exemplo n.º 9
0
def _MergeWithRepoProp(repo_prop_file, target):
    chromium_sha = None
    webview_sha = None
    with open(repo_prop_file) as prop:
        for line in prop:
            project, sha = line.split()
            if project == 'platform/external/chromium_org-history':
                chromium_sha = sha
            elif project == 'platform/frameworks/webview':
                webview_sha = sha
    if not chromium_sha or not webview_sha:
        logging.error('SHA1s for projects not found; invalid build.prop?')
        return 1
    chromium_revision = _GetSVNRevision(chromium_sha)
    logging.info('Merging Chromium at r%s and WebView at %s',
                 chromium_revision, webview_sha)
    _MergeProjects(chromium_revision, target)

    dest_dir = os.path.join(os.environ['ANDROID_BUILD_TOP'],
                            'frameworks/webview')
    merge_common.GetCommandStdout(['git', 'remote', 'update', 'goog'],
                                  cwd=dest_dir)
    merge_common.GetCommandStdout([
        'git', 'checkout', '-b', 'merge-to-' + target, '-t', 'goog/' + target
    ],
                                  cwd=dest_dir)
    if merge_common.GetCommandStdout(
        ['git', 'rev-list', '-1', 'HEAD..' + webview_sha], cwd=dest_dir):
        logging.debug('Creating merge for framework...')
        # Merge conflicts cause 'git merge' to return 1, so ignore errors
        merge_common.GetCommandStdout(
            ['git', 'merge', '--no-commit', '--no-ff', webview_sha],
            cwd=dest_dir,
            ignore_errors=True)
        merge_common.CheckNoConflictsAndCommitMerge(
            'Merge master-chromium into %s at r%s\n\n%s' %
            (target, chromium_revision, AUTOGEN_MESSAGE),
            cwd=dest_dir)
        upload = merge_common.GetCommandStdout(
            ['git', 'push', 'goog', 'HEAD:refs/for/' + target], cwd=dest_dir)
        logging.info(upload)
    else:
        logging.debug('No new commits to merge in framework')
    return 0
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 _GenerateNoticeFile(version):
    """Generates and commits a NOTICE file containing code licenses.

  This covers all third-party code (from Android's perspective) that lives in
  the Chromium tree.

  Args:
    version: The version to mention in generated commit messages.
  """
    logging.debug('Regenerating NOTICE file ...')

    contents = webview_licenses.GenerateNoticeFile()

    with open(os.path.join(merge_common.REPOSITORY_ROOT, 'NOTICE'), 'w') as f:
        f.write(contents)
    merge_common.GetCommandStdout(['git', 'add', 'NOTICE'])
    # Only try to commit the NOTICE update if the file has actually changed.
    if _ModifiedFilesInIndex():
        merge_common.GetCommandStdout([
            'git', 'commit', '-m',
            'Update NOTICE file after merge of Chromium at %s\n\n%s' %
            (version, AUTOGEN_MESSAGE)
        ])
Exemplo n.º 12
0
def Push(target):
    """Push the finished snapshot to the Android repository."""
    logging.debug('Pushing to server ...')
    refspec = 'merge-to-%s:%s' % (target, target)
    for path in merge_common.ALL_PROJECTS:
        logging.debug('Pushing %s', path)
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, 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 Push(version, target):
    """Push the finished snapshot to the Android repository."""
    src = 'merge-from-chromium-%s' % version
    # Use forced pushes ('+' prefix) for the temporary and archive branches in
    # case they already got updated by a previous (possibly failed?) merge, but
    # do not force push to the real master-chromium branch as this could erase
    # downstream changes.
    refspecs = [
        '%s:%s' % (src, target),
        '+%s:refs/archive/chromium-%s' % (src, version)
    ]
    if target == 'master-chromium':
        refspecs.insert(0, '+%s:master-chromium-merge' % src)
    for refspec in refspecs:
        logging.debug('Pushing to server (%s) ...' % refspec)
        for path in merge_common.ALL_PROJECTS:
            if path in merge_common.PROJECTS_WITH_FLAT_HISTORY:
                remote = 'history'
            else:
                remote = 'goog'
            logging.debug('Pushing %s', path)
            dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
            merge_common.GetCommandStdout(['git', 'push', remote, refspec],
                                          cwd=dest_dir)
def _ModifiedFilesInIndex(cwd=merge_common.REPOSITORY_ROOT):
    """Returns true if git's index contains any changes."""
    status = merge_common.GetCommandStdout(['git', 'status', '--porcelain'],
                                           cwd=cwd)
    return re.search(r'^[MADRC]', status, flags=re.MULTILINE) is not None
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 _GetSVNRevisionFromSha(sha1):
    commit = merge_common.GetCommandStdout(
        ['git', 'show', '--format=%H%n%b', sha1])
    return _ParseSvnRevisionFromGitCommitMessage(commit)
def _GetBlinkRevision():
    commit = merge_common.GetCommandStdout(
        ['git', 'log', '-n1', '--grep=git-svn-id:', '--format=%H%n%b'],
        cwd=os.path.join(merge_common.REPOSITORY_ROOT, 'third_party',
                         'WebKit'))
    return _ParseSvnRevisionFromGitCommitMessage(commit)
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 _GenerateMakefiles(version, unattended):
    """Run gyp to generate the Android build system makefiles.

  Args:
    version: The version to mention in generated commit messages.
    unattended: Run in unattended mode.
  """
    logging.debug('Generating makefiles ...')

    # TODO(torne): come up with a way to deal with hooks from DEPS properly

    # TODO(torne): The .tmp files are generated by
    # third_party/WebKit/Source/WebCore/WebCore.gyp/WebCore.gyp into the source
    # tree. We should avoid this, or at least use a more specific name to avoid
    # accidentally removing or adding other files.
    for path in merge_common.ALL_PROJECTS:
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
        merge_common.GetCommandStdout([
            'git', 'rm', '--ignore-unmatch', 'GypAndroid.*.mk',
            '*.target.*.mk', '*.host.*.mk', '*.tmp'
        ],
                                      cwd=dest_dir)

    try:
        merge_common.GetCommandStdout(
            ['android_webview/tools/gyp_webview', 'all'])
    except merge_common.MergeError as e:
        if not unattended:
            raise
        else:
            for path in merge_common.ALL_PROJECTS:
                merge_common.GetCommandStdout(['git', 'reset', '--hard'],
                                              cwd=os.path.join(
                                                  merge_common.REPOSITORY_ROOT,
                                                  path))
            raise merge_common.TemporaryMergeError(
                'Makefile generation failed: ' + str(e))

    for path in merge_common.ALL_PROJECTS:
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
        # git add doesn't have an --ignore-unmatch so we have to do this instead:
        merge_common.GetCommandStdout(['git', 'add', '-f', 'GypAndroid.*.mk'],
                                      ignore_errors=True,
                                      cwd=dest_dir)
        merge_common.GetCommandStdout(['git', 'add', '-f', '*.target.*.mk'],
                                      ignore_errors=True,
                                      cwd=dest_dir)
        merge_common.GetCommandStdout(['git', 'add', '-f', '*.host.*.mk'],
                                      ignore_errors=True,
                                      cwd=dest_dir)
        merge_common.GetCommandStdout(['git', 'add', '-f', '*.tmp'],
                                      ignore_errors=True,
                                      cwd=dest_dir)
        # Only try to commit the makefiles if something has actually changed.
        if _ModifiedFilesInIndex(dest_dir):
            merge_common.GetCommandStdout([
                'git', 'commit', '-m',
                'Update makefiles after merge of Chromium at %s\n\n%s' %
                (version, AUTOGEN_MESSAGE)
            ],
                                          cwd=dest_dir)
Exemplo n.º 20
0
def _MergeProjects(svn_revision, target):
    """Merges the Chromium projects from master-chromium to target.

  The larger projects' histories are flattened in the process.

  Args:
    svn_revision: The SVN revision for the main Chromium repository
  """
    for path in merge_common.PROJECTS_WITH_FLAT_HISTORY:
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
        merge_common.GetCommandStdout(
            ['git', 'remote', 'update', 'goog', 'history'], cwd=dest_dir)
        merge_common.GetCommandStdout([
            'git', 'checkout', '-b', 'merge-to-' + target, '-t',
            'goog/' + target
        ],
                                      cwd=dest_dir)
        merge_common.GetCommandStdout([
            'git', 'fetch', 'history',
            'refs/archive/chromium-%s' % svn_revision
        ],
                                      cwd=dest_dir)
        merge_sha1 = merge_common.GetCommandStdout(
            ['git', 'rev-parse', 'FETCH_HEAD'], cwd=dest_dir).strip()
        old_sha1 = merge_common.GetCommandStdout(['git', 'rev-parse', 'HEAD'],
                                                 cwd=dest_dir).strip()
        # Make the previous merges into grafts so we can do a correct merge.
        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'))
        if merge_common.GetCommandStdout(
            ['git', 'rev-list', '-1', 'HEAD..' + merge_sha1], cwd=dest_dir):
            logging.debug('Merging project %s ...', path)
            # Merge conflicts cause 'git merge' to return 1, so ignore errors
            merge_common.GetCommandStdout(
                ['git', 'merge', '--no-commit', '--squash', merge_sha1],
                cwd=dest_dir,
                ignore_errors=True)
            dirs_to_prune = merge_common.PRUNE_WHEN_FLATTENING.get(path, [])
            if dirs_to_prune:
                merge_common.GetCommandStdout(
                    ['git', 'rm', '--ignore-unmatch', '-rf'] + dirs_to_prune,
                    cwd=dest_dir)
            merge_common.CheckNoConflictsAndCommitMerge(
                'Merge from Chromium at DEPS revision %s\n\n%s' %
                (svn_revision, AUTOGEN_MESSAGE),
                cwd=dest_dir)
            new_sha1 = 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_sha1, old_sha1, merge_sha1))
            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' %
                (svn_revision, AUTOGEN_MESSAGE)
            ],
                                          cwd=dest_dir)
        else:
            logging.debug('No new commits to merge in project %s', path)

    for path in merge_common.PROJECTS_WITH_FULL_HISTORY:
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
        merge_common.GetCommandStdout(['git', 'remote', 'update', 'goog'],
                                      cwd=dest_dir)
        merge_common.GetCommandStdout([
            'git', 'checkout', '-b', 'merge-to-' + target, '-t',
            'goog/' + target
        ],
                                      cwd=dest_dir)
        merge_common.GetCommandStdout([
            'git', 'fetch', 'goog',
            'refs/archive/chromium-%s' % svn_revision
        ],
                                      cwd=dest_dir)
        if merge_common.GetCommandStdout(
            ['git', 'rev-list', '-1', 'HEAD..FETCH_HEAD'], cwd=dest_dir):
            logging.debug('Merging project %s ...', path)
            # Merge conflicts cause 'git merge' to return 1, so ignore errors
            merge_common.GetCommandStdout(
                ['git', 'merge', '--no-commit', '--no-ff', 'FETCH_HEAD'],
                cwd=dest_dir,
                ignore_errors=True)
            merge_common.CheckNoConflictsAndCommitMerge(
                'Merge from Chromium at DEPS revision %s\n\n%s' %
                (svn_revision, AUTOGEN_MESSAGE),
                cwd=dest_dir)
        else:
            logging.debug('No new commits to merge in project %s', path)
def _MergeProjects(version, root_sha1, target, unattended, buildspec_url):
    """Merges each required Chromium project into the Android repository.

  .DEPS.git is consulted to determine which revision each project must be merged
  at. Only a whitelist of required projects are merged.

  Args:
    version: The version to mention in generated commit messages.
    root_sha1: The git hash to merge in the root repository.
    target: The target branch to merge to.
    unattended: Run in unattended mode.
    buildspec_url: URL for buildspec repository, when merging a branch.
  Raises:
    TemporaryMergeError: If incompatibly licensed code is left after pruning.
  """
    # The logic for this step lives here, in the Android tree, as it makes no
    # sense for a Chromium tree to know about this merge.

    if unattended:
        branch_create_flag = '-B'
    else:
        branch_create_flag = '-b'
    branch_name = 'merge-from-chromium-%s' % version

    logging.debug('Parsing DEPS ...')
    if root_sha1:
        deps_content = _ReadGitFile(root_sha1, '.DEPS.git')
    else:
        deps_content = _ReadGitFile('FETCH_HEAD',
                                    'releases/' + version + '/.DEPS.git',
                                    buildspec_url, 'master')

    deps_vars = _ParseDEPS(deps_content)

    merge_info = _GetProjectMergeInfo(merge_common.THIRD_PARTY_PROJECTS,
                                      deps_vars)

    for path in merge_info:
        # webkit needs special handling as we have a local mirror
        local_mirrored = path == 'third_party/WebKit'
        url = merge_info[path]['url']
        sha1 = merge_info[path]['sha1']
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
        if local_mirrored:
            remote = 'history'
        else:
            remote = 'goog'
        merge_common.GetCommandStdout([
            'git', 'checkout', branch_create_flag, branch_name, '-t',
            remote + '/' + target
        ],
                                      cwd=dest_dir)
        if not local_mirrored or not root_sha1:
            logging.debug('Fetching project %s at %s ...', path, sha1)
            fetch_args = ['git', 'fetch', url, sha1]
            merge_common.GetCommandStdout(fetch_args, cwd=dest_dir)
        if merge_common.GetCommandStdout(
            ['git', 'rev-list', '-1', 'HEAD..' + sha1], cwd=dest_dir):
            logging.debug('Merging project %s at %s ...', path, sha1)
            # Merge conflicts make git merge return 1, so ignore errors
            merge_common.GetCommandStdout(
                ['git', 'merge', '--no-commit', sha1],
                cwd=dest_dir,
                ignore_errors=True)
            merge_common.CheckNoConflictsAndCommitMerge(
                'Merge %s from %s at %s\n\n%s' %
                (path, url, sha1, AUTOGEN_MESSAGE),
                cwd=dest_dir,
                unattended=unattended)
        else:
            logging.debug('No new commits to merge in project %s', path)

    # Handle root repository separately.
    merge_common.GetCommandStdout([
        'git', 'checkout', branch_create_flag, branch_name, '-t',
        'history/' + target
    ])
    if not root_sha1:
        merge_info = _GetProjectMergeInfo([''], deps_vars)
        url = merge_info['']['url']
        root_sha1 = merge_info['']['sha1']
        merge_common.GetCommandStdout(['git', 'fetch', url, root_sha1])
    logging.debug('Merging Chromium at %s ...', root_sha1)
    # Merge conflicts make git merge return 1, so ignore errors
    merge_common.GetCommandStdout(['git', 'merge', '--no-commit', root_sha1],
                                  ignore_errors=True)
    merge_common.CheckNoConflictsAndCommitMerge(
        'Merge Chromium at %s (%s)\n\n%s' %
        (version, root_sha1, AUTOGEN_MESSAGE),
        unattended=unattended)

    logging.debug('Getting directories to exclude ...')

    # We import this now that we have merged the latest version.
    # It imports to a global in order that it can be used to generate NOTICE
    # later. We also disable writing bytecode to keep the source tree clean.
    sys.path.append(
        os.path.join(merge_common.REPOSITORY_ROOT, 'android_webview', 'tools'))
    sys.dont_write_bytecode = True
    global webview_licenses
    import webview_licenses
    import known_issues

    for path, exclude_list in known_issues.KNOWN_INCOMPATIBLE.iteritems():
        logging.debug('  %s',
                      '\n  '.join(os.path.join(path, x) for x in exclude_list))
        dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
        merge_common.GetCommandStdout(
            ['git', 'rm', '-rf', '--ignore-unmatch'] + exclude_list,
            cwd=dest_dir)
        if _ModifiedFilesInIndex(dest_dir):
            merge_common.GetCommandStdout(
                ['git', 'commit', '-m', 'Exclude unwanted directories'],
                cwd=dest_dir)