11c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# Copyright (C) 2012 The Android Open Source Project
21c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)#
31c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# Licensed under the Apache License, Version 2.0 (the "License");
41c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# you may not use this file except in compliance with the License.
51c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# You may obtain a copy of the License at
61c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)#
71c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)#      http://www.apache.org/licenses/LICENSE-2.0
81c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)#
91c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# Unless required by applicable law or agreed to in writing, software
101c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# distributed under the License is distributed on an "AS IS" BASIS,
111c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
121c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# See the License for the specific language governing permissions and
131c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# limitations under the License.
141c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
1582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)"""Common data/functions for the Chromium merging scripts."""
161c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
175d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)import logging
181c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)import os
191c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)import re
201c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)import subprocess
211c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
221c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
231c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)REPOSITORY_ROOT = os.path.join(os.environ['ANDROID_BUILD_TOP'],
241c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                               'external/chromium_org')
251c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
261c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
271c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# Whitelist of projects that need to be merged to build WebView. We don't need
281c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)# the other upstream repositories used to build the actual Chrome app.
2982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)# Different stages of the merge process need different ways of looking at the
3082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)# list, so we construct different combinations below.
3182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
3233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)THIRD_PARTY_PROJECTS_WITH_FLAT_HISTORY = [
3333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)    'third_party/WebKit',
3433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)]
3533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
3633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)THIRD_PARTY_PROJECTS_WITH_FULL_HISTORY = [
371c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'sdch/open-vcdiff',
381c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'testing/gtest',
39c057d5bb4006c946b148106e1536a0570047e84fBo Liu    'third_party/angle_dx11',
4046e06376d64605bfae5337fbd2588537c8a1f982Torne (Richard Coles)    'third_party/eyesfree/src/android/java/src/com/googlecode/eyesfree/braille',
411c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/freetype',
421c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/icu',
431c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/leveldatabase/src',
44fe1e1418b6c6d4d9492feb627aa2af5fadd8eb53Torne (Richard Coles)    'third_party/libjingle/source/talk',
451c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/libphonenumber/src/phonenumbers',
461c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/libphonenumber/src/resources',
472ad1105953c418b574ecffa8b002d41d0564e9d7Ben Murdoch    'third_party/mesa/src',
481c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/openssl',
49fc21d2e7a06dcdbe43f35f1ba20d9e8770738339Ben Murdoch    'third_party/opus/src',
501c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/ots',
511c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/skia/include',
521c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/skia/gyp',
531c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'third_party/skia/src',
54cadf645507c329e0f5d7fbb67d9311cfa26ac638Torne (Richard Coles)    'third_party/smhasher/src',
55b455389686a6d4225f9270c4a2f63e03a8c0f6cdTorne (Richard Coles)    'third_party/yasm/source/patched-yasm',
561c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'tools/grit',
571c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'tools/gyp',
581c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    'v8',
591c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)]
601c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
6133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)PROJECTS_WITH_FLAT_HISTORY = ['.'] + THIRD_PARTY_PROJECTS_WITH_FLAT_HISTORY
6233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)PROJECTS_WITH_FULL_HISTORY = THIRD_PARTY_PROJECTS_WITH_FULL_HISTORY
6333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
6433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)THIRD_PARTY_PROJECTS = (THIRD_PARTY_PROJECTS_WITH_FLAT_HISTORY +
6533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)                        THIRD_PARTY_PROJECTS_WITH_FULL_HISTORY)
6633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
671c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)ALL_PROJECTS = ['.'] + THIRD_PARTY_PROJECTS
681c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
691c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
7033e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)# Directories to be removed when flattening history.
7133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)PRUNE_WHEN_FLATTENING = {
7233e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)    'third_party/WebKit': [
7333e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)        'LayoutTests',
7433e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)    ],
7533e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)}
7633e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
7733e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
7882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)# Only projects that have their history flattened can have directories pruned.
7933e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)assert all(p in PROJECTS_WITH_FLAT_HISTORY for p in PRUNE_WHEN_FLATTENING)
8033e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
8133e5c69dc781743cc2560bef57763170b61bae3fTorne (Richard Coles)
8282c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)class MergeError(Exception):
8382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Used to signal an error that prevents the merge from being completed."""
8482c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
8582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
8682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)class CommandError(MergeError):
8782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """This exception is raised when a process run by GetCommandStdout fails."""
8882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
8982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  def __init__(self, returncode, cmd, cwd, stdout, stderr):
9082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    super(CommandError, self).__init__()
9182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    self.returncode = returncode
9282c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    self.cmd = cmd
9382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    self.cwd = cwd
9482c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    self.stdout = stdout
9582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    self.stderr = stderr
9682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
9782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  def __str__(self):
9882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    return ("Command '%s' returned non-zero exit status %d. cwd was '%s'.\n\n"
9982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)            "===STDOUT===\n%s\n===STDERR===\n%s\n" %
10082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)            (self.cmd, self.returncode, self.cwd, self.stdout, self.stderr))
10182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
10282c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
103862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)class TemporaryMergeError(MergeError):
104862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)  """A merge error that can potentially be resolved by trying again later."""
105862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)
106862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)
1071c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)def GetCommandStdout(args, cwd=REPOSITORY_ROOT, ignore_errors=False):
1081c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  """Gets stdout from runnng the specified shell command.
10982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
11082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Similar to subprocess.check_output() except that it can capture stdout and
11182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  stderr separately for better error reporting.
11282c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
1131c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  Args:
11482c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    args: The command and its arguments as an iterable.
1151c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    cwd: The working directory to use. Defaults to REPOSITORY_ROOT.
11682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    ignore_errors: Ignore the command's return code and stderr.
1171c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  Returns:
1181c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    stdout from running the command.
11982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Raises:
12082c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    CommandError: if the command exited with a nonzero status.
1211c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  """
1221c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  p = subprocess.Popen(args=args, cwd=cwd, stdout=subprocess.PIPE,
1231c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                       stderr=subprocess.PIPE)
1241c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  stdout, stderr = p.communicate()
1251c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  if p.returncode == 0 or ignore_errors:
1261c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    return stdout
1271c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  else:
12882c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    raise CommandError(p.returncode, ' '.join(args), cwd, stdout, stderr)
1291c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
1301c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
13114ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)def CheckNoConflictsAndCommitMerge(commit_message, unattended=False,
13214ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)                                   cwd=REPOSITORY_ROOT):
13382c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  """Checks for conflicts and commits once they are resolved.
13482c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
13582c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  Certain conflicts are resolved automatically; if any remain, the user is
13682c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)  prompted to resolve them. The user can specify a custom commit message.
13782c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)
1381c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  Args:
13982c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    commit_message: The default commit message.
14014ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)    unattended: If running unattended, abort on conflicts.
14182c98161b9cd8e8f94e00d89b6fd56540473b3d4Torne (Richard Coles)    cwd: Working directory to use.
14214ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)  Raises:
143862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)    TemporaryMergeError: If there are conflicts in unattended mode.
1441c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  """
1451c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  status = GetCommandStdout(['git', 'status', '--porcelain'], cwd=cwd)
1461c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  conflicts_deleted_by_us = re.findall(r'^(?:DD|DU) ([^\n]+)$', status,
1471c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                       flags=re.MULTILINE)
1481c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  if conflicts_deleted_by_us:
1495d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)    logging.info('Keeping ours for the following locally deleted files.\n  %s',
1505d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)                 '\n  '.join(conflicts_deleted_by_us))
1511c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    GetCommandStdout(['git', 'rm', '-rf', '--ignore-unmatch'] +
1521c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                     conflicts_deleted_by_us, cwd=cwd)
1531c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
1541c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  # If upstream renames a file we have deleted then it will conflict, but
1551c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  # we shouldn't just blindly delete these files as they may have been renamed
1561c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  # into a directory we don't delete. Let them get re-added; they will get
1571c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  # re-deleted if they are still in a directory we delete.
1581c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  conflicts_renamed_by_them = re.findall(r'^UA ([^\n]+)$', status,
1591c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                                         flags=re.MULTILINE)
1601c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  if conflicts_renamed_by_them:
1615d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)    logging.info('Adding theirs for the following locally deleted files.\n %s',
1625d65b76f6747f51bb623d00205aca85581ad4d91Torne (Richard Coles)                 '\n  '.join(conflicts_renamed_by_them))
1631c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    GetCommandStdout(['git', 'add', '-f'] + conflicts_renamed_by_them, cwd=cwd)
1641c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
1651c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  while True:
1661c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    status = GetCommandStdout(['git', 'status', '--porcelain'], cwd=cwd)
1671c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    conflicts = re.findall(r'^((DD|AU|UD|UA|DU|AA|UU) [^\n]+)$', status,
1681c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)                           flags=re.MULTILINE)
1691c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    if not conflicts:
1701c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)      break
17114ac2c8d49fa1f5f12e6a80af93f1c50312d80a8Torne (Richard Coles)    if unattended:
172862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)      GetCommandStdout(['git', 'reset', '--hard'], cwd=cwd)
173862edb04ee66c65ce977d9b14b8809aad40749acTorne (Richard Coles)      raise TemporaryMergeError('Cannot resolve merge conflicts.')
1741c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    conflicts_string = '\n'.join([x[0] for x in conflicts])
1751c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    new_commit_message = raw_input(
1761c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)        ('The following conflicts exist and must be resolved.\n\n%s\n\nWhen '
1771c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)         'done, enter a commit message or press enter to use the default '
1781c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)         '(\'%s\').\n\n') % (conflicts_string, commit_message))
1791c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)    if new_commit_message:
1801c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)      commit_message = new_commit_message
1811c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)
1821c263f2b522bcb9159547d14f9f57abac46967c7Torne (Richard Coles)  GetCommandStdout(['git', 'commit', '-m', commit_message], cwd=cwd)
183