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 23 matching lines...) Expand all Loading... | |
34 class Checkout(object): | 34 class Checkout(object): |
35 """Base class for implementing different types of checkouts. | 35 """Base class for implementing different types of checkouts. |
36 | 36 |
37 Attributes: | 37 Attributes: |
38 |base|: the absolute path of the directory in which this script is run. | 38 |base|: the absolute path of the directory in which this script is run. |
39 |spec|: the spec for this checkout as returned by the recipe. Different | 39 |spec|: the spec for this checkout as returned by the recipe. Different |
40 subclasses will expect different keys in this dictionary. | 40 subclasses will expect different keys in this dictionary. |
41 |root|: the directory into which the checkout will be performed, as returned | 41 |root|: the directory into which the checkout will be performed, as returned |
42 by the recipe. This is a relative path from |base|. | 42 by the recipe. This is a relative path from |base|. |
43 """ | 43 """ |
44 def __init__(self, spec, root): | 44 def __init__(self, dryrun, spec, root): |
45 self.base = os.getcwd() | 45 self.base = os.getcwd() |
46 self.dryrun = dryrun | |
46 self.spec = spec | 47 self.spec = spec |
47 self.root = root | 48 self.root = root |
48 | 49 |
49 def exists(self): | 50 def exists(self): |
50 pass | 51 pass |
51 | 52 |
52 def init(self): | 53 def init(self): |
53 pass | 54 pass |
54 | 55 |
55 def sync(self): | 56 def sync(self): |
56 pass | 57 pass |
57 | 58 |
58 | 59 |
59 class GclientCheckout(Checkout): | 60 class GclientCheckout(Checkout): |
60 | 61 |
61 @staticmethod | 62 def run_gclient(self, *cmd, **kwargs): |
62 def run_gclient(*cmd, **kwargs): | |
63 print 'Running: gclient %s' % ' '.join(pipes.quote(x) for x in cmd) | 63 print 'Running: gclient %s' % ' '.join(pipes.quote(x) for x in cmd) |
64 return subprocess.check_call(('gclient',) + cmd, **kwargs) | 64 if not self.dryrun: |
65 return subprocess.check_call(('gclient',) + cmd, **kwargs) | |
65 | 66 |
66 | 67 |
67 class GitCheckout(Checkout): | 68 class GitCheckout(Checkout): |
68 | 69 |
69 @staticmethod | 70 def run_git(self, *cmd, **kwargs): |
70 def run_git(*cmd, **kwargs): | |
71 print 'Running: git %s' % ' '.join(pipes.quote(x) for x in cmd) | 71 print 'Running: git %s' % ' '.join(pipes.quote(x) for x in cmd) |
72 return subprocess.check_call(('git',) + cmd, **kwargs) | 72 if not self.dryrun: |
73 return subprocess.check_call(('git',) + cmd, **kwargs) | |
73 | 74 |
74 | 75 |
75 class GclientGitSvnCheckout(GclientCheckout, GitCheckout): | 76 class GclientGitSvnCheckout(GclientCheckout, GitCheckout): |
76 | 77 |
77 def __init__(self, spec, root): | 78 def __init__(self, dryrun, spec, root): |
78 super(GclientGitSvnCheckout, self).__init__(spec, root) | 79 super(GclientGitSvnCheckout, self).__init__(dryrun, spec, root) |
79 assert 'solutions' in self.spec | 80 assert 'solutions' in self.spec |
80 keys = ['solutions', 'target_os', 'target_os_only'] | 81 keys = ['solutions', 'target_os', 'target_os_only'] |
81 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) | 82 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) |
82 for key in self.spec if key in keys) | 83 for key in self.spec if key in keys) |
83 self.spec['gclient_spec'] = gclient_spec | 84 self.spec['gclient_spec'] = gclient_spec |
84 assert 'svn_url' in self.spec | 85 assert 'svn_url' in self.spec |
85 assert 'svn_branch' in self.spec | 86 assert 'svn_branch' in self.spec |
86 assert 'svn_ref' in self.spec | 87 assert 'svn_ref' in self.spec |
87 | 88 |
88 def exists(self): | 89 def exists(self): |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
122 self.run_git('svn', 'fetch', cwd=wd) | 123 self.run_git('svn', 'fetch', cwd=wd) |
123 | 124 |
124 | 125 |
125 CHECKOUT_TYPE_MAP = { | 126 CHECKOUT_TYPE_MAP = { |
126 'gclient': GclientCheckout, | 127 'gclient': GclientCheckout, |
127 'gclient_git_svn': GclientGitSvnCheckout, | 128 'gclient_git_svn': GclientGitSvnCheckout, |
128 'git': GitCheckout, | 129 'git': GitCheckout, |
129 } | 130 } |
130 | 131 |
131 | 132 |
132 def CheckoutFactory(type_name, spec, root): | 133 def CheckoutFactory(type_name, dryrun, spec, root): |
133 """Factory to build Checkout class instances.""" | 134 """Factory to build Checkout class instances.""" |
134 class_ = CHECKOUT_TYPE_MAP.get(type_name) | 135 class_ = CHECKOUT_TYPE_MAP.get(type_name) |
135 if not class_: | 136 if not class_: |
136 raise KeyError('unrecognized checkout type: %s' % type_name) | 137 raise KeyError('unrecognized checkout type: %s' % type_name) |
137 return class_(spec, root) | 138 return class_(dryrun, spec, root) |
138 | 139 |
139 | 140 |
140 ################################################# | 141 ################################################# |
141 # Utility function and file entry point. | 142 # Utility function and file entry point. |
142 ################################################# | 143 ################################################# |
143 def usage(msg=None): | 144 def usage(msg=None): |
144 """Print help and exit.""" | 145 """Print help and exit.""" |
145 if msg: | 146 if msg: |
146 print 'Error:', msg | 147 print 'Error:', msg |
147 | 148 |
148 print ( | 149 print ( |
149 """ | 150 """ |
150 usage: %s <recipe> [--property=value [--property2=value2 ...]] | 151 usage: %s [-n|--dry-run] <recipe> [--property=value [--property2=value2 ...]] |
151 """ % os.path.basename(sys.argv[0])) | 152 """ % os.path.basename(sys.argv[0])) |
152 sys.exit(bool(msg)) | 153 sys.exit(bool(msg)) |
153 | 154 |
154 | 155 |
155 def handle_args(argv): | 156 def handle_args(argv): |
156 """Gets the recipe name from the command line arguments.""" | 157 """Gets the recipe name from the command line arguments.""" |
157 if len(argv) <= 1: | 158 if len(argv) <= 1: |
158 usage('Must specify a recipe.') | 159 usage('Must specify a recipe.') |
159 if argv[1] in ('-h', '--help', 'help'): | 160 if argv[1] in ('-h', '--help', 'help'): |
160 usage() | 161 usage() |
161 | 162 |
163 if argv[1] in ('-n', '--dry-run'): | |
164 dryrun = True | |
165 argv.pop(1) | |
agable
2013/04/03 21:07:00
This is the best way to do it for now.
I'll look
| |
166 | |
162 def looks_like_arg(arg): | 167 def looks_like_arg(arg): |
163 return arg.startswith('--') and arg.count('=') == 1 | 168 return arg.startswith('--') and arg.count('=') == 1 |
164 | 169 |
165 bad_parms = [x for x in argv[2:] if not looks_like_arg(x)] | 170 bad_parms = [x for x in argv[2:] if not looks_like_arg(x)] |
166 if bad_parms: | 171 if bad_parms: |
167 usage('Got bad arguments %s' % bad_parms) | 172 usage('Got bad arguments %s' % bad_parms) |
168 | 173 |
169 recipe = argv[1] | 174 recipe = argv[1] |
170 props = argv[2:] | 175 props = argv[2:] |
171 return recipe, props | 176 return dryrun, recipe, props |
172 | 177 |
173 | 178 |
174 def run_recipe_fetch(recipe, props, aliased=False): | 179 def run_recipe_fetch(recipe, props, aliased=False): |
175 """Invoke a recipe's fetch method with the passed-through args | 180 """Invoke a recipe's fetch method with the passed-through args |
176 and return its json output as a python object.""" | 181 and return its json output as a python object.""" |
177 recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe)) | 182 recipe_path = os.path.abspath(os.path.join(SCRIPT_PATH, 'recipes', recipe)) |
178 cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props | 183 cmd = [sys.executable, recipe_path + '.py', 'fetch'] + props |
179 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] | 184 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] |
180 spec = json.loads(result) | 185 spec = json.loads(result) |
181 if 'alias' in spec: | 186 if 'alias' in spec: |
182 assert not aliased | 187 assert not aliased |
183 return run_recipe_fetch( | 188 return run_recipe_fetch( |
184 spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True) | 189 spec['alias']['recipe'], spec['alias']['props'] + props, aliased=True) |
185 cmd = [sys.executable, recipe_path + '.py', 'root'] | 190 cmd = [sys.executable, recipe_path + '.py', 'root'] |
186 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] | 191 result = subprocess.Popen(cmd, stdout=subprocess.PIPE).communicate()[0] |
187 root = json.loads(result) | 192 root = json.loads(result) |
188 return spec, root | 193 return spec, root |
189 | 194 |
190 | 195 |
191 def run(spec, root): | 196 def run(dryrun, spec, root): |
192 """Perform a checkout with the given type and configuration. | 197 """Perform a checkout with the given type and configuration. |
193 | 198 |
194 Args: | 199 Args: |
200 dryrun: if True, don't actually execute the commands | |
195 spec: Checkout configuration returned by the the recipe's fetch_spec | 201 spec: Checkout configuration returned by the the recipe's fetch_spec |
196 method (checkout type, repository url, etc.). | 202 method (checkout type, repository url, etc.). |
197 root: The directory into which the repo expects to be checkout out. | 203 root: The directory into which the repo expects to be checkout out. |
198 """ | 204 """ |
199 assert 'type' in spec | 205 assert 'type' in spec |
200 checkout_type = spec['type'] | 206 checkout_type = spec['type'] |
201 checkout_spec = spec['%s_spec' % checkout_type] | 207 checkout_spec = spec['%s_spec' % checkout_type] |
202 try: | 208 try: |
203 checkout = CheckoutFactory(checkout_type, checkout_spec, root) | 209 checkout = CheckoutFactory(checkout_type, dryrun, checkout_spec, root) |
204 except KeyError: | 210 except KeyError: |
205 return 1 | 211 return 1 |
206 if checkout.exists(): | 212 if checkout.exists(): |
207 print 'You appear to already have this checkout.' | 213 print 'You appear to already have this checkout.' |
208 print 'Aborting to avoid clobbering your work.' | 214 print 'Aborting to avoid clobbering your work.' |
209 return 1 | 215 return 1 |
210 checkout.init() | 216 checkout.init() |
211 return 0 | 217 return 0 |
212 | 218 |
213 | 219 |
214 def main(): | 220 def main(): |
215 recipe, props = handle_args(sys.argv) | 221 dryrun, recipe, props = handle_args(sys.argv) |
216 spec, root = run_recipe_fetch(recipe, props) | 222 spec, root = run_recipe_fetch(recipe, props) |
217 return run(spec, root) | 223 return run(dryrun, spec, root) |
218 | 224 |
219 | 225 |
220 if __name__ == '__main__': | 226 if __name__ == '__main__': |
221 sys.exit(main()) | 227 sys.exit(main()) |
OLD | NEW |