182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)#!/usr/bin/python
29df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)#
39df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# Copyright (C) 2012 The Android Open Source Project
49df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)#
59df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License");
69df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# you may not use this file except in compliance with the License.
79df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# You may obtain a copy of the License at
89df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)#
99df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)#      http://www.apache.org/licenses/LICENSE-2.0
109df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)#
119df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software
129df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS,
139df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
149df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# See the License for the specific language governing permissions and
159df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# limitations under the License.
169df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
1782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)"""Merge Chromium into the Android tree."""
189df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
195d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)import logging
206a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)import optparse
216a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)import os
226a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)import re
236a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)import sys
241c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
251c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)import merge_common
266a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
276a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
289df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# We need to import this *after* merging from upstream to get the latest
299df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)# version. Set it to none here to catch uses before it's imported.
309df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)webview_licenses = None
319df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
329df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
331c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)AUTOGEN_MESSAGE = 'This commit was generated by merge_from_chromium.py.'
344465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)SRC_GIT_BRANCH = 'refs/remotes/history/upstream-master'
3570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci
369df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
374465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)def _ReadGitFile(sha1, path, git_url=None, git_branch=None):
384465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)  """Reads a file from a (possibly remote) git project at a specific revision.
3982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
409df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  Args:
419df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)    sha1: The SHA1 at which to read.
429df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)    path: The relative path of the file to read.
434465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)    git_url: The URL of the git server, if reading a remote project.
444465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)    git_branch: The branch to fetch, if reading a remote project.
459df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  Returns:
469df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)    The contents of the specified file.
479df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  """
484465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)  if git_url:
49f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'fetch', '-f', git_url, git_branch])
5082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  return merge_common.GetCommandStdout(['git', 'show', '%s:%s' % (sha1, path)])
519df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
529df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
53f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)def _ParseDEPS(deps_content):
54753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  """Parses the DEPS file from Chromium and returns its contents.
5582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
566a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Args:
57753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    deps_content: The contents of the DEPS file as text.
586a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Returns:
59753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    A dictionary of the contents of DEPS at the specified revision
606a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  """
616a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
626a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  class FromImpl(object):
636a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    """Used to implement the From syntax."""
646a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
656a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    def __init__(self, module_name):
666a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      self.module_name = module_name
676a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
686a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    def __str__(self):
696a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      return 'From("%s")' % self.module_name
706a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
716a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  class _VarImpl(object):
7270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci
736a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    def __init__(self, custom_vars, local_scope):
746a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      self._custom_vars = custom_vars
756a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      self._local_scope = local_scope
766a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
776a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    def Lookup(self, var_name):
786a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      """Implements the Var syntax."""
796a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      if var_name in self._custom_vars:
806a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)        return self._custom_vars[var_name]
816a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      elif var_name in self._local_scope.get('vars', {}):
826a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)        return self._local_scope['vars'][var_name]
836a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      raise Exception('Var is not defined: %s' % var_name)
846a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
856a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  tmp_locals = {}
866a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  var = _VarImpl({}, tmp_locals)
876a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  tmp_globals = {'From': FromImpl, 'Var': var.Lookup, 'deps_os': {}}
8870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  exec(deps_content) in tmp_globals, tmp_locals  # pylint: disable=W0122
896a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  return tmp_locals
906a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
916a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
92f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)def _GetProjectMergeInfo(projects, deps_vars):
93753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  """Gets the git URL and SHA1 for each project based on DEPS.
9482c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
956a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Args:
96f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    projects: The list of projects to consider.
97753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    deps_vars: The dictionary of dependencies from DEPS.
986a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Returns:
996a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    A dictionary from project to git URL and SHA1 - 'path: (url, sha1)'
10082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Raises:
101753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    TemporaryMergeError: if a project to be merged is not found in DEPS.
1026a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  """
1036a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  deps_fallback_order = [
1046a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      deps_vars['deps'],
1056a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      deps_vars['deps_os']['unix'],
1066a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      deps_vars['deps_os']['android'],
1076a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  ]
1086a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  result = {}
109f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  for path in projects:
1106a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    for deps in deps_fallback_order:
11170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci      if path:
112f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)        upstream_path = os.path.join('src', path)
113f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      else:
114f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)        upstream_path = 'src'
115f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      url_plus_sha1 = deps.get(upstream_path)
1166a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      if url_plus_sha1:
1176a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)        break
1186a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    else:
119862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)      raise merge_common.TemporaryMergeError(
120753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci          'Could not find DEPS entry for project %s. This probably '
12182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)          'means that the project list in merge_from_chromium.py needs to be '
12282c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)          'updated.' % path)
1236a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    match = re.match('(.*?)@(.*)', url_plus_sha1)
1246a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    url = match.group(1)
1256a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    sha1 = match.group(2)
1265d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)    logging.debug('  Got URL %s and SHA1 %s for project %s', url, sha1, path)
1276a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    result[path] = {'url': url, 'sha1': sha1}
1286a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  return result
1296a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
1306a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
131f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)def _MergeProjects(version, root_sha1, target, unattended, buildspec_url):
13282c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Merges each required Chromium project into the Android repository.
13382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
134753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  DEPS is consulted to determine which revision each project must be merged
13582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  at. Only a whitelist of required projects are merged.
13682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
1376a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Args:
138f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    version: The version to mention in generated commit messages.
139f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    root_sha1: The git hash to merge in the root repository.
140f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    target: The target branch to merge to.
14114ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)    unattended: Run in unattended mode.
142f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    buildspec_url: URL for buildspec repository, when merging a branch.
143f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  Returns:
144f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    The abbrev sha1 merged. It will be either |root_sha1| itself (when merging
145f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    chromium trunk) or the upstream sha1 of the release.
14682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Raises:
147862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    TemporaryMergeError: If incompatibly licensed code is left after pruning.
1486a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  """
1496a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # The logic for this step lives here, in the Android tree, as it makes no
1506a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # sense for a Chromium tree to know about this merge.
1516a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
15214ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)  if unattended:
15314ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)    branch_create_flag = '-B'
15414ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)  else:
15514ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)    branch_create_flag = '-b'
156f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  branch_name = 'merge-from-chromium-%s' % version
15714ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)
1585d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)  logging.debug('Parsing DEPS ...')
159f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  if root_sha1:
160753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    deps_content = _ReadGitFile(root_sha1, 'DEPS')
161f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  else:
16270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci    # TODO(primiano): At some point the release branches will use DEPS as well,
16370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci    # instead of .DEPS.git. Rename below when that day will come.
164462a80eeb54228445a3601b6aef6a3484cbf2615Bo Liu    deps_content = _ReadGitFile('FETCH_HEAD',
165462a80eeb54228445a3601b6aef6a3484cbf2615Bo Liu                                'releases/' + version + '/.DEPS.git',
166462a80eeb54228445a3601b6aef6a3484cbf2615Bo Liu                                buildspec_url,
167462a80eeb54228445a3601b6aef6a3484cbf2615Bo Liu                                'master')
1686a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
169f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  deps_vars = _ParseDEPS(deps_content)
170f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)
171f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  merge_info = _GetProjectMergeInfo(merge_common.THIRD_PARTY_PROJECTS,
172f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)                                    deps_vars)
1736a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
1746a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  for path in merge_info:
1754465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)    # webkit needs special handling as we have a local mirror
1764465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)    local_mirrored = path == 'third_party/WebKit'
1776a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    url = merge_info[path]['url']
1786a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    sha1 = merge_info[path]['sha1']
1791c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
1804465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)    if local_mirrored:
1814465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)      remote = 'history'
1824465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)    else:
1834465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)      remote = 'goog'
1841c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'checkout',
185862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)                                   branch_create_flag, branch_name,
186f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)                                   '-t', remote + '/' + target],
1874465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)                                  cwd=dest_dir)
188f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    if not local_mirrored or not root_sha1:
1894465da421aa5ee3d2f6a016b961c301040f0fa23Torne (Richard Coles)      logging.debug('Fetching project %s at %s ...', path, sha1)
1903cc50c8a8aa48850211f6724bba1d788c9f08180Torne (Richard Coles)      fetch_args = ['git', 'fetch', url, sha1]
191816f8b55f5a0143dd4106d12c54915c3cf05625dTorne (Richard Coles)      merge_common.GetCommandStdout(fetch_args, cwd=dest_dir)
1921c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    if merge_common.GetCommandStdout(['git', 'rev-list', '-1', 'HEAD..' + sha1],
1931c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                     cwd=dest_dir):
1945d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)      logging.debug('Merging project %s at %s ...', path, sha1)
1956a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)      # Merge conflicts make git merge return 1, so ignore errors
1961c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)      merge_common.GetCommandStdout(['git', 'merge', '--no-commit', sha1],
1971c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                    cwd=dest_dir, ignore_errors=True)
1981c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)      merge_common.CheckNoConflictsAndCommitMerge(
1999df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)          'Merge %s from %s at %s\n\n%s' % (path, url, sha1, AUTOGEN_MESSAGE),
20014ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)          cwd=dest_dir, unattended=unattended)
2016a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    else:
2025d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)      logging.debug('No new commits to merge in project %s', path)
2036a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
2046a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # Handle root repository separately.
20514ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)  merge_common.GetCommandStdout(['git', 'checkout',
206862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)                                 branch_create_flag, branch_name,
207f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)                                 '-t', 'history/' + target])
208f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  if not root_sha1:
209f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    merge_info = _GetProjectMergeInfo([''], deps_vars)
210f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    url = merge_info['']['url']
211f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    merged_sha1 = merge_info['']['sha1']
212f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    merge_common.GetCommandStdout(['git', 'fetch', url, merged_sha1])
213f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    merged_sha1 = merge_common.Abbrev(merged_sha1)
214f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    merge_msg_version = '%s (%s)' % (version, merged_sha1)
215753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  else:
216753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    merge_msg_version = root_sha1
217f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci    merged_sha1 = root_sha1
218753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci
219f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  logging.debug('Merging Chromium at %s ...', merged_sha1)
2206a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # Merge conflicts make git merge return 1, so ignore errors
221f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  merge_common.GetCommandStdout(['git', 'merge', '--no-commit', merged_sha1],
2221c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                ignore_errors=True)
2231c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  merge_common.CheckNoConflictsAndCommitMerge(
224753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      'Merge Chromium at %s\n\n%s'
225753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      % (merge_msg_version, AUTOGEN_MESSAGE), unattended=unattended)
2266a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
2275d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)  logging.debug('Getting directories to exclude ...')
2289df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
2299df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  # We import this now that we have merged the latest version.
2309df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  # It imports to a global in order that it can be used to generate NOTICE
231c7fa5294b03fbc8ccab655d87faaa6d6d704018eTorne (Richard Coles)  # later. We also disable writing bytecode to keep the source tree clean.
2321c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  sys.path.append(os.path.join(merge_common.REPOSITORY_ROOT, 'android_webview',
2331c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                               'tools'))
234c7fa5294b03fbc8ccab655d87faaa6d6d704018eTorne (Richard Coles)  sys.dont_write_bytecode = True
23570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  global webview_licenses  # pylint: disable=W0602
23670e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  import webview_licenses  # pylint: disable=W0621,W0612,C6204
23770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  import known_issues  # pylint: disable=C6204
2389df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
239cadf645507c329e0f5d7fbb67d9311cfa26ac638Torne (Richard Coles)  for path, exclude_list in known_issues.KNOWN_INCOMPATIBLE.iteritems():
2405d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)    logging.debug('  %s', '\n  '.join(os.path.join(path, x) for x in
2415d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)                                      exclude_list))
2421c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
2431c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'rm', '-rf', '--ignore-unmatch'] +
2441c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                  exclude_list, cwd=dest_dir)
2453c67328c9fd2fd48405af3950e59893bfdf1287bTorne (Richard Coles)    if _ModifiedFilesInIndex(dest_dir):
2461c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)      merge_common.GetCommandStdout(['git', 'commit', '-m',
247cb12295daa313fcefcfff1681c30f3fe935e762eTorne (Richard Coles)                                     'Exclude unwanted directories'],
2481c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                    cwd=dest_dir)
249f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  assert(root_sha1 is None or root_sha1 == merged_sha1)
250f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  return merged_sha1
2513c67328c9fd2fd48405af3950e59893bfdf1287bTorne (Richard Coles)
2523ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)
2533ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)def _CheckLicenses():
2543ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)  """Check that no incompatibly licensed directories exist."""
2559df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  directories_left_over = webview_licenses.GetIncompatibleDirectories()
2569df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  if directories_left_over:
257862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    raise merge_common.TemporaryMergeError(
258862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)        'Incompatibly licensed directories remain: ' +
259862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)        '\n'.join(directories_left_over))
2606a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
2616a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
262f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)def _GenerateMakefiles(version, unattended):
26382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Run gyp to generate the Android build system makefiles.
2646a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
26582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Args:
266f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    version: The version to mention in generated commit messages.
267862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    unattended: Run in unattended mode.
26882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """
2694098e8316ddaa5b20fd8f97438fd063a5468db0eTorne (Richard Coles)  logging.debug('Generating makefiles ...')
2704098e8316ddaa5b20fd8f97438fd063a5468db0eTorne (Richard Coles)
271d7fe98f16c652eb648ff5668a2d81858458c18f6Richard Coles  # TODO(torne): come up with a way to deal with hooks from DEPS properly
272d7fe98f16c652eb648ff5668a2d81858458c18f6Richard Coles
2739df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  # TODO(torne): The .tmp files are generated by
2746a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # third_party/WebKit/Source/WebCore/WebCore.gyp/WebCore.gyp into the source
2756a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # tree. We should avoid this, or at least use a more specific name to avoid
2766a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # accidentally removing or adding other files.
2771c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  for path in merge_common.ALL_PROJECTS:
2781c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
2791c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'rm', '--ignore-unmatch',
2805568eda06be15d573395eaba779d62c09f6f96f1Torne (Richard Coles)                                   'GypAndroid.*.mk', '*.target.*.mk',
2815568eda06be15d573395eaba779d62c09f6f96f1Torne (Richard Coles)                                   '*.host.*.mk', '*.tmp'], cwd=dest_dir)
2821c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
283862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)  try:
2841f39fd6f96da9ebacdf3462306f3aef6dec2ec0bTorne (Richard Coles)    merge_common.GetCommandStdout(['android_webview/tools/gyp_webview', 'all'])
2852735b74c930c17d39cb6510c0c2c0a43df1d181aTorne (Richard Coles)  except merge_common.MergeError as e:
286862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    if not unattended:
287862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)      raise
288862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    else:
289862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)      for path in merge_common.ALL_PROJECTS:
290862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)        merge_common.GetCommandStdout(
291862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)            ['git', 'reset', '--hard'],
292862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)            cwd=os.path.join(merge_common.REPOSITORY_ROOT, path))
2932735b74c930c17d39cb6510c0c2c0a43df1d181aTorne (Richard Coles)      raise merge_common.TemporaryMergeError('Makefile generation failed: ' +
29472724a6d44b1f9ebc7e122045a72f3502185844dTorne (Richard Coles)                                             str(e))
2951c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
2961c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  for path in merge_common.ALL_PROJECTS:
2971c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
2989df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)    # git add doesn't have an --ignore-unmatch so we have to do this instead:
2995568eda06be15d573395eaba779d62c09f6f96f1Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'add', '-f', 'GypAndroid.*.mk'],
3001c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                  ignore_errors=True, cwd=dest_dir)
3015568eda06be15d573395eaba779d62c09f6f96f1Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'add', '-f', '*.target.*.mk'],
3021c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                  ignore_errors=True, cwd=dest_dir)
3035568eda06be15d573395eaba779d62c09f6f96f1Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'add', '-f', '*.host.*.mk'],
3041c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                  ignore_errors=True, cwd=dest_dir)
3051c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    merge_common.GetCommandStdout(['git', 'add', '-f', '*.tmp'],
3061c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                  ignore_errors=True, cwd=dest_dir)
3079df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)    # Only try to commit the makefiles if something has actually changed.
3089df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)    if _ModifiedFilesInIndex(dest_dir):
30982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)      merge_common.GetCommandStdout(
31082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)          ['git', 'commit', '-m',
311f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)           'Update makefiles after merge of Chromium at %s\n\n%s' %
312f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)           (version, AUTOGEN_MESSAGE)], cwd=dest_dir)
3136a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
3149df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)
3151c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)def _ModifiedFilesInIndex(cwd=merge_common.REPOSITORY_ROOT):
31682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Returns true if git's index contains any changes."""
3171c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  status = merge_common.GetCommandStdout(['git', 'status', '--porcelain'],
3181c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                         cwd=cwd)
31982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  return re.search(r'^[MADRC]', status, flags=re.MULTILINE) is not None
3206a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
3216a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
322f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)def _GenerateNoticeFile(version):
32382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Generates and commits a NOTICE file containing code licenses.
32482c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
32582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  This covers all third-party code (from Android's perspective) that lives in
32682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  the Chromium tree.
32782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
3286a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Args:
329f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    version: The version to mention in generated commit messages.
3306a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  """
3315d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)  logging.debug('Regenerating NOTICE file ...')
3326a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
3339df6a6c4638cfab15aa303682c15050a9509eb44Torne (Richard Coles)  contents = webview_licenses.GenerateNoticeFile()
3346a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
3351c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  with open(os.path.join(merge_common.REPOSITORY_ROOT, 'NOTICE'), 'w') as f:
3366a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    f.write(contents)
3371c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  merge_common.GetCommandStdout(['git', 'add', 'NOTICE'])
3386a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # Only try to commit the NOTICE update if the file has actually changed.
3396a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  if _ModifiedFilesInIndex():
3401c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    merge_common.GetCommandStdout([
3416a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)        'git', 'commit', '-m',
342f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)        'Update NOTICE file after merge of Chromium at %s\n\n%s'
343f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)        % (version, AUTOGEN_MESSAGE)])
3446a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
3456a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
346753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tuccidef _GenerateLastChange(version, root_sha1):
34782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Write a build/util/LASTCHANGE file containing the current revision.
34882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
34982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  The revision number is compiled into the binary at build time from this file.
35082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
351e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)  Args:
352f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    version: The version to mention in generated commit messages.
353753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    root_sha1: The SHA1 of the main project (before the merge).
354e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)  """
3555d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)  logging.debug('Updating LASTCHANGE ...')
3561c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  with open(os.path.join(merge_common.REPOSITORY_ROOT, 'build/util/LASTCHANGE'),
3571c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)            'w') as f:
35870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci    f.write('LASTCHANGE=%s\n' % merge_common.Abbrev(root_sha1))
3591c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  merge_common.GetCommandStdout(['git', 'add', '-f', 'build/util/LASTCHANGE'])
360d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)  logging.debug('Updating LASTCHANGE.blink ...')
361d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)  with open(os.path.join(merge_common.REPOSITORY_ROOT,
362d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)                         'build/util/LASTCHANGE.blink'), 'w') as f:
363d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)    f.write('LASTCHANGE=%s\n' % _GetBlinkRevision())
364d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)  merge_common.GetCommandStdout(['git', 'add', '-f',
365d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)                                 'build/util/LASTCHANGE.blink'])
366e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)  if _ModifiedFilesInIndex():
3671c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    merge_common.GetCommandStdout([
368e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)        'git', 'commit', '-m',
369f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)        'Update LASTCHANGE file after merge of Chromium at %s\n\n%s'
370f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)        % (version, AUTOGEN_MESSAGE)])
371e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)
372e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)
373a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liudef GetHEAD():
374753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  """Fetch the latest HEAD revision from the Chromium Git mirror.
375a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu
376a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu  Returns:
377753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    The latest HEAD revision (A Git abbrev SHA1).
378a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu  """
379753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  return _GetGitAbbrevSHA1(SRC_GIT_BRANCH, 'HEAD')
380a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu
381a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu
3823977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liudef _ParseSvnRevisionFromGitCommitMessage(commit_message):
3833977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu  return re.search(r'^git-svn-id: .*@([0-9]+)', commit_message,
3843977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu                   flags=re.MULTILINE).group(1)
3853977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu
3863977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu
387753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tuccidef _GetGitAbbrevSHA1(git_branch, revision):
38870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  """Returns an abbrev. SHA for the given revision (or branch, if HEAD)."""
38970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  assert revision
39070e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  logging.debug('Getting Git revision for %s ...', revision)
391753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci
392753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  upstream = git_branch if revision == 'HEAD' else revision
3933977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu
394d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci  # Make sure the remote and the branch exist locally.
395d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci  try:
396d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci    merge_common.GetCommandStdout([
397d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci        'git', 'show-ref', '--verify', '--quiet', git_branch])
398d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci  except merge_common.CommandError:
399d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci    raise merge_common.TemporaryMergeError(
400d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci        'Cannot find the branch %s. Have you sync\'d master-chromium in this '
401d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci        'checkout?' % git_branch)
402d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci
403753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  # Make sure the |upstream| Git object has been mirrored.
404753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  try:
405753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    merge_common.GetCommandStdout([
406753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci        'git', 'merge-base', '--is-ancestor', upstream, git_branch])
407753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  except merge_common.CommandError:
408753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    raise merge_common.TemporaryMergeError(
409753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci        'Upstream object (%s) not reachable from %s' % (upstream, git_branch))
4103977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu
41170e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  abbrev_sha = merge_common.Abbrev(merge_common.GetCommandStdout(
41270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci      ['git', 'rev-list', '--max-count=1', upstream]).split()[0])
41370e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  return abbrev_sha
4146a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
4156a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
416d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)def _GetBlinkRevision():
41770e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  # TODO(primiano): Switch to Git as soon as Blink gets migrated as well.
41870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  commit = merge_common.GetCommandStdout(
41970e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci      ['git', 'log', '-n1', '--grep=git-svn-id:', '--format=%H%n%b'],
420d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)      cwd=os.path.join(merge_common.REPOSITORY_ROOT, 'third_party', 'WebKit'))
421d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)  return _ParseSvnRevisionFromGitCommitMessage(commit)
422d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)
423d52d6a84a5dc0ba5264915b2e1524f65d1841b3bTorne (Richard Coles)
424753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tuccidef Snapshot(root_sha1, release, target, unattended, buildspec_url):
42582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Takes a snapshot of the Chromium tree and merges it into Android.
42682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
42782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Android makefiles and a top-level NOTICE file are generated and committed
42882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  after the merge.
42982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
4306a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  Args:
431753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    root_sha1: The abbrev sha1 in the Chromium git mirror to merge from.
432f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    release: The Chromium release version to merge from (e.g. "30.0.1599.20").
433753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci             Only one of root_sha1 and release should be specified.
434f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    target: The target branch to merge to.
43514ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)    unattended: Run in unattended mode.
436f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    buildspec_url: URL for buildspec repository, used when merging a release.
4376a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
43882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Returns:
43982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    True if new commits were merged; False if no new commits were present.
44082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """
441753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  if release:
442f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    root_sha1 = None
443753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    version = release
444f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  else:
445753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    root_sha1 = _GetGitAbbrevSHA1(SRC_GIT_BRANCH, root_sha1)
446753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    version = root_sha1
447753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci
44870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci  assert (root_sha1 is not None and len(root_sha1) > 6) or version == release
449753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci
450753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  if root_sha1 and not merge_common.GetCommandStdout(
451753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      ['git', 'rev-list', '-1', 'HEAD..' + root_sha1]):
45270e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci    logging.info('No new commits to merge at %s (%s)', version, root_sha1)
453753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    return False
454f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)
455f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  logging.info('Snapshotting Chromium at %s (%s)', version, root_sha1)
4566a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
4576a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  # 1. Merge, accounting for excluded directories
458f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  merged_sha1 = _MergeProjects(version, root_sha1, target, unattended,
459f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci                               buildspec_url)
4606a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
4613ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)  # 2. Generate Android makefiles
462f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  _GenerateMakefiles(version, unattended)
4633ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)
4643ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)  # 3. Check for incompatible licenses
4653ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)  _CheckLicenses()
4663ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)
4673ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)  # 4. Generate Android NOTICE file
468f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  _GenerateNoticeFile(version)
4696a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
4703ec6f231ebe47f01e8fada97209d1b888d757615Torne (Richard Coles)  # 5. Generate LASTCHANGE file
471f2e9853ae82a5b792ddf1af7c7d8924e9cb8c9b4Primiano Tucci  _GenerateLastChange(version, merged_sha1)
472e9d946910fc23e839e0ba89df0a59cdcd1aac8ddTorne (Richard Coles)
4731896d5d734a45c412f47fe58816665d374617d3bTorne (Richard Coles)  return True
4741896d5d734a45c412f47fe58816665d374617d3bTorne (Richard Coles)
4756a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
476f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)def Push(version, target):
477bda7cff5dd92725ba000fe95d9434f81a7af486eTorne (Richard Coles)  """Push the finished snapshot to the Android repository."""
478f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  src = 'merge-from-chromium-%s' % version
4793b1480d1d7d07cb989630cd5767f439dd3f5fabdTorne (Richard Coles)  # Use forced pushes ('+' prefix) for the temporary and archive branches in
4803b1480d1d7d07cb989630cd5767f439dd3f5fabdTorne (Richard Coles)  # case they already got updated by a previous (possibly failed?) merge, but
4813b1480d1d7d07cb989630cd5767f439dd3f5fabdTorne (Richard Coles)  # do not force push to the real master-chromium branch as this could erase
4823b1480d1d7d07cb989630cd5767f439dd3f5fabdTorne (Richard Coles)  # downstream changes.
483f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  refspecs = ['%s:%s' % (src, target),
484f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)              '+%s:refs/archive/chromium-%s' % (src, version)]
485f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  if target == 'master-chromium':
486f533bc666f11273be2d7a211be4cda18d53bf0a3Torne (Richard Coles)    refspecs.insert(0, '+%s:master-chromium-merge' % src)
487f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  for refspec in refspecs:
48870e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci    logging.debug('Pushing to server (%s) ...', refspec)
48909ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)    for path in merge_common.ALL_PROJECTS:
49009ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)      if path in merge_common.PROJECTS_WITH_FLAT_HISTORY:
49109ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)        remote = 'history'
49209ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)      else:
49309ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)        remote = 'goog'
49409ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)      logging.debug('Pushing %s', path)
49509ead754abd2cfcffea54040b1c9ac98db8af834Torne (Richard Coles)      dest_dir = os.path.join(merge_common.REPOSITORY_ROOT, path)
4963b1480d1d7d07cb989630cd5767f439dd3f5fabdTorne (Richard Coles)      merge_common.GetCommandStdout(['git', 'push', remote, refspec],
4973b1480d1d7d07cb989630cd5767f439dd3f5fabdTorne (Richard Coles)                                    cwd=dest_dir)
498bda7cff5dd92725ba000fe95d9434f81a7af486eTorne (Richard Coles)
499bda7cff5dd92725ba000fe95d9434f81a7af486eTorne (Richard Coles)
5006a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)def main():
5016a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  parser = optparse.OptionParser(usage='%prog [options]')
5026a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  parser.epilog = ('Takes a snapshot of the Chromium tree at the specified '
503753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci                   'Chromium Git revision and merges it into this repository. '
5046a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)                   'Paths marked as excluded for license reasons are removed '
5056a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)                   'as part of the merge. Also generates Android makefiles and '
5066a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)                   'generates a top-level NOTICE file suitable for use in the '
5076a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)                   'Android build.')
5086a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  parser.add_option(
5093977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu      '', '--sha1',
510753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      default='HEAD',
511753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      help=('Merge to the specified chromium sha1 revision from ' +
512753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci            SRC_GIT_BRANCH + ' branch. Default is HEAD, to merge from ToT.'))
513f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  parser.add_option(
514f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      '', '--release',
515f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      default=None,
516753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      help=('Merge to the specified chromium release buildspec (e.g., "30.0.'
517753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci            '1599.20"). Only one of --sha1 and --release should be specified'))
518f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  parser.add_option(
519f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      '', '--buildspec_url',
520f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      default=None,
521f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      help=('Git URL for buildspec repository.'))
522f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)  parser.add_option(
523f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      '', '--target',
524f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      default='master-chromium', metavar='BRANCH',
525f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      help=('Target branch to push to. Defaults to master-chromium.'))
5261896d5d734a45c412f47fe58816665d374617d3bTorne (Richard Coles)  parser.add_option(
52714ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)      '', '--push',
52814ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)      default=False, action='store_true',
5293977434d5c3aafc00eaae0d2e78fa8ddd72536f3Bo Liu      help=('Push the result of a previous merge to the server. Note '
530753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci            '--sha1 must be given.'))
5319fb672be51066b51a372885377cc0eff78e1a34cTorne (Richard Coles)  parser.add_option(
532a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu      '', '--get_head',
533a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu      default=False, action='store_true',
534a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu      help=('Just print the current HEAD revision on stdout and exit.'))
535a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu  parser.add_option(
53614ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)      '', '--unattended',
5371896d5d734a45c412f47fe58816665d374617d3bTorne (Richard Coles)      default=False, action='store_true',
53814ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)      help=('Run in unattended mode.'))
539d2a42d0b062f4c9bb4c3fefc80cde98350b5974eTorne (Richard Coles)  parser.add_option(
540d2a42d0b062f4c9bb4c3fefc80cde98350b5974eTorne (Richard Coles)      '', '--no_changes_exit',
541d2a42d0b062f4c9bb4c3fefc80cde98350b5974eTorne (Richard Coles)      default=0, type='int',
542d2a42d0b062f4c9bb4c3fefc80cde98350b5974eTorne (Richard Coles)      help=('Exit code to use if there are no changes to merge, for scripts.'))
5436a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  (options, args) = parser.parse_args()
5446a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  if args:
5456a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    parser.print_help()
5466a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    return 1
5476a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
5486a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  if 'ANDROID_BUILD_TOP' not in os.environ:
5496a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    print >>sys.stderr, 'You need to run the Android envsetup.sh and lunch.'
5506a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)    return 1
5516a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
552d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci  if os.environ.get('GYP_DEFINES'):
553d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci    print >>sys.stderr, (
554d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci        'The environment is defining GYP_DEFINES (=%s). It will affect the '
555d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci        ' generated makefiles.' % os.environ['GYP_DEFINES'])
556d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci    if not options.unattended and raw_input('Continue? [y/N]') != 'y':
557d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci      return 1
558d7b0b1b361c4cc745274a6e754b0ef2d57c73c4bPrimiano Tucci
5595d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)  logging.basicConfig(format='%(message)s', level=logging.DEBUG,
5605d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)                      stream=sys.stdout)
5615d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)
562753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci  if options.get_head:
563a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu    logging.disable(logging.CRITICAL)  # Prevent log messages
564a611c7e365068e2130b6de87b2b14e3b75ae1d4eBo Liu    print GetHEAD()
5659fb672be51066b51a372885377cc0eff78e1a34cTorne (Richard Coles)  elif options.push:
566f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)    if options.release:
567f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      Push(options.release, options.target)
568753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    elif options.sha1:
569753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci      Push(options.sha1, options.target)
570862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    else:
571f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      print >>sys.stderr, 'You need to pass the version to push.'
572f81a1ea8129a3a620d677d6eeab99d2727367c88Torne (Richard Coles)      return 1
57314ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)  else:
574753233e1666d4a8f69a4fae4345f9590f19589efPrimiano Tucci    if not Snapshot(options.sha1, options.release, options.target,
57570e496d940c22bf07cd2c5634e1819276ddac416Primiano Tucci                    options.unattended, options.buildspec_url):
576d2a42d0b062f4c9bb4c3fefc80cde98350b5974eTorne (Richard Coles)      return options.no_changes_exit
57714ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)
5786a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  return 0
5796a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)
5806a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)if __name__ == '__main__':
5816a8c82cdaffde31477cacabea89afefeded67f3eTorne (Richard Coles)  sys.exit(main())
582