Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7)

Side by Side Diff: fetch.py

Issue 106403003: fetch.py: Use '--nohooks' when calling 'gclient sync' (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Add --nohooks option to fetch.py Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright (c) 2013 The Chromium Authors. All rights reserved. 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 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """ 6 """
7 Tool to perform checkouts in one easy command line! 7 Tool to perform checkouts in one easy command line!
8 8
9 Usage: 9 Usage:
10 fetch <recipe> [--property=value [--property2=value2 ...]] 10 fetch <recipe> [--property=value [--property2=value2 ...]]
11 11
12 This script is a wrapper around various version control and repository 12 This script is a wrapper around various version control and repository
13 checkout commands. It requires a |recipe| name, fetches data from that 13 checkout commands. It requires a |recipe| name, fetches data from that
14 recipe in depot_tools/recipes, and then performs all necessary inits, 14 recipe in depot_tools/recipes, and then performs all necessary inits,
15 checkouts, pulls, fetches, etc. 15 checkouts, pulls, fetches, etc.
16 16
17 Optional arguments may be passed on the command line in key-value pairs. 17 Optional arguments may be passed on the command line in key-value pairs.
18 These parameters will be passed through to the recipe's main method. 18 These parameters will be passed through to the recipe's main method.
19 """ 19 """
20 20
21 import collections
21 import json 22 import json
22 import os 23 import os
23 import subprocess 24 import subprocess
24 import sys 25 import sys
25 import pipes 26 import pipes
26 27
27 from distutils import spawn 28 from distutils import spawn
28 29
29 30
30 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) 31 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__))
31 32
33 # Simple named tuple type for command line options.
34 # dryrun: if True, don't actually execute the commands.
35 # nohooks: if True, don't run hooks after a fetch/checkout.
36 Options = collections.namedtuple('Options', ['dryrun', 'nohooks'])
Dirk Pranke 2013/12/11 19:53:23 You could just import optparse and create an optpa
32 37
33 ################################################# 38 #################################################
34 # Checkout class definitions. 39 # Checkout class definitions.
35 ################################################# 40 #################################################
36 class Checkout(object): 41 class Checkout(object):
37 """Base class for implementing different types of checkouts. 42 """Base class for implementing different types of checkouts.
38 43
39 Attributes: 44 Attributes:
40 |base|: the absolute path of the directory in which this script is run. 45 |base|: the absolute path of the directory in which this script is run.
41 |spec|: the spec for this checkout as returned by the recipe. Different 46 |spec|: the spec for this checkout as returned by the recipe. Different
42 subclasses will expect different keys in this dictionary. 47 subclasses will expect different keys in this dictionary.
43 |root|: the directory into which the checkout will be performed, as returned 48 |root|: the directory into which the checkout will be performed, as returned
44 by the recipe. This is a relative path from |base|. 49 by the recipe. This is a relative path from |base|.
45 """ 50 """
46 def __init__(self, dryrun, spec, root): 51 def __init__(self, options, spec, root):
47 self.base = os.getcwd() 52 self.base = os.getcwd()
48 self.dryrun = dryrun 53 self.options = options
49 self.spec = spec 54 self.spec = spec
50 self.root = root 55 self.root = root
51 56
52 def exists(self): 57 def exists(self):
53 pass 58 pass
54 59
55 def init(self): 60 def init(self):
56 pass 61 pass
57 62
58 def sync(self): 63 def sync(self):
59 pass 64 pass
60 65
61 def run(self, cmd, **kwargs): 66 def run(self, cmd, **kwargs):
62 print 'Running: %s' % (' '.join(pipes.quote(x) for x in cmd)) 67 print 'Running: %s' % (' '.join(pipes.quote(x) for x in cmd))
63 if self.dryrun: 68 if self.options.dryrun:
64 return 0 69 return 0
65 return subprocess.check_call(cmd, **kwargs) 70 return subprocess.check_call(cmd, **kwargs)
66 71
67 72
68 class GclientCheckout(Checkout): 73 class GclientCheckout(Checkout):
69 74
70 def run_gclient(self, *cmd, **kwargs): 75 def run_gclient(self, *cmd, **kwargs):
71 if not spawn.find_executable('gclient'): 76 if not spawn.find_executable('gclient'):
72 cmd_prefix = (sys.executable, os.path.join(SCRIPT_PATH, 'gclient.py')) 77 cmd_prefix = (sys.executable, os.path.join(SCRIPT_PATH, 'gclient.py'))
73 else: 78 else:
(...skipping 16 matching lines...) Expand all
90 def run_svn(self, *cmd, **kwargs): 95 def run_svn(self, *cmd, **kwargs):
91 if sys.platform == 'win32' and not spawn.find_executable('svn'): 96 if sys.platform == 'win32' and not spawn.find_executable('svn'):
92 svn_path = os.path.join(SCRIPT_PATH, 'svn_bin', 'svn.exe') 97 svn_path = os.path.join(SCRIPT_PATH, 'svn_bin', 'svn.exe')
93 else: 98 else:
94 svn_path = 'svn' 99 svn_path = 'svn'
95 return self.run((svn_path,) + cmd, **kwargs) 100 return self.run((svn_path,) + cmd, **kwargs)
96 101
97 102
98 class GclientGitCheckout(GclientCheckout, GitCheckout): 103 class GclientGitCheckout(GclientCheckout, GitCheckout):
99 104
100 def __init__(self, dryrun, spec, root): 105 def __init__(self, options, spec, root):
101 super(GclientGitCheckout, self).__init__(dryrun, spec, root) 106 super(GclientGitCheckout, self).__init__(options, spec, root)
102 assert 'solutions' in self.spec 107 assert 'solutions' in self.spec
103 keys = ['solutions', 'target_os', 'target_os_only'] 108 keys = ['solutions', 'target_os', 'target_os_only']
104 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) 109 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key])
105 for key in keys if key in self.spec) 110 for key in keys if key in self.spec)
106 self.spec['gclient_spec'] = gclient_spec 111 self.spec['gclient_spec'] = gclient_spec
107 112
108 def exists(self): 113 def exists(self):
109 return os.path.exists(os.path.join(os.getcwd(), self.root)) 114 return os.path.exists(os.path.join(os.getcwd(), self.root))
110 115
111 def init(self): 116 def init(self):
112 # TODO(dpranke): Work around issues w/ delta compression on big repos. 117 # TODO(dpranke): Work around issues w/ delta compression on big repos.
113 self.run_git('config', '--global', 'core.deltaBaseCacheLimit', '1G') 118 self.run_git('config', '--global', 'core.deltaBaseCacheLimit', '1G')
114 119
115 # Configure and do the gclient checkout. 120 # Configure and do the gclient checkout.
116 self.run_gclient('config', '--spec', self.spec['gclient_spec']) 121 self.run_gclient('config', '--spec', self.spec['gclient_spec'])
117 self.run_gclient('sync') 122 gclient_cmd = 'sync'
123 if self.options.nohooks:
124 gclient_cmd = ('sync', '--nohooks')
125 self.run_gclient(gclient_cmd)
Dirk Pranke 2013/12/11 19:53:23 It seems like you have a type conflict here; gclie
digit1 2013/12/13 15:26:22 It's either a string or a tuple, and this works du
118 126
119 # Configure git. 127 # Configure git.
120 wd = os.path.join(self.base, self.root) 128 wd = os.path.join(self.base, self.root)
121 if self.dryrun: 129 if self.options.dryrun:
122 print 'cd %s' % wd 130 print 'cd %s' % wd
123 self.run_git( 131 self.run_git(
124 'submodule', 'foreach', 132 'submodule', 'foreach',
125 'git config -f $toplevel/.git/config submodule.$name.ignore all', 133 'git config -f $toplevel/.git/config submodule.$name.ignore all',
126 cwd=wd) 134 cwd=wd)
127 self.run_git('config', 'diff.ignoreSubmodules', 'all', cwd=wd) 135 self.run_git('config', 'diff.ignoreSubmodules', 'all', cwd=wd)
128 136
129 137
130 class GclientGitSvnCheckout(GclientGitCheckout, SvnCheckout): 138 class GclientGitSvnCheckout(GclientGitCheckout, SvnCheckout):
131 139
132 def __init__(self, dryrun, spec, root): 140 def __init__(self, options, spec, root):
133 super(GclientGitSvnCheckout, self).__init__(dryrun, spec, root) 141 super(GclientGitSvnCheckout, self).__init__(options, spec, root)
134 assert 'svn_url' in self.spec 142 assert 'svn_url' in self.spec
135 assert 'svn_branch' in self.spec 143 assert 'svn_branch' in self.spec
136 assert 'svn_ref' in self.spec 144 assert 'svn_ref' in self.spec
137 145
138 def init(self): 146 def init(self):
139 # Ensure we are authenticated with subversion for all submodules. 147 # Ensure we are authenticated with subversion for all submodules.
140 git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}')) 148 git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}'))
141 git_svn_dirs.update({self.root: self.spec}) 149 git_svn_dirs.update({self.root: self.spec})
142 for _, svn_spec in git_svn_dirs.iteritems(): 150 for _, svn_spec in git_svn_dirs.iteritems():
143 try: 151 try:
144 self.run_svn('ls', '--non-interactive', svn_spec['svn_url']) 152 self.run_svn('ls', '--non-interactive', svn_spec['svn_url'])
145 except subprocess.CalledProcessError: 153 except subprocess.CalledProcessError:
146 print 'Please run `svn ls %s`' % svn_spec['svn_url'] 154 print 'Please run `svn ls %s`' % svn_spec['svn_url']
147 return 1 155 return 1
148 156
149 super(GclientGitSvnCheckout, self).init() 157 super(GclientGitSvnCheckout, self).init()
150 158
151 # Configure git-svn. 159 # Configure git-svn.
152 for path, svn_spec in git_svn_dirs.iteritems(): 160 for path, svn_spec in git_svn_dirs.iteritems():
153 real_path = os.path.join(*path.split('/')) 161 real_path = os.path.join(*path.split('/'))
154 if real_path != self.root: 162 if real_path != self.root:
155 real_path = os.path.join(self.root, real_path) 163 real_path = os.path.join(self.root, real_path)
156 wd = os.path.join(self.base, real_path) 164 wd = os.path.join(self.base, real_path)
157 if self.dryrun: 165 if self.options.dryrun:
158 print 'cd %s' % wd 166 print 'cd %s' % wd
159 self.run_git('svn', 'init', '--prefix=origin/', '-T', 167 self.run_git('svn', 'init', '--prefix=origin/', '-T',
160 svn_spec['svn_branch'], svn_spec['svn_url'], cwd=wd) 168 svn_spec['svn_branch'], svn_spec['svn_url'], cwd=wd)
161 self.run_git('config', '--replace', 'svn-remote.svn.fetch', 169 self.run_git('config', '--replace', 'svn-remote.svn.fetch',
162 svn_spec['svn_branch'] + ':refs/remotes/origin/' + 170 svn_spec['svn_branch'] + ':refs/remotes/origin/' +
163 svn_spec['svn_ref'], cwd=wd) 171 svn_spec['svn_ref'], cwd=wd)
164 self.run_git('svn', 'fetch', cwd=wd) 172 self.run_git('svn', 'fetch', cwd=wd)
165 173
166 174
167 175
168 CHECKOUT_TYPE_MAP = { 176 CHECKOUT_TYPE_MAP = {
169 'gclient': GclientCheckout, 177 'gclient': GclientCheckout,
170 'gclient_git': GclientGitCheckout, 178 'gclient_git': GclientGitCheckout,
171 'gclient_git_svn': GclientGitSvnCheckout, 179 'gclient_git_svn': GclientGitSvnCheckout,
172 'git': GitCheckout, 180 'git': GitCheckout,
173 } 181 }
174 182
175 183
176 def CheckoutFactory(type_name, dryrun, spec, root): 184 def CheckoutFactory(type_name, options, spec, root):
177 """Factory to build Checkout class instances.""" 185 """Factory to build Checkout class instances."""
178 class_ = CHECKOUT_TYPE_MAP.get(type_name) 186 class_ = CHECKOUT_TYPE_MAP.get(type_name)
179 if not class_: 187 if not class_:
180 raise KeyError('unrecognized checkout type: %s' % type_name) 188 raise KeyError('unrecognized checkout type: %s' % type_name)
181 return class_(dryrun, spec, root) 189 return class_(options, spec, root)
182 190
183 191
184 ################################################# 192 #################################################
185 # Utility function and file entry point. 193 # Utility function and file entry point.
186 ################################################# 194 #################################################
187 def usage(msg=None): 195 def usage(msg=None):
188 """Print help and exit.""" 196 """Print help and exit."""
189 if msg: 197 if msg:
190 print 'Error:', msg 198 print 'Error:', msg
191 199
192 print ( 200 print (
193 """ 201 """
194 usage: %s [-n|--dry-run] <recipe> [--property=value [--property2=value2 ...]] 202 usage: %s [options] <recipe> [--property=value [--property2=value2 ...]]
203
204 This script can be used to download the Chromium sources. See
205 http://www.chromium.org/developers/how-tos/get-the-code
206 for full usage instructions.
207
208 Valid options:
209 -h, --help, help Print this message.
210 --nohooks Don't run hooks after checkout.
211 -n, --dryrun Don't run commands, only print them.
195 """ % os.path.basename(sys.argv[0])) 212 """ % os.path.basename(sys.argv[0]))
196 sys.exit(bool(msg)) 213 sys.exit(bool(msg))
197 214
198 215
199 def handle_args(argv): 216 def handle_args(argv):
200 """Gets the recipe name from the command line arguments.""" 217 """Gets the recipe name from the command line arguments."""
201 if len(argv) <= 1: 218 if len(argv) <= 1:
202 usage('Must specify a recipe.') 219 usage('Must specify a recipe.')
203 if argv[1] in ('-h', '--help', 'help'): 220 if argv[1] in ('-h', '--help', 'help'):
204 usage() 221 usage()
205 222
206 dryrun = False 223 dryrun = False
207 if argv[1] in ('-n', '--dry-run'): 224 nohooks = False
208 dryrun = True 225 while len(argv) >= 2:
226 arg = argv[1]
227 if not arg.startswith('-'):
228 break
209 argv.pop(1) 229 argv.pop(1)
230 if arg in ('-n', '--dry-run'):
231 dryrun = True
232 elif arg == '--nohooks':
233 nohooks = True
234 else:
235 usage('Invalid option %s.' % arg)
210 236
211 def looks_like_arg(arg): 237 def looks_like_arg(arg):
212 return arg.startswith('--') and arg.count('=') == 1 238 return arg.startswith('--') and arg.count('=') == 1
213 239
214 bad_parms = [x for x in argv[2:] if not looks_like_arg(x)] 240 bad_parms = [x for x in argv[2:] if not looks_like_arg(x)]
215 if bad_parms: 241 if bad_parms:
216 usage('Got bad arguments %s' % bad_parms) 242 usage('Got bad arguments %s' % bad_parms)
217 243
218 recipe = argv[1] 244 recipe = argv[1]
219 props = argv[2:] 245 props = argv[2:]
220 return dryrun, recipe, props 246 return Options(dryrun=dryrun, nohooks=nohooks), recipe, props
Dirk Pranke 2013/12/11 19:53:23 This could just be: return optparse.Values({'dr
digit1 2013/12/13 15:26:22 I've done that, however: - The Python documentati
221 247
222 248
223 def run_recipe_fetch(recipe, props, aliased=False): 249 def run_recipe_fetch(recipe, props, aliased=False):
224 """Invoke a recipe's fetch method with the passed-through args 250 """Invoke a recipe's fetch method with the passed-through args
225 and return its json output as a python object.""" 251 and return its json output as a python object."""
226 recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe)) 252 recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe))
227 if not os.path.exists(recipe_path + '.py'): 253 if not os.path.exists(recipe_path + '.py'):
228 print "Could not find a recipe for %s" % recipe 254 print "Could not find a recipe for %s" % recipe
229 sys.exit(1) 255 sys.exit(1)
230 256
231 cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props 257 cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props
232 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] 258 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
233 259
234 spec = json.loads(result) 260 spec = json.loads(result)
235 if 'alias' in spec: 261 if 'alias' in spec:
236 assert not aliased 262 assert not aliased
237 return run_recipe_fetch( 263 return run_recipe_fetch(
238 spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True) 264 spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True)
239 cmd = [sys.executable, recipe_path + '.py', 'root'] 265 cmd = [sys.executable, recipe_path + '.py', 'root']
240 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] 266 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0]
241 root = json.loads(result) 267 root = json.loads(result)
242 return spec, root 268 return spec, root
243 269
244 270
245 def run(dryrun, spec, root): 271 def run(options, spec, root):
246 """Perform a checkout with the given type and configuration. 272 """Perform a checkout with the given type and configuration.
247 273
248 Args: 274 Args:
249 dryrun: if True, don't actually execute the commands 275 options: Options instance.
250 spec: Checkout configuration returned by the the recipe's fetch_spec 276 spec: Checkout configuration returned by the the recipe's fetch_spec
251 method (checkout type, repository url, etc.). 277 method (checkout type, repository url, etc.).
252 root: The directory into which the repo expects to be checkout out. 278 root: The directory into which the repo expects to be checkout out.
253 """ 279 """
254 assert 'type' in spec 280 assert 'type' in spec
255 checkout_type = spec['type'] 281 checkout_type = spec['type']
256 checkout_spec = spec['%s_spec' % checkout_type] 282 checkout_spec = spec['%s_spec' % checkout_type]
257 try: 283 try:
258 checkout = CheckoutFactory(checkout_type, dryrun, checkout_spec, root) 284 checkout = CheckoutFactory(checkout_type, options, checkout_spec, root)
259 except KeyError: 285 except KeyError:
260 return 1 286 return 1
261 if checkout.exists(): 287 if checkout.exists():
262 print 'You appear to already have a checkout. "fetch" is used only' 288 print 'You appear to already have a checkout. "fetch" is used only'
263 print 'to get new checkouts. Use "gclient sync" to update the checkout.' 289 print 'to get new checkouts. Use "gclient sync" to update the checkout.'
264 print 290 print
265 print 'Fetch also does not yet deal with partial checkouts, so if fetch' 291 print 'Fetch also does not yet deal with partial checkouts, so if fetch'
266 print 'failed, delete the checkout and start over (crbug.com/230691).' 292 print 'failed, delete the checkout and start over (crbug.com/230691).'
267 return 1 293 return 1
268 return checkout.init() 294 return checkout.init()
269 295
270 296
271 def main(): 297 def main():
272 dryrun, recipe, props = handle_args(sys.argv) 298 options, recipe, props = handle_args(sys.argv)
273 spec, root = run_recipe_fetch(recipe, props) 299 spec, root = run_recipe_fetch(recipe, props)
274 return run(dryrun, spec, root) 300 return run(options, spec, root)
275 301
276 302
277 if __name__ == '__main__': 303 if __name__ == '__main__':
278 sys.exit(main()) 304 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698