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 293 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
304 raise gclient_utils.Error( | 304 raise gclient_utils.Error( |
305 "Solutions have conflicting versions of dependency \"%s\"" % d) | 305 "Solutions have conflicting versions of dependency \"%s\"" % d) |
306 if d in solution_urls and solution_urls[d] != url: | 306 if d in solution_urls and solution_urls[d] != url: |
307 raise gclient_utils.Error( | 307 raise gclient_utils.Error( |
308 "Dependency \"%s\" conflicts with specified solution" % d) | 308 "Dependency \"%s\" conflicts with specified solution" % d) |
309 # Grab the dependency. | 309 # Grab the dependency. |
310 deps[d] = url | 310 deps[d] = url |
311 return deps | 311 return deps |
312 | 312 |
313 def _RunHookAction(self, hook_dict, matching_file_list): | 313 def _RunHookAction(self, hook_dict, matching_file_list): |
314 """Runs the action from a single hook. | 314 """Runs the action from a single hook.""" |
315 """ | |
316 logging.info(hook_dict) | 315 logging.info(hook_dict) |
317 logging.info(matching_file_list) | 316 logging.info(matching_file_list) |
318 command = hook_dict['action'][:] | 317 command = hook_dict['action'][:] |
319 if command[0] == 'python': | 318 if command[0] == 'python': |
320 # If the hook specified "python" as the first item, the action is a | 319 # If the hook specified "python" as the first item, the action is a |
321 # Python script. Run it by starting a new copy of the same | 320 # Python script. Run it by starting a new copy of the same |
322 # interpreter. | 321 # interpreter. |
323 command[0] = sys.executable | 322 command[0] = sys.executable |
324 | 323 |
325 if '$matching_files' in command: | 324 if '$matching_files' in command: |
326 splice_index = command.index('$matching_files') | 325 splice_index = command.index('$matching_files') |
327 command[splice_index:splice_index + 1] = matching_file_list | 326 command[splice_index:splice_index + 1] = matching_file_list |
328 | 327 |
329 # Use a discrete exit status code of 2 to indicate that a hook action | 328 # Use a discrete exit status code of 2 to indicate that a hook action |
330 # failed. Users of this script may wish to treat hook action failures | 329 # failed. Users of this script may wish to treat hook action failures |
331 # differently from VC failures. | 330 # differently from VC failures. |
332 gclient_utils.SubprocessCall(command, self.root_dir(), fail_status=2) | 331 return gclient_utils.SubprocessCall(command, self.root_dir(), fail_status=2) |
333 | 332 |
334 def _RunHooks(self, command, file_list, is_using_git): | 333 def _RunHooks(self, command, file_list, is_using_git): |
335 """Evaluates all hooks, running actions as needed. | 334 """Evaluates all hooks, running actions as needed. |
336 """ | 335 """ |
337 # Hooks only run for these command types. | 336 # Hooks only run for these command types. |
338 if not command in ('update', 'revert', 'runhooks'): | 337 if not command in ('update', 'revert', 'runhooks'): |
339 return | 338 return |
340 | 339 |
341 # Hooks only run when --nohooks is not specified | 340 # Hooks only run when --nohooks is not specified |
342 if self._options.nohooks: | 341 if self._options.nohooks: |
(...skipping 20 matching lines...) Expand all Loading... |
363 matching_file_list = [f for f in file_list if pattern.search(f)] | 362 matching_file_list = [f for f in file_list if pattern.search(f)] |
364 if matching_file_list: | 363 if matching_file_list: |
365 self._RunHookAction(hook_dict, matching_file_list) | 364 self._RunHookAction(hook_dict, matching_file_list) |
366 | 365 |
367 def root_dir(self): | 366 def root_dir(self): |
368 return self.parent.root_dir() | 367 return self.parent.root_dir() |
369 | 368 |
370 def enforced_os(self): | 369 def enforced_os(self): |
371 return self.parent.enforced_os() | 370 return self.parent.enforced_os() |
372 | 371 |
| 372 def recursion_limit(self): |
| 373 return self.parent.recursion_limit() - 1 |
| 374 |
| 375 def tree(self, force_all): |
| 376 return self.parent.tree(force_all) |
| 377 |
| 378 def get_custom_deps(self, name, url): |
| 379 """Returns a custom deps if applicable.""" |
| 380 if self.parent: |
| 381 url = self.parent.get_custom_deps(name, url) |
| 382 # None is a valid return value to disable a dependency. |
| 383 return self.custom_deps.get(name, url) |
| 384 |
| 385 def __str__(self): |
| 386 out = [] |
| 387 for i in ('name', 'url', 'safesync_url', 'custom_deps', 'custom_vars', |
| 388 'deps_hooks'): |
| 389 # 'deps_file' |
| 390 if self.__dict__[i]: |
| 391 out.append('%s: %s' % (i, self.__dict__[i])) |
| 392 |
| 393 for d in self.dependencies: |
| 394 out.extend([' ' + x for x in str(d).splitlines()]) |
| 395 out.append('') |
| 396 return '\n'.join(out) |
| 397 |
| 398 def __repr__(self): |
| 399 return '%s: %s' % (self.name, self.url) |
| 400 |
373 | 401 |
374 class GClient(Dependency): | 402 class GClient(Dependency): |
375 """Main gclient checkout root where .gclient resides.""" | 403 """Object that represent a gclient checkout. A tree of Dependency(), one per |
| 404 solution or DEPS entry.""" |
376 SUPPORTED_COMMANDS = [ | 405 SUPPORTED_COMMANDS = [ |
377 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update', | 406 'cleanup', 'diff', 'export', 'pack', 'revert', 'status', 'update', |
378 'runhooks' | 407 'runhooks' |
379 ] | 408 ] |
380 | 409 |
381 DEPS_OS_CHOICES = { | 410 DEPS_OS_CHOICES = { |
382 "win32": "win", | 411 "win32": "win", |
383 "win": "win", | 412 "win": "win", |
384 "cygwin": "win", | 413 "cygwin": "win", |
385 "darwin": "mac", | 414 "darwin": "mac", |
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
422 self._options = options | 451 self._options = options |
423 if options.deps_os: | 452 if options.deps_os: |
424 enforced_os = options.deps_os.split(',') | 453 enforced_os = options.deps_os.split(',') |
425 else: | 454 else: |
426 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] | 455 enforced_os = [self.DEPS_OS_CHOICES.get(sys.platform, 'unix')] |
427 if 'all' in enforced_os: | 456 if 'all' in enforced_os: |
428 enforced_os = self.DEPS_OS_CHOICES.itervalues() | 457 enforced_os = self.DEPS_OS_CHOICES.itervalues() |
429 self._enforced_os = list(set(enforced_os)) | 458 self._enforced_os = list(set(enforced_os)) |
430 self._root_dir = root_dir | 459 self._root_dir = root_dir |
431 self.config_content = None | 460 self.config_content = None |
| 461 # Do not change previous behavior. Only solution level and immediate DEPS |
| 462 # are processed. |
| 463 self._recursion_limit = 2 |
432 | 464 |
433 def SetConfig(self, content): | 465 def SetConfig(self, content): |
434 assert self.dependencies == [] | 466 assert self.dependencies == [] |
435 config_dict = {} | 467 config_dict = {} |
436 self.config_content = content | 468 self.config_content = content |
437 try: | 469 try: |
438 exec(content, config_dict) | 470 exec(content, config_dict) |
439 except SyntaxError, e: | 471 except SyntaxError, e: |
440 try: | 472 try: |
441 # Try to construct a human readable error message | 473 # Try to construct a human readable error message |
(...skipping 221 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
663 modified_files = gclient_scm.scm.SVN.CaptureStatus(e_dir) | 695 modified_files = gclient_scm.scm.SVN.CaptureStatus(e_dir) |
664 else: | 696 else: |
665 file_list = [] | 697 file_list = [] |
666 scm = gclient_scm.CreateSCM(prev_entries[entry], self.root_dir(), | 698 scm = gclient_scm.CreateSCM(prev_entries[entry], self.root_dir(), |
667 entry_fixed) | 699 entry_fixed) |
668 scm.status(self._options, [], file_list) | 700 scm.status(self._options, [], file_list) |
669 modified_files = file_list != [] | 701 modified_files = file_list != [] |
670 if not self._options.delete_unversioned_trees or modified_files: | 702 if not self._options.delete_unversioned_trees or modified_files: |
671 # There are modified files in this entry. Keep warning until | 703 # There are modified files in this entry. Keep warning until |
672 # removed. | 704 # removed. |
673 print(("\nWARNING: \"%s\" is no longer part of this client. " | 705 print(('\nWARNING: \'%s\' is no longer part of this client. ' |
674 "It is recommended that you manually remove it.\n") % | 706 'It is recommended that you manually remove it.\n') % |
675 entry_fixed) | 707 entry_fixed) |
676 else: | 708 else: |
677 # Delete the entry | 709 # Delete the entry |
678 print("\n________ deleting \'%s\' " + | 710 print('\n________ deleting \'%s\' ' + |
679 "in \'%s\'") % (entry_fixed, self.root_dir()) | 711 'in \'%s\'') % (entry_fixed, self.root_dir()) |
680 gclient_utils.RemoveDirectory(e_dir) | 712 gclient_utils.RemoveDirectory(e_dir) |
681 # record the current list of entries for next time | 713 # record the current list of entries for next time |
682 self._SaveEntries(entries) | 714 self._SaveEntries(entries) |
683 return 0 | 715 return 0 |
684 | 716 |
685 def PrintRevInfo(self): | 717 def PrintRevInfo(self): |
686 """Output revision info mapping for the client and its dependencies. | 718 """Output revision info mapping for the client and its dependencies. |
687 | 719 |
688 This allows the capture of an overall "revision" for the source tree that | 720 This allows the capture of an overall "revision" for the source tree that |
689 can be used to reproduce the same tree in the future. It is only useful for | 721 can be used to reproduce the same tree in the future. It is only useful for |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
759 print(";\n".join(["%s: %s" % (x, entries[x]) | 791 print(";\n".join(["%s: %s" % (x, entries[x]) |
760 for x in sorted(entries.keys())])) | 792 for x in sorted(entries.keys())])) |
761 | 793 |
762 # Print the snapshot configuration file | 794 # Print the snapshot configuration file |
763 if self._options.snapshot: | 795 if self._options.snapshot: |
764 config = self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient} | 796 config = self.DEFAULT_SNAPSHOT_FILE_TEXT % {'solution_list': new_gclient} |
765 snapclient = GClient(self.root_dir(), self._options) | 797 snapclient = GClient(self.root_dir(), self._options) |
766 snapclient.SetConfig(config) | 798 snapclient.SetConfig(config) |
767 print(snapclient.config_content) | 799 print(snapclient.config_content) |
768 | 800 |
| 801 def ParseDepsFile(self, direct_reference): |
| 802 """No DEPS to parse for a .gclient file.""" |
| 803 self.direct_reference = direct_reference |
| 804 self.deps_parsed = True |
| 805 |
769 def root_dir(self): | 806 def root_dir(self): |
| 807 """Root directory of gclient checkout.""" |
770 return self._root_dir | 808 return self._root_dir |
771 | 809 |
772 def enforced_os(self): | 810 def enforced_os(self): |
| 811 """What deps_os entries that are to be parsed.""" |
773 return self._enforced_os | 812 return self._enforced_os |
774 | 813 |
| 814 def recursion_limit(self): |
| 815 """How recursive can each dependencies in DEPS file can load DEPS file.""" |
| 816 return self._recursion_limit |
| 817 |
| 818 def tree(self, force_all): |
| 819 """Returns a flat list of all the dependencies.""" |
| 820 def subtree(dep): |
| 821 if not force_all and not dep.direct_reference: |
| 822 # Was loaded from a From() keyword in a DEPS file, don't load all its |
| 823 # dependencies. |
| 824 return [] |
| 825 result = dep.dependencies[:] |
| 826 for d in dep.dependencies: |
| 827 result.extend(subtree(d)) |
| 828 return result |
| 829 return subtree(self) |
| 830 |
775 | 831 |
776 #### gclient commands. | 832 #### gclient commands. |
777 | 833 |
778 | 834 |
779 def CMDcleanup(parser, args): | 835 def CMDcleanup(parser, args): |
780 """Cleans up all working copies. | 836 """Cleans up all working copies. |
781 | 837 |
782 Mostly svn-specific. Simply runs 'svn cleanup' for each module. | 838 Mostly svn-specific. Simply runs 'svn cleanup' for each module. |
783 """ | 839 """ |
784 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', | 840 parser.add_option('--deps', dest='deps_os', metavar='OS_LIST', |
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1118 return CMDhelp(parser, argv) | 1174 return CMDhelp(parser, argv) |
1119 except gclient_utils.Error, e: | 1175 except gclient_utils.Error, e: |
1120 print >> sys.stderr, 'Error: %s' % str(e) | 1176 print >> sys.stderr, 'Error: %s' % str(e) |
1121 return 1 | 1177 return 1 |
1122 | 1178 |
1123 | 1179 |
1124 if '__main__' == __name__: | 1180 if '__main__' == __name__: |
1125 sys.exit(Main(sys.argv[1:])) | 1181 sys.exit(Main(sys.argv[1:])) |
1126 | 1182 |
1127 # vim: ts=2:sw=2:tw=80:et: | 1183 # vim: ts=2:sw=2:tw=80:et: |
OLD | NEW |