Chromium Code Reviews| 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 |