1#!/usr/bin/env python 2# Copyright (c) 2011 The Chromium Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Update third_party/WebKit using git. 7 8Under the assumption third_party/WebKit is a clone of git.webkit.org, 9we can use git commands to make it match the version requested by DEPS. 10 11See http://code.google.com/p/chromium/wiki/UsingWebKitGit for details on 12how to use this. 13""" 14 15import logging 16import optparse 17import os 18import re 19import subprocess 20import sys 21import urllib 22 23 24def RunGit(command): 25 """Run a git subcommand, returning its output.""" 26 # On Windows, use shell=True to get PATH interpretation. 27 command = ['git'] + command 28 logging.info(' '.join(command)) 29 shell = (os.name == 'nt') 30 proc = subprocess.Popen(command, shell=shell, stdout=subprocess.PIPE) 31 out = proc.communicate()[0].strip() 32 logging.info('Returned "%s"' % out) 33 return out 34 35 36def GetOverrideShortBranchName(): 37 """Returns the user-configured override branch name, if any.""" 38 override_config_name = 'chromium.sync-branch' 39 return RunGit(['config', '--get', override_config_name]) 40 41 42def GetGClientBranchName(): 43 """Returns the name of the magic branch that lets us know that DEPS is 44 managing the update cycle.""" 45 # Is there an override branch specified? 46 override_branch_name = GetOverrideShortBranchName() 47 if not override_branch_name: 48 return 'refs/heads/gclient' # No override, so return the default branch. 49 50 # Verify that the branch from config exists. 51 ref_branch = 'refs/heads/' + override_branch_name 52 current_head = RunGit(['show-ref', '--hash', ref_branch]) 53 if current_head: 54 return ref_branch 55 56 # Inform the user about the problem and how to fix it. 57 print ("The specified override branch ('%s') doesn't appear to exist." % 58 override_branch_name) 59 print "Please fix your git config value '%s'." % overide_config_name 60 sys.exit(1) 61 62 63def GetWebKitRev(): 64 """Extract the 'webkit_revision' variable out of DEPS.""" 65 locals = {'Var': lambda _: locals["vars"][_], 66 'From': lambda *args: None} 67 execfile('DEPS', {}, locals) 68 return locals['vars']['webkit_revision'] 69 70 71def GetWebKitRevFromTarball(version): 72 """Extract the 'webkit_revision' variable out of tarball DEPS.""" 73 deps_url = "http://src.chromium.org/svn/releases/" + version + "/DEPS" 74 f = urllib.urlopen(deps_url) 75 s = f.read() 76 m = re.search('(?<=/Source@)\w+', s) 77 return m.group(0) 78 79 80def HasGitRev(target_rev): 81 """Finds if a git hash exists in the repository.""" 82 83 cmd = ['git', 'rev-list', '--max-count=1', target_rev] 84 logging.info(' '.join(cmd)) 85 result = subprocess.call(cmd, shell=(os.name == 'nt'), stdout=subprocess.PIPE) 86 return result == 0 87 88 89def GetRemote(): 90 branch = GetOverrideShortBranchName() 91 if not branch: 92 branch = 'gclient' 93 94 remote = RunGit(['config', '--get', 'branch.' + branch + '.remote']) 95 if remote: 96 return remote 97 return 'origin' 98 99 100def UpdateGClientBranch(webkit_rev, magic_gclient_branch): 101 """Update the magic gclient branch to point at |webkit_rev|. 102 103 Returns: true if the branch didn't need changes.""" 104 if not HasGitRev(webkit_rev): 105 print "%s not available; fetching." % webkit_rev 106 subprocess.check_call(['git', 'fetch', GetRemote()], 107 shell=(os.name == 'nt')) 108 if not HasGitRev(webkit_rev): 109 print "ERROR: Couldn't find %s in the repository." % webkit_rev 110 sys.exit(1) 111 112 current = RunGit(['show-ref', '--hash', magic_gclient_branch]) 113 if current == webkit_rev: 114 return False # No change necessary. 115 116 subprocess.check_call(['git', 'update-ref', '-m', 'gclient sync', 117 magic_gclient_branch, webkit_rev], 118 shell=(os.name == 'nt')) 119 return True 120 121 122def UpdateCurrentCheckoutIfAppropriate(magic_gclient_branch): 123 """Reset the current gclient branch if that's what we have checked out.""" 124 branch = RunGit(['symbolic-ref', '-q', 'HEAD']) 125 if branch != magic_gclient_branch: 126 print "We have now updated the 'gclient' branch, but third_party/WebKit" 127 print "has some other branch ('%s') checked out." % branch 128 print "Run 'git checkout gclient' under third_party/WebKit if you want" 129 print "to switch it to the version requested by DEPS." 130 return 1 131 132 if subprocess.call(['git', 'diff-index', '--exit-code', '--shortstat', 133 'HEAD'], shell=(os.name == 'nt')): 134 print "Resetting tree state to new revision." 135 subprocess.check_call(['git', 'reset', '--hard'], shell=(os.name == 'nt')) 136 137 138def main(): 139 parser = optparse.OptionParser() 140 parser.add_option('-v', '--verbose', action='store_true') 141 parser.add_option('-r', '--revision', help="switch to desired revision") 142 parser.add_option('-t', '--tarball', help="switch to desired tarball release") 143 options, args = parser.parse_args() 144 if options.verbose: 145 logging.basicConfig(level=logging.INFO) 146 if not os.path.exists('third_party/WebKit/.git'): 147 if os.path.exists('third_party/WebKit'): 148 print "ERROR: third_party/WebKit appears to not be under git control." 149 else: 150 print "ERROR: third_party/WebKit could not be found." 151 print "Did you run this script from the right directory?" 152 153 print "See http://code.google.com/p/chromium/wiki/UsingWebKitGit for" 154 print "setup instructions." 155 return 1 156 157 if options.revision: 158 webkit_rev = options.revision 159 if options.tarball: 160 print "WARNING: --revision is given, so ignore --tarball" 161 else: 162 if options.tarball: 163 webkit_rev = GetWebKitRevFromTarball(options.tarball) 164 else: 165 webkit_rev = GetWebKitRev() 166 167 print 'Desired revision: %s.' % webkit_rev 168 os.chdir('third_party/WebKit') 169 magic_gclient_branch = GetGClientBranchName() 170 changed = UpdateGClientBranch(webkit_rev, magic_gclient_branch) 171 if changed: 172 return UpdateCurrentCheckoutIfAppropriate(magic_gclient_branch) 173 else: 174 print "Already on correct revision." 175 return 0 176 177 178if __name__ == '__main__': 179 sys.exit(main()) 180