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 8 Under the assumption third_party/WebKit is a clone of git.webkit.org, 9 we can use git commands to make it match the version requested by DEPS. 10 11 See http://code.google.com/p/chromium/wiki/UsingWebKitGit for details on 12 how to use this. 13 """ 14 15 import logging 16 import optparse 17 import os 18 import re 19 import subprocess 20 import sys 21 import urllib 22 23 24 def 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 36 def 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 42 def 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 63 def 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 71 def 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 80 def 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 89 def 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 100 def 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 122 def 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 138 def 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 178 if __name__ == '__main__': 179 sys.exit(main()) 180