| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2013 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 """This module contains utilities for managing gclient checkouts.""" | |
| 7 | |
| 8 | |
| 9 from common import find_depot_tools | |
| 10 | |
| 11 from py.utils.git_utils import GIT | |
| 12 from py.utils import shell_utils | |
| 13 from py.utils import misc | |
| 14 import os | |
| 15 | |
| 16 | |
| 17 WHICH = 'where' if os.name == 'nt' else 'which' | |
| 18 SKIA_TRUNK = 'skia' | |
| 19 | |
| 20 | |
| 21 # TODO(borenet): There are a number of Git commands in this file. They should | |
| 22 # probably be added to git_utils. | |
| 23 | |
| 24 | |
| 25 def _GetGclientPy(): | |
| 26 """ Return the path to the gclient.py file. """ | |
| 27 path_to_gclient = find_depot_tools.add_depot_tools_to_path() | |
| 28 if path_to_gclient: | |
| 29 return os.path.join(path_to_gclient, 'gclient.py') | |
| 30 print 'Falling back on using "gclient" or "gclient.bat"' | |
| 31 if os.name == 'nt': | |
| 32 return 'gclient.bat' | |
| 33 else: | |
| 34 return 'gclient' | |
| 35 | |
| 36 | |
| 37 GCLIENT_PY = _GetGclientPy() | |
| 38 GCLIENT_FILE = '.gclient' | |
| 39 | |
| 40 | |
| 41 def _RunCmd(cmd): | |
| 42 """ Run a "gclient ..." command. """ | |
| 43 return shell_utils.run(['python', GCLIENT_PY] + cmd) | |
| 44 | |
| 45 | |
| 46 def GClient(): | |
| 47 """Run "gclient" without any command. | |
| 48 | |
| 49 This is useful because gclient installs things and updates itself, and we may | |
| 50 want it to do so before we attempt to do other things. | |
| 51 """ | |
| 52 return _RunCmd([]) | |
| 53 | |
| 54 | |
| 55 def Config(spec): | |
| 56 """ Configure a local checkout. """ | |
| 57 return _RunCmd(['config', '--spec=%s' % spec]) | |
| 58 | |
| 59 | |
| 60 def _GetLocalConfig(): | |
| 61 """Find and return the configuration for the local checkout. | |
| 62 | |
| 63 Returns: tuple of the form (checkout_root, solutions_dict), where | |
| 64 checkout_root is the path to the directory containing the .glient file, | |
| 65 and solutions_dict is the dictionary of solutions defined in .gclient. | |
| 66 """ | |
| 67 checkout_root = os.path.abspath(os.curdir) | |
| 68 depth = len(checkout_root.split(os.path.sep)) | |
| 69 # Start with the current working directory and move upwards until we find the | |
| 70 # .gclient file. | |
| 71 while not os.path.isfile(os.path.join(checkout_root, GCLIENT_FILE)): | |
| 72 if not depth: | |
| 73 raise Exception('Unable to find %s' % GCLIENT_FILE) | |
| 74 checkout_root = os.path.abspath(os.path.join(checkout_root, os.pardir)) | |
| 75 depth -= 1 | |
| 76 config_vars = {} | |
| 77 exec(open(os.path.join(checkout_root, GCLIENT_FILE)).read(), config_vars) | |
| 78 return checkout_root, config_vars['solutions'] | |
| 79 | |
| 80 | |
| 81 def maybe_fix_identity(username='chrome-bot', email='skia.committer@gmail.com'): | |
| 82 """If either of user.name or user.email is not defined, define it.""" | |
| 83 try: | |
| 84 shell_utils.run([GIT, 'config', '--get', 'user.name']) | |
| 85 except shell_utils.CommandFailedException: | |
| 86 shell_utils.run([GIT, 'config', 'user.name', '"%s"' % username]) | |
| 87 | |
| 88 try: | |
| 89 shell_utils.run([GIT, 'config', '--get', 'user.email']) | |
| 90 except shell_utils.CommandFailedException: | |
| 91 shell_utils.run([GIT, 'config', 'user.email', '"%s"' % email]) | |
| 92 | |
| 93 | |
| 94 def Sync(revisions=None, force=False, delete_unversioned_trees=False, | |
| 95 verbose=False, jobs=None, no_hooks=False, extra_args=None): | |
| 96 """ Update the local checkout using gclient. | |
| 97 | |
| 98 Args: | |
| 99 revisions: optional list of (branch, revision) tuples indicating which | |
| 100 projects to sync to which revisions. | |
| 101 force: whether to run with --force. | |
| 102 delete_unversioned_trees: whether to run with --delete-unversioned-trees. | |
| 103 verbose: whether to run with --verbose. | |
| 104 jobs: optional argument for the --jobs flag. | |
| 105 no_hooks: whether to run with --nohooks. | |
| 106 extra_args: optional list; any additional arguments. | |
| 107 """ | |
| 108 for branch, _ in (revisions or []): | |
| 109 # Do whatever it takes to get up-to-date with origin/master. | |
| 110 if os.path.exists(branch): | |
| 111 with misc.ChDir(branch): | |
| 112 # First, fix the git identity if needed. | |
| 113 maybe_fix_identity() | |
| 114 | |
| 115 # If there are local changes, "git checkout" will fail. | |
| 116 shell_utils.run([GIT, 'reset', '--hard', 'HEAD']) | |
| 117 # In case HEAD is detached... | |
| 118 shell_utils.run([GIT, 'checkout', 'master']) | |
| 119 # Always fetch, in case we're unmanaged. | |
| 120 shell_utils.run([GIT, 'fetch']) | |
| 121 # This updates us to origin/master even if master has diverged. | |
| 122 shell_utils.run([GIT, 'reset', '--hard', 'origin/master']) | |
| 123 | |
| 124 cmd = ['sync', '--no-nag-max'] | |
| 125 if verbose: | |
| 126 cmd.append('--verbose') | |
| 127 if force: | |
| 128 cmd.append('--force') | |
| 129 if delete_unversioned_trees: | |
| 130 cmd.append('--delete_unversioned_trees') | |
| 131 if jobs: | |
| 132 cmd.append('-j%d' % jobs) | |
| 133 if no_hooks: | |
| 134 cmd.append('--nohooks') | |
| 135 for branch, revision in (revisions or []): | |
| 136 if revision: | |
| 137 cmd.extend(['--revision', '%s@%s' % (branch, revision)]) | |
| 138 if extra_args: | |
| 139 cmd.extend(extra_args) | |
| 140 output = _RunCmd(cmd) | |
| 141 | |
| 142 # "gclient sync" just downloads all of the commits. In order to actually sync | |
| 143 # to the desired commit, we have to "git reset" to that commit. | |
| 144 for branch, revision in (revisions or []): | |
| 145 with misc.ChDir(branch): | |
| 146 if revision: | |
| 147 shell_utils.run([GIT, 'reset', '--hard', revision]) | |
| 148 else: | |
| 149 shell_utils.run([GIT, 'reset', '--hard', 'origin/master']) | |
| 150 return output | |
| 151 | |
| 152 | |
| 153 def GetCheckedOutHash(): | |
| 154 """ Determine what commit we actually got. If there are local modifications, | |
| 155 raise an exception. """ | |
| 156 checkout_root, config_dict = _GetLocalConfig() | |
| 157 | |
| 158 # Get the checked-out commit hash for the first gclient solution. | |
| 159 with misc.ChDir(os.path.join(checkout_root, config_dict[0]['name'])): | |
| 160 # First, print out the remote from which we synced, just for debugging. | |
| 161 cmd = [GIT, 'remote', '-v'] | |
| 162 try: | |
| 163 shell_utils.run(cmd) | |
| 164 except shell_utils.CommandFailedException as e: | |
| 165 print e | |
| 166 | |
| 167 # "git rev-parse HEAD" returns the commit hash for HEAD. | |
| 168 return shell_utils.run([GIT, 'rev-parse', 'HEAD'], | |
| 169 log_in_real_time=False).rstrip('\n') | |
| 170 | |
| 171 | |
| 172 def GetGitRepoPOSIXTimestamp(): | |
| 173 """Returns the POSIX timestamp for the current Skia commit as in int.""" | |
| 174 git_show_command = [GIT, 'show', '--format=%at', '-s'] | |
| 175 raw_timestamp = shell_utils.run( | |
| 176 git_show_command, log_in_real_time=False, echo=False, | |
| 177 print_timestamps=False) | |
| 178 return int(raw_timestamp) | |
| 179 | |
| 180 | |
| 181 # than extract the number for the current repo | |
| 182 def GetGitNumber(commit_hash): | |
| 183 """Returns the GIT number for the current Skia commit as in int.""" | |
| 184 try: | |
| 185 git_show_command = [GIT, 'number'] | |
| 186 git_number = shell_utils.run( | |
| 187 git_show_command, log_in_real_time=False, echo=False, | |
| 188 print_timestamps=False) | |
| 189 return int(git_number) | |
| 190 except shell_utils.CommandFailedException: | |
| 191 print 'GetGitNumber: Unable to get git number, returning -1' | |
| 192 return -1 | |
| 193 | |
| 194 | |
| 195 def Revert(): | |
| 196 shell_utils.run([GIT, 'clean', '-f', '-d']) | |
| 197 shell_utils.run([GIT, 'reset', '--hard', 'HEAD']) | |
| 198 | |
| 199 | |
| 200 def RunHooks(gyp_defines=None, gyp_generators=None): | |
| 201 """ Run "gclient runhooks". | |
| 202 | |
| 203 Args: | |
| 204 gyp_defines: optional string; GYP_DEFINES to be passed to Gyp. | |
| 205 gyp_generators: optional string; which GYP_GENERATORS to use. | |
| 206 """ | |
| 207 if gyp_defines: | |
| 208 os.environ['GYP_DEFINES'] = gyp_defines | |
| 209 print 'GYP_DEFINES="%s"' % os.environ['GYP_DEFINES'] | |
| 210 if gyp_generators: | |
| 211 os.environ['GYP_GENERATORS'] = gyp_generators | |
| 212 print 'GYP_GENERATORS="%s"' % os.environ['GYP_GENERATORS'] | |
| 213 | |
| 214 _RunCmd(['runhooks']) | |
| OLD | NEW |