OLD | NEW |
---|---|
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 ...]] |
(...skipping 25 matching lines...) Expand all Loading... | |
36 class Checkout(object): | 36 class Checkout(object): |
37 """Base class for implementing different types of checkouts. | 37 """Base class for implementing different types of checkouts. |
38 | 38 |
39 Attributes: | 39 Attributes: |
40 |base|: the absolute path of the directory in which this script is run. | 40 |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 | 41 |spec|: the spec for this checkout as returned by the recipe. Different |
42 subclasses will expect different keys in this dictionary. | 42 subclasses will expect different keys in this dictionary. |
43 |root|: the directory into which the checkout will be performed, as returned | 43 |root|: the directory into which the checkout will be performed, as returned |
44 by the recipe. This is a relative path from |base|. | 44 by the recipe. This is a relative path from |base|. |
45 """ | 45 """ |
46 def __init__(self, dryrun, spec, root): | 46 def __init__(self, dryrun, nohooks, spec, root): |
47 self.base = os.getcwd() | 47 self.base = os.getcwd() |
48 self.dryrun = dryrun | 48 self.dryrun = dryrun |
49 self.nohooks = nohooks | |
49 self.spec = spec | 50 self.spec = spec |
50 self.root = root | 51 self.root = root |
51 | 52 |
52 def exists(self): | 53 def exists(self): |
53 pass | 54 pass |
54 | 55 |
55 def init(self): | 56 def init(self): |
56 pass | 57 pass |
57 | 58 |
58 def sync(self): | 59 def sync(self): |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
90 def run_svn(self, *cmd, **kwargs): | 91 def run_svn(self, *cmd, **kwargs): |
91 if sys.platform == 'win32' and not spawn.find_executable('svn'): | 92 if sys.platform == 'win32' and not spawn.find_executable('svn'): |
92 svn_path = os.path.join(SCRIPT_PATH, 'svn_bin', 'svn.exe') | 93 svn_path = os.path.join(SCRIPT_PATH, 'svn_bin', 'svn.exe') |
93 else: | 94 else: |
94 svn_path = 'svn' | 95 svn_path = 'svn' |
95 return self.run((svn_path,) + cmd, **kwargs) | 96 return self.run((svn_path,) + cmd, **kwargs) |
96 | 97 |
97 | 98 |
98 class GclientGitCheckout(GclientCheckout, GitCheckout): | 99 class GclientGitCheckout(GclientCheckout, GitCheckout): |
99 | 100 |
100 def __init__(self, dryrun, spec, root): | 101 def __init__(self, dryrun, nohooks, spec, root): |
101 super(GclientGitCheckout, self).__init__(dryrun, spec, root) | 102 super(GclientGitCheckout, self).__init__(dryrun, nohooks, spec, root) |
102 assert 'solutions' in self.spec | 103 assert 'solutions' in self.spec |
103 keys = ['solutions', 'target_os', 'target_os_only'] | 104 keys = ['solutions', 'target_os', 'target_os_only'] |
104 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) | 105 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) |
105 for key in self.spec if key in keys) | 106 for key in self.spec if key in keys) |
106 self.spec['gclient_spec'] = gclient_spec | 107 self.spec['gclient_spec'] = gclient_spec |
107 | 108 |
108 def exists(self): | 109 def exists(self): |
109 return os.path.exists(os.path.join(os.getcwd(), self.root)) | 110 return os.path.exists(os.path.join(os.getcwd(), self.root)) |
110 | 111 |
111 def init(self): | 112 def init(self): |
112 # TODO(dpranke): Work around issues w/ delta compression on big repos. | 113 # TODO(dpranke): Work around issues w/ delta compression on big repos. |
113 self.run_git('config', '--global', 'core.deltaBaseCacheLimit', '1G') | 114 self.run_git('config', '--global', 'core.deltaBaseCacheLimit', '1G') |
114 | 115 |
115 # Configure and do the gclient checkout. | 116 # Configure and do the gclient checkout. |
116 self.run_gclient('config', '--spec', self.spec['gclient_spec']) | 117 self.run_gclient('config', '--spec', self.spec['gclient_spec']) |
117 self.run_gclient('sync') | 118 |
119 if self.nohooks: | |
120 self.run_gclient('sync', '--nohooks') | |
121 else: | |
122 self.run_gclient('sync') | |
118 | 123 |
119 # Configure git. | 124 # Configure git. |
120 wd = os.path.join(self.base, self.root) | 125 wd = os.path.join(self.base, self.root) |
121 if self.dryrun: | 126 if self.dryrun: |
122 print 'cd %s' % wd | 127 print 'cd %s' % wd |
123 self.run_git( | 128 self.run_git( |
124 'submodule', 'foreach', | 129 'submodule', 'foreach', |
125 'git config -f $toplevel/.git/config submodule.$name.ignore all', | 130 'git config -f $toplevel/.git/config submodule.$name.ignore all', |
126 cwd=wd) | 131 cwd=wd) |
127 self.run_git('config', 'diff.ignoreSubmodules', 'all', cwd=wd) | 132 self.run_git('config', 'diff.ignoreSubmodules', 'all', cwd=wd) |
128 | 133 |
129 | 134 |
130 class GclientGitSvnCheckout(GclientGitCheckout, SvnCheckout): | 135 class GclientGitSvnCheckout(GclientGitCheckout, SvnCheckout): |
131 | 136 |
132 def __init__(self, dryrun, spec, root): | 137 def __init__(self, dryrun, nohooks, spec, root): |
133 super(GclientGitSvnCheckout, self).__init__(dryrun, spec, root) | 138 super(GclientGitSvnCheckout, self).__init__(dryrun, nohooks, spec, root) |
134 assert 'svn_url' in self.spec | 139 assert 'svn_url' in self.spec |
135 assert 'svn_branch' in self.spec | 140 assert 'svn_branch' in self.spec |
136 assert 'svn_ref' in self.spec | 141 assert 'svn_ref' in self.spec |
137 | 142 |
138 def init(self): | 143 def init(self): |
139 # Ensure we are authenticated with subversion for all submodules. | 144 # Ensure we are authenticated with subversion for all submodules. |
140 git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}')) | 145 git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}')) |
141 git_svn_dirs.update({self.root: self.spec}) | 146 git_svn_dirs.update({self.root: self.spec}) |
142 for _, svn_spec in git_svn_dirs.iteritems(): | 147 for _, svn_spec in git_svn_dirs.iteritems(): |
143 try: | 148 try: |
(...skipping 22 matching lines...) Expand all Loading... | |
166 | 171 |
167 | 172 |
168 CHECKOUT_TYPE_MAP = { | 173 CHECKOUT_TYPE_MAP = { |
169 'gclient': GclientCheckout, | 174 'gclient': GclientCheckout, |
170 'gclient_git': GclientGitCheckout, | 175 'gclient_git': GclientGitCheckout, |
171 'gclient_git_svn': GclientGitSvnCheckout, | 176 'gclient_git_svn': GclientGitSvnCheckout, |
172 'git': GitCheckout, | 177 'git': GitCheckout, |
173 } | 178 } |
174 | 179 |
175 | 180 |
176 def CheckoutFactory(type_name, dryrun, spec, root): | 181 def CheckoutFactory(type_name, dryrun, nohooks, spec, root): |
177 """Factory to build Checkout class instances.""" | 182 """Factory to build Checkout class instances.""" |
178 class_ = CHECKOUT_TYPE_MAP.get(type_name) | 183 class_ = CHECKOUT_TYPE_MAP.get(type_name) |
179 if not class_: | 184 if not class_: |
180 raise KeyError('unrecognized checkout type: %s' % type_name) | 185 raise KeyError('unrecognized checkout type: %s' % type_name) |
181 return class_(dryrun, spec, root) | 186 return class_(dryrun, nohooks, spec, root) |
182 | 187 |
183 | 188 |
184 ################################################# | 189 ################################################# |
185 # Utility function and file entry point. | 190 # Utility function and file entry point. |
186 ################################################# | 191 ################################################# |
187 def usage(msg=None): | 192 def usage(msg=None): |
188 """Print help and exit.""" | 193 """Print help and exit.""" |
189 if msg: | 194 if msg: |
190 print 'Error:', msg | 195 print 'Error:', msg |
191 | 196 |
192 print ( | 197 print ( |
193 """ | 198 """ |
194 usage: %s [-n|--dry-run] <recipe> [--property=value [--property2=value2 ...]] | 199 usage: %s [-n|--dry-run] [--nohooks] <recipe> [--property=value [--property2=val ue2 ...]] |
195 """ % os.path.basename(sys.argv[0])) | 200 """ % os.path.basename(sys.argv[0])) |
196 sys.exit(bool(msg)) | 201 sys.exit(bool(msg)) |
197 | 202 |
198 | 203 |
199 def handle_args(argv): | 204 def handle_args(argv): |
200 """Gets the recipe name from the command line arguments.""" | 205 """Gets the recipe name from the command line arguments.""" |
201 if len(argv) <= 1: | 206 if len(argv) <= 1: |
202 usage('Must specify a recipe.') | 207 usage('Must specify a recipe.') |
203 if argv[1] in ('-h', '--help', 'help'): | 208 if argv[1] in ('-h', '--help', 'help'): |
204 usage() | 209 usage() |
205 | 210 |
206 dryrun = False | 211 dryrun = False |
207 if argv[1] in ('-n', '--dry-run'): | 212 if argv[1] in ('-n', '--dry-run'): |
208 dryrun = True | 213 dryrun = True |
209 argv.pop(1) | 214 argv.pop(1) |
210 | 215 |
216 nohooks = False | |
217 if argv[1] == '--nohooks': | |
218 nohooks = True | |
219 argv.pop(1) | |
agable
2013/04/17 17:10:40
I'm going to do a full rewrite of the fetch and re
| |
220 | |
211 def looks_like_arg(arg): | 221 def looks_like_arg(arg): |
212 return arg.startswith('--') and arg.count('=') == 1 | 222 return arg.startswith('--') and arg.count('=') == 1 |
213 | 223 |
214 bad_parms = [x for x in argv[2:] if not looks_like_arg(x)] | 224 bad_parms = [x for x in argv[2:] if not looks_like_arg(x)] |
215 if bad_parms: | 225 if bad_parms: |
216 usage('Got bad arguments %s' % bad_parms) | 226 usage('Got bad arguments %s' % bad_parms) |
217 | 227 |
218 recipe = argv[1] | 228 recipe = argv[1] |
219 props = argv[2:] | 229 props = argv[2:] |
220 return dryrun, recipe, props | 230 return dryrun, nohooks, recipe, props |
221 | 231 |
222 | 232 |
223 def run_recipe_fetch(recipe, props, aliased=False): | 233 def run_recipe_fetch(recipe, props, aliased=False): |
224 """Invoke a recipe's fetch method with the passed-through args | 234 """Invoke a recipe's fetch method with the passed-through args |
225 and return its json output as a python object.""" | 235 and return its json output as a python object.""" |
226 recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe)) | 236 recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe)) |
227 if not os.path.exists(recipe_path + '.py'): | 237 if not os.path.exists(recipe_path + '.py'): |
228 print "Could not find a recipe for %s" % recipe | 238 print "Could not find a recipe for %s" % recipe |
229 sys.exit(1) | 239 sys.exit(1) |
230 | 240 |
231 cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props | 241 cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props |
232 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] | 242 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] |
233 | 243 |
234 spec = json.loads(result) | 244 spec = json.loads(result) |
235 if 'alias' in spec: | 245 if 'alias' in spec: |
236 assert not aliased | 246 assert not aliased |
237 return run_recipe_fetch( | 247 return run_recipe_fetch( |
238 spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True) | 248 spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True) |
239 cmd = [sys.executable, recipe_path + '.py', 'root'] | 249 cmd = [sys.executable, recipe_path + '.py', 'root'] |
240 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] | 250 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] |
241 root = json.loads(result) | 251 root = json.loads(result) |
242 return spec, root | 252 return spec, root |
243 | 253 |
244 | 254 |
245 def run(dryrun, spec, root): | 255 def run(dryrun, nohooks, spec, root): |
246 """Perform a checkout with the given type and configuration. | 256 """Perform a checkout with the given type and configuration. |
247 | 257 |
248 Args: | 258 Args: |
249 dryrun: if True, don't actually execute the commands | 259 dryrun: if True, don't actually execute the commands |
250 spec: Checkout configuration returned by the the recipe's fetch_spec | 260 spec: Checkout configuration returned by the the recipe's fetch_spec |
251 method (checkout type, repository url, etc.). | 261 method (checkout type, repository url, etc.). |
252 root: The directory into which the repo expects to be checkout out. | 262 root: The directory into which the repo expects to be checkout out. |
253 """ | 263 """ |
254 assert 'type' in spec | 264 assert 'type' in spec |
255 checkout_type = spec['type'] | 265 checkout_type = spec['type'] |
256 checkout_spec = spec['%s_spec' % checkout_type] | 266 checkout_spec = spec['%s_spec' % checkout_type] |
257 try: | 267 try: |
258 checkout = CheckoutFactory(checkout_type, dryrun, checkout_spec, root) | 268 checkout = CheckoutFactory( |
269 checkout_type, dryrun, nohooks, checkout_spec, root) | |
259 except KeyError: | 270 except KeyError: |
260 return 1 | 271 return 1 |
261 if checkout.exists(): | 272 if checkout.exists(): |
262 print 'You appear to already have a checkout. "fetch" is used only' | 273 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.' | 274 print 'to get new checkouts. Use "gclient sync" to update the checkout.' |
264 print | 275 print |
265 print 'Fetch also does not yet deal with partial checkouts, so if fetch' | 276 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).' | 277 print 'failed, delete the checkout and start over (crbug.com/230691).' |
267 return 1 | 278 return 1 |
268 return checkout.init() | 279 return checkout.init() |
269 | 280 |
270 | 281 |
271 def main(): | 282 def main(): |
272 dryrun, recipe, props = handle_args(sys.argv) | 283 dryrun, nohooks, recipe, props = handle_args(sys.argv) |
273 spec, root = run_recipe_fetch(recipe, props) | 284 spec, root = run_recipe_fetch(recipe, props) |
274 return run(dryrun, spec, root) | 285 return run(dryrun, nohooks, spec, root) |
275 | 286 |
276 | 287 |
277 if __name__ == '__main__': | 288 if __name__ == '__main__': |
278 sys.exit(main()) | 289 sys.exit(main()) |
OLD | NEW |