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 ...]] |
| 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 json | 21 import json |
| 22 import os | 22 import os |
| 23 import subprocess | 23 import subprocess |
| 24 import sys | 24 import sys |
| 25 import pipes | 25 import pipes |
| 26 | 26 |
| 27 from distutils import spawn | |
| 28 | |
| 27 | 29 |
| 28 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) | 30 SCRIPT_PATH = os.path.dirname(os.path.abspath(__file__)) |
| 29 | 31 |
| 30 | 32 |
| 31 ################################################# | 33 ################################################# |
| 32 # Checkout class definitions. | 34 # Checkout class definitions. |
| 33 ################################################# | 35 ################################################# |
| 34 class Checkout(object): | 36 class Checkout(object): |
| 35 """Base class for implementing different types of checkouts. | 37 """Base class for implementing different types of checkouts. |
| 36 | 38 |
| 37 Attributes: | 39 Attributes: |
| 38 |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. |
| 39 |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 |
| 40 subclasses will expect different keys in this dictionary. | 42 subclasses will expect different keys in this dictionary. |
| 41 |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 |
| 42 by the recipe. This is a relative path from |base|. | 44 by the recipe. This is a relative path from |base|. |
| 43 """ | 45 """ |
| 44 def __init__(self, dryrun, spec, root): | 46 def __init__(self, dryrun, spec, root): |
| 45 self.base = os.getcwd() | 47 self.base = os.getcwd() |
| 46 self.dryrun = dryrun | 48 self.dryrun = dryrun |
| 47 self.spec = spec | 49 self.spec = spec |
| 48 self.root = root | 50 self.root = root |
| 51 self._script_paths = {} | |
| 52 self._binary_paths = {} | |
| 53 self._binary_subdirs = {} | |
| 49 | 54 |
| 50 def exists(self): | 55 def exists(self): |
| 51 pass | 56 pass |
| 52 | 57 |
| 53 def init(self): | 58 def init(self): |
| 54 pass | 59 pass |
| 55 | 60 |
| 56 def sync(self): | 61 def sync(self): |
| 57 pass | 62 pass |
| 58 | 63 |
| 64 def _find_executable(self, binary_name): | |
| 65 if sys.platform == 'win32': | |
|
szager1
2013/04/11 21:30:01
This is unnecessary; spawn.find_executable will lo
Dirk Pranke
2013/04/11 21:52:08
Huh, so it does. That's great, as it simplifies th
| |
| 66 exe_name = binary_name + '.exe' | |
| 67 else: | |
| 68 exe_name = binary_name | |
| 69 path = spawn.find_executable(exe_name) | |
| 70 if path: | |
| 71 return path | |
| 72 return spawn.find_executable( | |
|
szager1
2013/04/11 21:30:01
What's the purpose of this call? As far as I can
Dirk Pranke
2013/04/11 21:52:08
I thought that spawn.find_executable() does actual
szager1
2013/04/11 22:18:43
Whoops, should be:
spawn.find_executable(binary_n
| |
| 73 os.path.join(SCRIPT_PATH, | |
| 74 self._binary_subdirs[binary_name], | |
| 75 exe_name)) | |
| 76 | |
| 77 def run_script(self, script, *args, **kwargs): | |
| 78 path = self._script_paths.get(script) | |
| 79 if not path: | |
| 80 path = os.path.join(SCRIPT_PATH, script + '.py') | |
|
szager1
2013/04/11 21:30:01
path = os.path.join(SCRIPT_PATH, script)
if not sc
Dirk Pranke
2013/04/11 21:52:08
Again, this is too general for my intentions.
| |
| 81 if not os.path.exists(path): | |
| 82 print "Error: could not find '%s.py' in depot_tools" % script | |
| 83 return 1 | |
| 84 self._script_paths[script] = path | |
| 85 print 'Running: %s %s' % (script, | |
| 86 ' '.join(pipes.quote(arg) for arg in args)) | |
| 87 if self.dryrun: | |
| 88 return 0 | |
| 89 return subprocess.check_call((sys.executable, path) + args, **kwargs) | |
| 90 | |
| 91 def run_binary(self, binary_name, *args, **kwargs): | |
| 92 path = self._binary_paths.get(binary_name) | |
| 93 if not path: | |
| 94 path = self._find_executable(binary_name) | |
| 95 if not path: | |
| 96 print "Error: could not find '%s' in PATH" % binary_name | |
| 97 return 1 | |
| 98 self._binary_paths[binary_name] = path | |
| 99 print 'Running: %s %s' % (binary_name, | |
| 100 ' '.join(pipes.quote(arg) for arg in args)) | |
| 101 if self.dryrun: | |
| 102 return 0 | |
| 103 return subprocess.check_call((path,) + args, **kwargs) | |
| 104 | |
| 59 | 105 |
| 60 class GclientCheckout(Checkout): | 106 class GclientCheckout(Checkout): |
| 61 | 107 def run_gclient(self, *args, **kwargs): |
| 62 def run_gclient(self, *cmd, **kwargs): | 108 return self.run_script('gclient', *args, **kwargs) |
| 63 print 'Running: gclient %s' % ' '.join(pipes.quote(x) for x in cmd) | |
| 64 if not self.dryrun: | |
| 65 return subprocess.check_call( | |
| 66 (sys.executable, os.path.join(SCRIPT_PATH, 'gclient.py')) + cmd, | |
| 67 **kwargs) | |
| 68 | 109 |
| 69 | 110 |
| 70 class GitCheckout(Checkout): | 111 class GitCheckout(Checkout): |
| 112 def __init__(self, *args, **kwargs): | |
| 113 super(GitCheckout, self).__init__(*args, **kwargs) | |
| 114 self._binary_subdirs['git'] = os.path.join('git-1.8.0_bin', 'bin') | |
|
szager1
2013/04/11 21:30:01
if sys.platform == 'win32':
self._binary_subdirs
Dirk Pranke
2013/04/11 21:52:08
I'm not sure if you would still want me to make th
szager1
2013/04/11 22:18:43
Done.
| |
| 71 | 115 |
| 72 def run_git(self, *cmd, **kwargs): | 116 def run_git(self, *args, **kwargs): |
| 73 print 'Running: git %s' % ' '.join(pipes.quote(x) for x in cmd) | 117 return self.run_binary('git', *args, **kwargs) |
| 74 if not self.dryrun: | |
| 75 return subprocess.check_call(('git',) + cmd, **kwargs) | |
| 76 | 118 |
| 77 | 119 |
| 78 class SvnCheckout(Checkout): | 120 class SvnCheckout(Checkout): |
| 121 def __init__(self, *args, **kwargs): | |
| 122 super(SvnCheckout, self).__init__(*args, **kwargs) | |
| 123 self._binary_subdirs['svn'] = 'svn_bin' | |
|
szager1
2013/04/11 21:30:01
if sys.platform == 'win32':
...
| |
| 79 | 124 |
| 80 def run_svn(self, *cmd, **kwargs): | 125 def run_svn(self, *args, **kwargs): |
| 81 print 'Running: svn %s' % ' '.join(pipes.quote(x) for x in cmd) | 126 return self.run_binary('svn', *args, **kwargs) |
| 82 if not self.dryrun: | |
| 83 return subprocess.check_call(('svn',) + cmd, **kwargs) | |
| 84 | 127 |
| 85 | 128 |
| 86 class GclientGitSvnCheckout(GclientCheckout, GitCheckout, SvnCheckout): | 129 class GclientGitSvnCheckout(GclientCheckout, GitCheckout, SvnCheckout): |
| 87 | 130 |
| 88 def __init__(self, dryrun, spec, root): | 131 def __init__(self, dryrun, spec, root): |
| 89 super(GclientGitSvnCheckout, self).__init__(dryrun, spec, root) | 132 super(GclientGitSvnCheckout, self).__init__(dryrun, spec, root) |
| 90 assert 'solutions' in self.spec | 133 assert 'solutions' in self.spec |
| 91 keys = ['solutions', 'target_os', 'target_os_only'] | 134 keys = ['solutions', 'target_os', 'target_os_only'] |
| 92 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) | 135 gclient_spec = '\n'.join('%s = %s' % (key, self.spec[key]) |
| 93 for key in self.spec if key in keys) | 136 for key in self.spec if key in keys) |
| 94 self.spec['gclient_spec'] = gclient_spec | 137 self.spec['gclient_spec'] = gclient_spec |
| 95 assert 'svn_url' in self.spec | 138 assert 'svn_url' in self.spec |
| 96 assert 'svn_branch' in self.spec | 139 assert 'svn_branch' in self.spec |
| 97 assert 'svn_ref' in self.spec | 140 assert 'svn_ref' in self.spec |
| 98 | 141 |
| 99 def exists(self): | 142 def exists(self): |
| 100 return os.path.exists(os.path.join(os.getcwd(), self.root)) | 143 return os.path.exists(os.path.join(os.getcwd(), self.root)) |
| 101 | 144 |
| 102 def init(self): | 145 def init(self): |
| 103 # Ensure we are authenticated with subversion for all submodules. | 146 # Ensure we are authenticated with subversion for all submodules. |
| 104 git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}')) | 147 git_svn_dirs = json.loads(self.spec.get('submodule_git_svn_spec', '{}')) |
| 105 git_svn_dirs.update({self.root: self.spec}) | 148 git_svn_dirs.update({self.root: self.spec}) |
| 106 for _, svn_spec in git_svn_dirs.iteritems(): | 149 for _, svn_spec in git_svn_dirs.iteritems(): |
| 107 try: | 150 try: |
| 108 self.run_svn('ls', '--non-interactive', svn_spec['svn_url']) | 151 self.run_svn('ls', '--non-interactive', svn_spec['svn_url']) |
| 109 except subprocess.CalledProcessError: | 152 except subprocess.CalledProcessError: |
| 110 print 'Please run `svn ls %s`' % svn_spec['svn_url'] | 153 print 'Please run `svn ls %s`' % svn_spec['svn_url'] |
| 111 return 1 | 154 return 1 |
| 112 | 155 |
| 113 # TODO(dpranke): Work around issues w/ delta compression on big repos. | 156 # TODO(dpranke): This works around issues w/ delta compression on |
| 157 # big repos. We should either not be setting this globally, or | |
| 158 # not need it at all, or both. | |
| 114 self.run_git('config', '--global', 'core.deltaBaseCacheLimit', '1G') | 159 self.run_git('config', '--global', 'core.deltaBaseCacheLimit', '1G') |
| 115 | 160 |
| 116 # Configure and do the gclient checkout. | 161 # Configure and do the gclient checkout. |
| 117 self.run_gclient('config', '--spec', self.spec['gclient_spec']) | 162 self.run_gclient('config', '--spec', self.spec['gclient_spec']) |
| 118 self.run_gclient('sync') | 163 self.run_gclient('sync') |
| 119 | 164 |
| 120 # Configure git. | 165 # Configure git. |
| 121 wd = os.path.join(self.base, self.root) | 166 wd = os.path.join(self.base, self.root) |
| 122 if self.dryrun: | 167 if self.dryrun: |
| 123 print 'cd %s' % wd | 168 print 'cd %s' % wd |
| (...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 244 | 289 |
| 245 | 290 |
| 246 def main(): | 291 def main(): |
| 247 dryrun, recipe, props = handle_args(sys.argv) | 292 dryrun, recipe, props = handle_args(sys.argv) |
| 248 spec, root = run_recipe_fetch(recipe, props) | 293 spec, root = run_recipe_fetch(recipe, props) |
| 249 return run(dryrun, spec, root) | 294 return run(dryrun, spec, root) |
| 250 | 295 |
| 251 | 296 |
| 252 if __name__ == '__main__': | 297 if __name__ == '__main__': |
| 253 sys.exit(main()) | 298 sys.exit(main()) |
| OLD | NEW |