OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 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 """Meta checkout manager supporting both Subversion and GIT. | 6 """Meta checkout manager supporting both Subversion and GIT. |
7 | 7 |
8 Files | 8 Files |
9 .gclient : Current client configuration, written by 'config' command. | 9 .gclient : Current client configuration, written by 'config' command. |
10 Format is a Python script defining 'solutions', a list whose | 10 Format is a Python script defining 'solutions', a list whose |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
42 it will be removed from the list and the list will be extended | 42 it will be removed from the list and the list will be extended |
43 by the list of matching files. | 43 by the list of matching files. |
44 | 44 |
45 Example: | 45 Example: |
46 hooks = [ | 46 hooks = [ |
47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", | 47 { "pattern": "\\.(gif|jpe?g|pr0n|png)$", |
48 "action": ["python", "image_indexer.py", "--all"]}, | 48 "action": ["python", "image_indexer.py", "--all"]}, |
49 ] | 49 ] |
50 """ | 50 """ |
51 | 51 |
52 __version__ = "0.5.2" | 52 __version__ = "0.6" |
53 | 53 |
| 54 import copy |
54 import logging | 55 import logging |
55 import optparse | 56 import optparse |
56 import os | 57 import os |
57 import posixpath | 58 import posixpath |
58 import pprint | 59 import pprint |
59 import re | 60 import re |
60 import subprocess | 61 import subprocess |
61 import sys | 62 import sys |
62 import urlparse | 63 import urlparse |
63 import urllib | 64 import urllib |
(...skipping 284 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
348 # Special support for single-file checkout. | 349 # Special support for single-file checkout. |
349 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): | 350 if not command in (None, 'cleanup', 'diff', 'pack', 'status'): |
350 options.revision = self.parsed_url.GetRevision() | 351 options.revision = self.parsed_url.GetRevision() |
351 scm = gclient_scm.SVNWrapper(self.parsed_url.GetPath(), | 352 scm = gclient_scm.SVNWrapper(self.parsed_url.GetPath(), |
352 self.root_dir(), | 353 self.root_dir(), |
353 self.name) | 354 self.name) |
354 scm.RunCommand('updatesingle', options, | 355 scm.RunCommand('updatesingle', options, |
355 args + [self.parsed_url.GetFilename()], | 356 args + [self.parsed_url.GetFilename()], |
356 self._file_list) | 357 self._file_list) |
357 else: | 358 else: |
| 359 # Create a shallow copy to mutate revision. |
| 360 options = copy.copy(options) |
358 options.revision = revision_overrides.get(self.name) | 361 options.revision = revision_overrides.get(self.name) |
359 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) | 362 scm = gclient_scm.CreateSCM(self.parsed_url, self.root_dir(), self.name) |
360 scm.RunCommand(command, options, args, self._file_list) | 363 scm.RunCommand(command, options, args, self._file_list) |
361 self._file_list = [os.path.join(self.name, f.strip()) | 364 self._file_list = [os.path.join(self.name, f.strip()) |
362 for f in self._file_list] | 365 for f in self._file_list] |
363 options.revision = None | |
364 self.processed = True | 366 self.processed = True |
365 if self.recursion_limit() > 0: | 367 if self.recursion_limit() > 0: |
366 # Then we can parse the DEPS file. | 368 # Then we can parse the DEPS file. |
367 self.ParseDepsFile() | 369 self.ParseDepsFile() |
368 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains | 370 # Adjust the implicit dependency requirement; e.g. if a DEPS file contains |
369 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of | 371 # both src/foo and src/foo/bar, src/foo/bar is implicitly dependent of |
370 # src/foo. Yes, it's O(n^2)... It's important to do that before | 372 # src/foo. Yes, it's O(n^2)... It's important to do that before |
371 # enqueueing them. | 373 # enqueueing them. |
372 for s in self.dependencies: | 374 for s in self.dependencies: |
373 for s2 in self.dependencies: | 375 for s2 in self.dependencies: |
(...skipping 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
703 Args: | 705 Args: |
704 command: The command to use (e.g., 'status' or 'diff') | 706 command: The command to use (e.g., 'status' or 'diff') |
705 args: list of str - extra arguments to add to the command line. | 707 args: list of str - extra arguments to add to the command line. |
706 """ | 708 """ |
707 if not self.dependencies: | 709 if not self.dependencies: |
708 raise gclient_utils.Error('No solution specified') | 710 raise gclient_utils.Error('No solution specified') |
709 revision_overrides = self._EnforceRevisions() | 711 revision_overrides = self._EnforceRevisions() |
710 pm = None | 712 pm = None |
711 if command == 'update' and not self._options.verbose: | 713 if command == 'update' and not self._options.verbose: |
712 pm = Progress('Syncing projects', 1) | 714 pm = Progress('Syncing projects', 1) |
713 work_queue = gclient_utils.ExecutionQueue(pm) | 715 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, pm) |
714 for s in self.dependencies: | 716 for s in self.dependencies: |
715 work_queue.enqueue(s) | 717 work_queue.enqueue(s) |
716 work_queue.flush(self._options, revision_overrides, command, args, | 718 work_queue.flush(self._options, revision_overrides, command, args, |
717 work_queue) | 719 work_queue) |
718 | 720 |
719 # Once all the dependencies have been processed, it's now safe to run the | 721 # Once all the dependencies have been processed, it's now safe to run the |
720 # hooks. | 722 # hooks. |
721 if not self._options.nohooks: | 723 if not self._options.nohooks: |
722 self.RunHooksRecursively(self._options) | 724 self.RunHooksRecursively(self._options) |
723 | 725 |
(...skipping 24 matching lines...) Expand all Loading... |
748 entry_fixed, self.root_dir())) | 750 entry_fixed, self.root_dir())) |
749 gclient_utils.RemoveDirectory(e_dir) | 751 gclient_utils.RemoveDirectory(e_dir) |
750 # record the current list of entries for next time | 752 # record the current list of entries for next time |
751 self._SaveEntries() | 753 self._SaveEntries() |
752 return 0 | 754 return 0 |
753 | 755 |
754 def PrintRevInfo(self): | 756 def PrintRevInfo(self): |
755 if not self.dependencies: | 757 if not self.dependencies: |
756 raise gclient_utils.Error('No solution specified') | 758 raise gclient_utils.Error('No solution specified') |
757 # Load all the settings. | 759 # Load all the settings. |
758 work_queue = gclient_utils.ExecutionQueue(None) | 760 work_queue = gclient_utils.ExecutionQueue(self._options.jobs, None) |
759 for s in self.dependencies: | 761 for s in self.dependencies: |
760 work_queue.enqueue(s) | 762 work_queue.enqueue(s) |
761 work_queue.flush(self._options, {}, None, [], work_queue) | 763 work_queue.flush(self._options, {}, None, [], work_queue) |
762 | 764 |
763 def GetURLAndRev(dep): | 765 def GetURLAndRev(dep): |
764 """Returns the revision-qualified SCM url for a Dependency.""" | 766 """Returns the revision-qualified SCM url for a Dependency.""" |
765 if dep.parsed_url is None: | 767 if dep.parsed_url is None: |
766 return None | 768 return None |
767 if isinstance(dep.parsed_url, self.FileImpl): | 769 if isinstance(dep.parsed_url, self.FileImpl): |
768 original_url = dep.parsed_url.file_location | 770 original_url = dep.parsed_url.file_location |
(...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1161 | 1163 |
1162 def Main(argv): | 1164 def Main(argv): |
1163 """Doesn't parse the arguments here, just find the right subcommand to | 1165 """Doesn't parse the arguments here, just find the right subcommand to |
1164 execute.""" | 1166 execute.""" |
1165 try: | 1167 try: |
1166 # Do it late so all commands are listed. | 1168 # Do it late so all commands are listed. |
1167 CMDhelp.usage = ('\n\nCommands are:\n' + '\n'.join([ | 1169 CMDhelp.usage = ('\n\nCommands are:\n' + '\n'.join([ |
1168 ' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip()) | 1170 ' %-10s %s' % (fn[3:], Command(fn[3:]).__doc__.split('\n')[0].strip()) |
1169 for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')])) | 1171 for fn in dir(sys.modules[__name__]) if fn.startswith('CMD')])) |
1170 parser = optparse.OptionParser(version='%prog ' + __version__) | 1172 parser = optparse.OptionParser(version='%prog ' + __version__) |
| 1173 parser.add_option('-j', '--jobs', default=1, type='int', |
| 1174 help='Specify how many SCM commands can run in parallel') |
1171 parser.add_option('-v', '--verbose', action='count', default=0, | 1175 parser.add_option('-v', '--verbose', action='count', default=0, |
1172 help='Produces additional output for diagnostics. Can be ' | 1176 help='Produces additional output for diagnostics. Can be ' |
1173 'used up to three times for more logging info.') | 1177 'used up to three times for more logging info.') |
1174 parser.add_option('--gclientfile', dest='config_filename', | 1178 parser.add_option('--gclientfile', dest='config_filename', |
1175 default=os.environ.get('GCLIENT_FILE', '.gclient'), | 1179 default=os.environ.get('GCLIENT_FILE', '.gclient'), |
1176 help='Specify an alternate %default file') | 1180 help='Specify an alternate %default file') |
1177 # Integrate standard options processing. | 1181 # Integrate standard options processing. |
1178 old_parser = parser.parse_args | 1182 old_parser = parser.parse_args |
1179 def Parse(args): | 1183 def Parse(args): |
1180 (options, args) = old_parser(args) | 1184 (options, args) = old_parser(args) |
1181 level = None | 1185 level = None |
1182 if options.verbose == 2: | 1186 if options.verbose == 2: |
1183 level = logging.INFO | 1187 level = logging.INFO |
1184 elif options.verbose > 2: | 1188 elif options.verbose > 2: |
1185 level = logging.DEBUG | 1189 level = logging.DEBUG |
1186 logging.basicConfig(level=level, | 1190 logging.basicConfig(level=level, |
1187 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s') | 1191 format='%(module)s(%(lineno)d) %(funcName)s:%(message)s') |
1188 options.entries_filename = options.config_filename + '_entries' | 1192 options.entries_filename = options.config_filename + '_entries' |
| 1193 if options.jobs < 1: |
| 1194 parser.error('--jobs must be 1 or higher') |
1189 | 1195 |
1190 # These hacks need to die. | 1196 # These hacks need to die. |
1191 if not hasattr(options, 'revisions'): | 1197 if not hasattr(options, 'revisions'): |
1192 # GClient.RunOnDeps expects it even if not applicable. | 1198 # GClient.RunOnDeps expects it even if not applicable. |
1193 options.revisions = [] | 1199 options.revisions = [] |
1194 if not hasattr(options, 'head'): | 1200 if not hasattr(options, 'head'): |
1195 options.head = None | 1201 options.head = None |
1196 if not hasattr(options, 'nohooks'): | 1202 if not hasattr(options, 'nohooks'): |
1197 options.nohooks = True | 1203 options.nohooks = True |
1198 if not hasattr(options, 'deps_os'): | 1204 if not hasattr(options, 'deps_os'): |
(...skipping 17 matching lines...) Expand all Loading... |
1216 return CMDhelp(parser, argv) | 1222 return CMDhelp(parser, argv) |
1217 except gclient_utils.Error, e: | 1223 except gclient_utils.Error, e: |
1218 print >> sys.stderr, 'Error: %s' % str(e) | 1224 print >> sys.stderr, 'Error: %s' % str(e) |
1219 return 1 | 1225 return 1 |
1220 | 1226 |
1221 | 1227 |
1222 if '__main__' == __name__: | 1228 if '__main__' == __name__: |
1223 sys.exit(Main(sys.argv[1:])) | 1229 sys.exit(Main(sys.argv[1:])) |
1224 | 1230 |
1225 # vim: ts=2:sw=2:tw=80:et: | 1231 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |