| #!/usr/bin/env python |
| # Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ Create (if needed) and sync a nested checkout of Skia inside of Chrome. """ |
| |
| from config_private import SKIA_GIT_URL |
| from common import chromium_utils |
| from optparse import OptionParser |
| |
| import gclient_utils |
| import os |
| import re |
| import shell_utils |
| import shlex |
| import sys |
| import urllib2 |
| |
| |
| CHROME_LKGR_URL = 'http://chromium-status.appspot.com/git-lkgr' |
| FETCH = 'fetch.bat' if os.name == 'nt' else 'fetch' |
| GCLIENT = 'gclient.bat' if os.name == 'nt' else 'gclient' |
| GIT = gclient_utils.GIT |
| GCLIENT_FILE = '.gclient' |
| PATH_TO_SKIA_IN_CHROME = os.path.join('src', 'third_party', 'skia', 'src') |
| |
| |
| def Sync(skia_revision=None, chrome_revision=None, use_lkgr_skia=False): |
| """ Create and sync a checkout of Skia inside a checkout of Chrome. Returns |
| a tuple containing the actually-obtained revision of Skia and the actually- |
| obtained revision of Chrome. |
| |
| skia_revision: revision of Skia to sync. If None, will attempt to determine |
| the most recent Skia revision. Ignored if use_lkgr_skia is True. |
| chrome_revision: revision of Chrome to sync. If None, will use the LKGR. |
| use_lkgr_skia: boolean; if True, leaves Skia at the revision requested by |
| Chrome instead of using skia_revision. |
| """ |
| # Figure out what revision of Skia we should use. |
| if not skia_revision: |
| output = shell_utils.run([GIT, 'ls-remote', SKIA_GIT_URL, '--verify', |
| 'refs/heads/master']) |
| if output: |
| skia_revision = shlex.split(output)[0] |
| if not skia_revision: |
| raise Exception('Could not determine current Skia revision!') |
| skia_revision = str(skia_revision) |
| |
| # Use Chrome LKGR, since gclient_utils will force a sync to origin/master. |
| if not chrome_revision: |
| chrome_revision = urllib2.urlopen(CHROME_LKGR_URL).read() |
| |
| # Run "fetch chromium". The initial run is allowed to fail after it does some |
| # work. At the least, we expect the .gclient file to be present when it |
| # finishes. |
| if not os.path.isfile(GCLIENT_FILE): |
| try: |
| shell_utils.run([FETCH, 'chromium', '--nosvn=True']) |
| except shell_utils.CommandFailedException: |
| pass |
| if not os.path.isfile(GCLIENT_FILE): |
| raise Exception('Could not fetch chromium!') |
| |
| # Hack the .gclient file to use LKGR and NOT check out Skia. |
| gclient_vars = {} |
| execfile(GCLIENT_FILE, gclient_vars) |
| for solution in gclient_vars['solutions']: |
| if solution['name'] == 'src': |
| solution['safesync_url'] = CHROME_LKGR_URL |
| if not solution.get('custom_deps'): |
| solution['custom_deps'] = {} |
| solution['managed'] = True |
| solution['custom_deps']['src/third_party/skia/gyp'] = None |
| solution['custom_deps']['src/third_party/skia/include'] = None |
| solution['custom_deps']['src/third_party/skia/src'] = None |
| break |
| print 'Writing %s:' % GCLIENT_FILE |
| with open(GCLIENT_FILE, 'w') as gclient_file: |
| for k, v in gclient_vars.iteritems(): |
| if not k.startswith('_'): |
| write_str = '%s = %s\n' % (str(k), str(v)) |
| print write_str |
| gclient_file.write(write_str) |
| |
| # Run "gclient sync" |
| try: |
| # Hack: We have to set some GYP_DEFINES, or upstream scripts will complain. |
| os.environ['GYP_DEFINES'] = os.environ.get('GYP_DEFINES') or '' |
| gclient_utils.Sync( |
| revisions=[('src', chrome_revision)], |
| jobs=1, |
| no_hooks=True, |
| force=True) |
| except shell_utils.CommandFailedException as e: |
| # We frequently see sync failures because a lock file wasn't deleted. In |
| # that case, delete the lock file and try again. |
| pattern = r".*fatal: Unable to create '(\S+)': File exists\..*" |
| match = re.search(pattern, e.output) |
| if not match: |
| raise e |
| file_to_delete = match.groups()[0] |
| try: |
| print 'Attempting to remove %s' % file_to_delete |
| os.remove(file_to_delete) |
| except OSError: |
| # If the file no longer exists, just try again. |
| pass |
| gclient_utils.Sync( |
| revisions=[('src', chrome_revision)], |
| jobs=1, |
| no_hooks=True, |
| force=True) |
| |
| # Find the actually-obtained Chrome revision. |
| os.chdir('src') |
| actual_chrome_rev = shell_utils.run([GIT, 'rev-parse', 'HEAD'], |
| log_in_real_time=False).rstrip() |
| |
| if use_lkgr_skia: |
| # Get the Skia revision requested by Chrome. |
| deps_vars = {} |
| deps_vars['Var'] = lambda x: deps_vars['vars'][x] |
| execfile('DEPS', deps_vars) |
| skia_revision = deps_vars['vars']['skia_hash'] |
| print 'Overriding skia_revision with %s' % skia_revision |
| |
| # Check out Skia. |
| skia_dir = os.path.join('third_party', 'skia') |
| print 'cd %s' % skia_dir |
| os.chdir(skia_dir) |
| try: |
| # Determine whether we already have a Skia checkout. If so, just update. |
| if not 'skia' in shell_utils.run([GIT, 'remote', '-v']): |
| raise Exception('%s does not contain a Skia checkout!' % skia_dir) |
| current_skia_rev = shell_utils.run([GIT, 'rev-parse', 'HEAD']).rstrip() |
| print 'Found existing Skia checkout at %s' % current_skia_rev |
| shell_utils.run([GIT, 'reset', '--hard', 'HEAD']) |
| shell_utils.run([GIT, 'checkout', 'master']) |
| shell_utils.run([GIT, 'fetch']) |
| shell_utils.run([GIT, 'reset', '--hard', 'origin/master']) |
| except Exception: |
| # If updating fails, assume that we need to check out Skia from scratch. |
| os.chdir(os.pardir) |
| chromium_utils.RemoveDirectory('skia') |
| shell_utils.run([GIT, 'clone', SKIA_GIT_URL, 'skia']) |
| os.chdir('skia') |
| shell_utils.run([GIT, 'reset', '--hard', skia_revision]) |
| |
| # Find the actually-obtained Skia revision. |
| actual_skia_rev = shell_utils.run([GIT, 'rev-parse', 'HEAD'], |
| log_in_real_time=False).rstrip() |
| |
| # Run gclient hooks |
| os.chdir(os.path.join(os.pardir, os.pardir, os.pardir)) |
| shell_utils.run([GCLIENT, 'runhooks']) |
| |
| # Verify that we got the requested revisions of Chrome and Skia. |
| if skia_revision != actual_skia_rev: |
| raise Exception('Requested Skia revision %s but got %s!' % ( |
| repr(skia_revision), repr(actual_skia_rev))) |
| if chrome_revision and chrome_revision != actual_chrome_rev: |
| raise Exception('Requested Chrome revision %s but got %s!' % ( |
| repr(chrome_revision), repr(actual_chrome_rev))) |
| |
| return (actual_skia_rev, actual_chrome_rev) |
| |
| |
| def Main(): |
| parser = OptionParser() |
| parser.add_option('--skia_revision', |
| help=('Desired revision of Skia. Defaults to the most ' |
| 'recent revision.')) |
| parser.add_option('--chrome_revision', |
| help=('Desired revision of Chrome. Defaults to the Last ' |
| 'Known Good Revision.')) |
| parser.add_option('--destination', |
| help=('Where to sync the code. Defaults to the current ' |
| 'directory.'), |
| default=os.curdir) |
| (options, _) = parser.parse_args() |
| dest_dir = os.path.abspath(options.destination) |
| cur_dir = os.path.abspath(os.curdir) |
| os.chdir(dest_dir) |
| try: |
| actual_skia_rev, actual_chrome_rev = Sync(options.skia_revision, |
| options.chrome_revision) |
| print 'Chrome synced to %s' % actual_chrome_rev |
| print 'Skia synced to %s' % actual_skia_rev |
| finally: |
| os.chdir(cur_dir) |
| |
| |
| if __name__ == '__main__': |
| sys.exit(Main()) |